Python直到第二次发送才检测到关闭的套接字
当我在连接的一端关闭套接字时,另一端在第二次发送数据时会出现错误,但第一次发送时却没有:
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 个回答
根据文档,在调用sock.close()
之前,先试着调用sock.shutdown()
。
这是正常现象,也是TCP/IP接口的实现方式(在几乎所有语言和操作系统中都是类似的)。
简单来说,如果你调用send()方法但数据无法送到另一端,你无法保证这个调用会直接返回一个错误。send/write调用只是把数据交给TCP协议栈,具体什么时候能送到就得看TCP协议栈的情况了。
TCP只是一个传输协议,如果你想知道你的应用程序“消息”是否已经到达另一端,你需要自己实现这个功能(某种形式的确认机制),这得作为你应用协议的一部分来做——没有免费的午餐。
不过,如果你从一个套接字读取数据(read()),当发生错误或者另一端关闭了套接字时,你可以立即收到通知——通常你需要在某种多路复用事件循环中进行这个操作(也就是说,使用select/poll或其他IO多路复用工具)。
需要注意的是,你不能通过read()来判断最近的send/write调用是否成功。这里有几个原因(但往往是那些你没想到的情况让你头疼):
- 由于网络拥堵,或者TCP窗口关闭(可能是对方读取速度慢),多个write()调用被缓冲起来,然后对方关闭了套接字或者发生了严重的网络错误,这样你就无法判断是最后一次写入没有成功,还是30秒前的写入没有送达。
- 网络错误,或者防火墙悄悄丢弃了你的数据包(没有生成ICMP回复),你得等到TCP超时连接才能得到错误,这可能需要几秒钟,通常是几分钟。
- 当你调用
send
时,TCP正在忙着重传数据——也许这些重传会产生错误。(这实际上和第一个情况是一样的)