Python直到第二次发送才检测到关闭的套接字

18 投票
2 回答
9371 浏览
提问于 2025-04-16 11:14

当我在连接的一端关闭套接字时,另一端在第二次发送数据时会出现错误,但第一次发送时却没有:

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("localhost", 12345))
server.listen(1)

client = socket.create_connection(("localhost",12345))
sock, addr = server.accept()
sock.close()

client.sendall("Hello World!")    # no error
client.sendall("Goodbye World!")  # error happens here

我尝试过设置TCP_NODELAY,使用send而不是sendall,检查fileno(),但我找不到任何方法让第一次发送抛出错误,甚至在之后检测到它失败。 编辑:sock.close之前调用sock.shutdown也没有帮助。 编辑 #2:即使在关闭后和写入之前添加time.sleep也没有关系。 编辑 #3:检查send返回的字节数也没有帮助,因为它总是返回消息中的字节数。

所以,如果我想检测错误,唯一想到的解决办法就是在每次sendall之后跟一个client.sendall(""),这样就会抛出错误。但这看起来有点像是变通的办法。我使用的是Linux 2.6.x,所以即使解决方案只适用于这个操作系统,我也会很高兴。

2 个回答

1

根据文档,在调用sock.close()之前,先试着调用sock.shutdown()

15

这是正常现象,也是TCP/IP接口的实现方式(在几乎所有语言和操作系统中都是类似的)。

简单来说,如果你调用send()方法但数据无法送到另一端,你无法保证这个调用会直接返回一个错误。send/write调用只是把数据交给TCP协议栈,具体什么时候能送到就得看TCP协议栈的情况了。

TCP只是一个传输协议,如果你想知道你的应用程序“消息”是否已经到达另一端,你需要自己实现这个功能(某种形式的确认机制),这得作为你应用协议的一部分来做——没有免费的午餐。

不过,如果你从一个套接字读取数据(read()),当发生错误或者另一端关闭了套接字时,你可以立即收到通知——通常你需要在某种多路复用事件循环中进行这个操作(也就是说,使用select/poll或其他IO多路复用工具)。

需要注意的是,你不能通过read()来判断最近的send/write调用是否成功。这里有几个原因(但往往是那些你没想到的情况让你头疼):

  • 由于网络拥堵,或者TCP窗口关闭(可能是对方读取速度慢),多个write()调用被缓冲起来,然后对方关闭了套接字或者发生了严重的网络错误,这样你就无法判断是最后一次写入没有成功,还是30秒前的写入没有送达。
  • 网络错误,或者防火墙悄悄丢弃了你的数据包(没有生成ICMP回复),你得等到TCP超时连接才能得到错误,这可能需要几秒钟,通常是几分钟。
  • 当你调用send时,TCP正在忙着重传数据——也许这些重传会产生错误。(这实际上和第一个情况是一样的)

撰写回答