【问题标题】:When and why socket.send() returns 0 in python?何时以及为什么 socket.send() 在 python 中返回 0?
【发布时间】:2016-04-27 11:13:42
【问题描述】:

python3socket programming howto 呈现这段代码 sn-p

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,则发送循环被中断。

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

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

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

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

编辑

HOWTO 规定:

但是,如果您打算重复使用您的套接字进行进一步的传输,您需要 意识到套接字上没有EOT。我再说一遍:如果一个套接字 send 或 recv 处理完 0 个字节后返回,连接已 破碎的。如果连接没有断开,您可以等待接收 永远,因为套接字不会告诉你什么都没有 更多内容(目前)。

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

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

【问题讨论】:

  • HOWTO 还解释说,“但是如果你打算重用你的套接字进行进一步的传输,你需要意识到套接字上没有 EOT。我重复一遍:如果套接字发送或接收之后返回处理 0 个字节,连接已断开。”
  • @J.J.Hakala 我尽力创造了一个send 在处理完 0 个字节后返回但找不到的情况。我在接收端尝试了几乎所有方法(关闭、关闭的各种组合等),但 send 方法从未返回 0。所以我的问题是 send 返回 0 的特殊情况是什么?

标签: python sockets python-3.x


【解决方案1】:

看到这个问题我有点惊呆了,因为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 返回 truefalse(1 或 0)和 return (ctx-&gt;result &gt;= 0)

  • sock_call_ex 返回

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

  • sock_send

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

    • sock_call返回ctx-&gt;result0

      ctx-&gt;resultsock_send_impl中的C调用send写入的字节数。

链显示,如果0 字节已发送,则没有错误,这实际上是潜在的现实生活中的套接字情况。

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

【讨论】:

    【解决方案2】:

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

    正如@mementum 在他的回答中所表明的那样,理论上套接字在没有错误但也没有发送数据的情况下可能返回零。

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

    所以,把它们放在一起,因为你的 sn-p 不会重置套接字类型 - 例如使用 set_blocking 函数 - 它正在使用阻塞套接字,因此无法返回零,因此无法命中标识的路径。

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

    【讨论】:

    • 那么你同意代码示例if sent == 0中的测试确实是不必要的?
    • 据我所知,是的。
    • 在发送或接收期间收到 *ix 信号会怎样?如果它发生在传输开始时,那不能传输 0 个字节吗?
    • 一个好问题。 Python 具有在信号中断时重试套接字调用的逻辑。见github.com/python/cpython/blob/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-01
    • 2020-01-03
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    相关资源
    最近更新 更多