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 回答
在看到问题时我被震惊了,因为
send
C调用可以返回0个字节,并且连接当然仍然存在(套接字不能简单地在给定时刻发送更多字节)我决定“使用源代码”,除非我非常错(这可能一直是经常),这是HOWTO中的一个错误 .
链:
send
是sock_send
的别名sock_send
依次拨打电话sock_call
sock_call
依次拨打电话sock_call_ex
sock_call
依次调用sock_send_impl
(已经从sock_send
开始向下传递)平仓:
sock_send_impl
使用return (ctx->result >= 0)
返回true
或false
(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
for0
ctx->result
是sock_send_impl
中C调用send
写入的字节数 .该链显示如果已发送
0
个字节,则没有错误,这实际上是潜在的真实插座情况 .如果我的逻辑错了,请有人告诉我 .
我可能错了,但我认为你正在寻找一个不可能的情况......
正如@mementum在他的回答中所示,理论上套接字在没有错误时返回零,但也没有数据发送 .
但是,如图所示elsewhere on SO这只能在非常具体的情况下发生 . 根据我的经验(并且在对已接受的答案的评论中也包括在内),当网络拥塞时,您只能在非阻塞套接字上获得零结果 . 现在Python套接字默认是阻塞的,这意味着内核应该等到有空间可以获取更多数据然后返回排队的字节数 . 根据定义,这永远不会为零 .
所以,把它们放在一起,因为你的代码片段没有重置套接字类型 - 例如使用
set_blocking
函数 - 它使用阻塞套接字,因此不能返回零,因此无法命中路径mementum .无论您做什么,都无法触发特定的代码行 .