首页 文章

什么时候以及为什么socket.send()在python中返回0?

提问于
浏览
13

python3 socket programming howto提供了此代码段

class MySocket:
    """demonstration class only
      - coded for clarity, not efficiency
    """

    def __init__(self, sock=None):
        if sock is None:
            self.sock = socket.socket(
                            socket.AF_INET, socket.SOCK_STREAM)
        else:
            self.sock = sock

    def connect(self, host, port):
        self.sock.connect((host, port))

    def mysend(self, msg):
        totalsent = 0
        while totalsent < MSGLEN:
            sent = self.sock.send(msg[totalsent:])
            if sent == 0:
                raise RuntimeError("socket connection broken")
            totalsent = totalsent + sent

    def myreceive(self):
        chunks = []
        bytes_recd = 0
        while bytes_recd < MSGLEN:
            chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048))
            if chunk == b'':
                raise RuntimeError("socket connection broken")
            chunks.append(chunk)
            bytes_recd = bytes_recd + len(chunk)
        return b''.join(chunks)

如果套接字send方法返回0,则发送循环中断 .

这个片段背后的逻辑是当 send 方法返回'0 bytes sent'时,套接字连接的发送方应该放弃发送数据的努力 . 这对于recv方法肯定是正确的,其中在阻塞模式下为套接字读取的零字节应该被解释为 EOF ,因此读取方应该放弃 .

但是我无法理解 send 方法在哪种情况下可以返回零 . 我对python套接字的理解是 send 由于OS级别的缓冲而立即返回 . 如果缓冲区已满, send 将阻塞,或者如果远程端连接关闭,则会引发异常 .

最后假设 send 返回零而不引发异常:这是否真的表明所有未来的 send 调用都将返回零?

我做了一些测试(尽管在OS X上只使用连接到 ::1 的套接字)并且无法找到 send 返回0的情况 .

Edit

HOWTO指出:

但是如果您计划重新使用套接字进行进一步传输,则需要意识到套接字上没有EOT . 我再说一遍:如果在处理0字节后套接字send或recv返回,则连接已断开 . 如果连接没有被破坏,你可以永远等待一个recv,因为套接字不会告诉你没有什么可读的(现在) .

很容易找到 recv 返回0的情况:当远程(发送)端调用socket.shutdown(SHUT_WR)时,接收端的 recv 将返回 0 并且不会引发任何异常 .

我正在寻找一个具体的例子,你可以证明从 send 接收0零表示连接断开(在发送时将继续返回0) .

2 回答

  • 3

    在看到问题时我被震惊了,因为 send C调用可以返回0个字节,并且连接当然仍然存在(套接字不能简单地在给定时刻发送更多字节)

    我决定“使用源代码”,除非我非常错(这可能一直是经常),这是HOWTO中的一个错误 .

    链:

    • sendsock_send 的别名

    • sock_send 依次拨打电话 sock_call

    • sock_call 依次拨打电话 sock_call_ex

    • sock_call 依次调用 sock_send_impl (已经从 sock_send 开始向下传递)

    平仓:

    • sock_send_impl 使用 return (ctx->result >= 0) 返回 truefalse (1或0)

    • sock_call_ex 返回

    • -1 如果 sock_send_impl 返回 false

    • 0 如果 sock_send_impl 返回 true

    • sock_call 透明地返回此值 .

    • sock_send

    • -1 返回 NULL (因为已设置错误并将引发异常)

    • sock_call 返回 ctx->result for 0

    ctx->resultsock_send_impl 中C调用 send 写入的字节数 .

    该链显示如果已发送 0 个字节,则没有错误,这实际上是潜在的真实插座情况 .

    如果我的逻辑错了,请有人告诉我 .

  • 5

    我可能错了,但我认为你正在寻找一个不可能的情况......

    正如@mementum在他的回答中所示,理论上套接字在没有错误时返回零,但也没有数据发送 .

    但是,如图所示elsewhere on SO这只能在非常具体的情况下发生 . 根据我的经验(并且在对已接受的答案的评论中也包括在内),当网络拥塞时,您只能在非阻塞套接字上获得零结果 . 现在Python套接字默认是阻塞的,这意味着内核应该等到有空间可以获取更多数据然后返回排队的字节数 . 根据定义,这永远不会为零 .

    所以,把它们放在一起,因为你的代码片段没有重置套接字类型 - 例如使用 set_blocking 函数 - 它使用阻塞套接字,因此不能返回零,因此无法命中路径mementum .

    无论您做什么,都无法触发特定的代码行 .

相关问题