在第二次发送之前,Python不会检测到已关闭的套接字

Eli*_*ght 16 python sockets

当我在连接的一端关闭套接字时,另一端在第二次发送数据时会收到错误,但不是第一次:

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
Run Code Online (Sandbox Code Playgroud)

我已经尝试设置TCP_NODELAY,使用send而不是sendall检查fileno(),我找不到任何方法来获取第一个发送错误,甚至检测到它失败后. 编辑:sock.shutdown之前打电话sock.close没有帮助. 编辑#2:即使time.sleep在结束之后和写作之前添加也没关系. 编辑#3:检查返回的字节数send没有帮助,因为它总是返回消息中的字节数.

因此,如果我想检测错误,我能想出的唯一解决方案就是sendall使用一个client.sendall("")会引发错误的解决方案.但这似乎很苛刻.我在Linux 2.6.x上,所以即使解决方案仅适用于该操作系统,我也会感到高兴.

nos*_*nos 13

这是预期的,以及如何实现TCP/IP API(所以它几乎在所有语言和所有操作系统上都相似)

简而言之,如果send()调用以某种方式无法将数据传递到另一端,则无法保证send()调用直接返回错误.发送/写入调用只是将数据传递到TCP堆栈,并且由TCP堆栈决定是否可以提供数据.

TCP也只是一种传输协议,如果您需要知道您的应用程序"消息"是否到达另一端,您需要自己实现(某种形式的ACK),作为应用程序协议的一部分 - 没有其他免费午餐.

但是 - 如果从套接字读取(),则可以在发生错误时立即得到通知,或者当另一端关闭套接字时 - 通常需要在某种形式的多路复用事件循环中执行此操作(即使用select /轮询或其他IO多路复用设施).

请注意,你不能从套接字中读取()来了解最近的发送/写入是否成功,这里有几个案例就是为什么(但是这是一个没有想到的情况总能得到你)

  • 由于网络拥塞,或者由于tcp窗口关闭(可能是一个慢速阅读器),然后另一端关闭套接字或发生硬网络错误,几个write()调用被缓冲了,因此您无法判断是否是没有通过的最后一次写入,或者是30秒前写的.
  • 网络错误或防火墙无声地丢弃您的数据包(不会生成ICMP回复),您必须等到TCP超时连接才能获得可能是几秒钟的错误,通常是几分钟.
  • TCP正忙着在你调用的时候进行重传send- 也许那些重传会产生错误.(与第一种情况完全相同)

  • @Eli Courtwright.是.TCP发送ACK.然而,这是在TCP(传输层)的ACK.TCP不知道您的消息,就TCP而言,它只是一个字节流.TCP ACK不会调用一个send()调用.它可以累积100*你的*消息.或3个字节的邮件. (3认同)
  • 否 - 没有好的方法等待ACK.,是 - 当套接字关闭时发送FIN(或RST,具体取决于状态).想象一下如果FIN数据包消失会发生什么(路由器/交换机丢弃数据包比大多数人想象的更常见).另外 - 你怎么知道在调用send()时收到了FIN数据包?也许它在你调用send()后收到了3纳秒.如果您还从该套接字读取(),则可以检测到close(),并且您也会收到发送错误的通知 - 但仅限于完美情况.但是,读取(或选择/轮询)中没有关闭/错误"事件"是非决定性的. (2认同)