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

时间:2016-01-21 09:30:54

标签: python sockets python-3.x

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)

如果socket send方法返回0,则发送循环被中断。

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

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

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

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

修改

HOWTO说:

  

但是,如果您计划重新使用套接字进行进一步传输,则需要   意识到插座上没有EOT。我再说一遍:如果是套接字   处理0字节后发送或recv返回,连接已经   破碎。如果连接尚未断开,您可以等待recv   永远,因为套接字不会告诉你什么都没有   更多内容(现在)。

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

我正在寻找一个具体示例,您可以在其中显示从0接收0零表示断开连接(在发送时将继续返回0)。

2 个答案:

答案 0 :(得分:5)

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

我决定&#34;使用来源&#34;除非我非常错(这可能一直是经常发生),这是HOWTO中的一个错误。

  • sendsock_send
  • 的别名
  • sock_send依次拨打电话sock_call
  • sock_call依次拨打电话sock_call_ex
  • sock_call依次调用sock_send_impl(已从sock_send开始向下传递)

放卷:

  • sock_send_impl使用true

  • 返回falsereturn (ctx->result >= 0)(1或0)
  • sock_call_ex返回

      如果-1返回sock_send_impl ,则
    • false 如果0返回sock_send_impl
    • ,则
    • true
  • sock_call透明地返回此值。

  • sock_send

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

    • ctx->result

      返回0的{​​{1}}

      sock_callctx->result中C调用send写入的字节数。

链表示如果发送了sock_send_impl个字节,则没有错误,这实际上是一个潜在的真实插座情况。

如果我的逻辑错误,请有人告诉我。

答案 1 :(得分:3)

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

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

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

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

这可以通过以下事实得到支持:无论您做什么,都无法触发特定的代码行。