我有一个套接字编程的情况,客户端关闭了套接字的写入端口以让服务器知道输入已完成(通过接收EOF),但保持读取端口打开以读取结果(一行文本)。对于服务器来说,知道客户端已成功读取结果并关闭了套接字(或者至少关闭了读取端口)是很有用的。是否有一种好的方法可以检查/等待这种状态?
我有一个套接字编程的情况,客户端关闭了套接字的写入端口以让服务器知道输入已完成(通过接收EOF),但保持读取端口打开以读取结果(一行文本)。对于服务器来说,知道客户端已成功读取结果并关闭了套接字(或者至少关闭了读取端口)是很有用的。是否有一种好的方法可以检查/等待这种状态?
ECONNRESET
或类似的错误,不是吗?但仍然有可能没有办法在实际尝试发送更多数据之前探测到这一点。 - R.. GitHub STOP HELPING ICEsend()
会在一些成功的发送后产生 ECONNRESET
错误,因为有缓冲区存在,正如我所说。 - user207421getsockopt
使用TCP_INFO
似乎是最明显的选择,但它不是跨平台的。
这里有一个Linux的示例:
import socket
import time
import struct
import pprint
def tcp_info(s):
rv = dict(zip("""
state ca_state retransmits probes backoff options snd_rcv_wscale
rto ato snd_mss rcv_mss unacked sacked lost retrans fackets
last_data_sent last_ack_sent last_data_recv last_ack_recv
pmtu rcv_ssthresh rtt rttvar snd_ssthresh snd_cwnd advmss reordering
rcv_rtt rcv_space
total_retrans
pacing_rate max_pacing_rate bytes_acked bytes_received segs_out segs_in
notsent_bytes min_rtt data_segs_in data_segs_out""".split(),
struct.unpack("BBBBBBBIIIIIIIIIIIIIIIIIIIIIIIILLLLIIIIII",
s.getsockopt(socket.IPPROTO_TCP, socket.TCP_INFO, 160))))
wscale = rv.pop("snd_rcv_wscale")
# bit field layout is up to compiler
# FIXME test the order of nibbles
rv["snd_wscale"] = wscale >> 4
rv["rcv_wscale"] = wscale & 0xf
return rv
for i in range(100):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 7878))
s.recv(10)
pprint.pprint(tcp_info(s))
shutdown(WR)
(与关闭几乎相同)
- REMOTE尚未发送FIN
- REMOTE已发送FIN但尚未接收到
- FIN已发送并且已丢失
- 您的端点接收到FINTCP_INFO
,其中state=7
(已关闭)。在某些情况下报告state=8
(关闭等待)。
http://lxr.free-electrons.com/source/net/ipv4/tcp.c#L1961具有Linux TCP状态机的所有恶毒细节。简而言之:
不要依赖套接字状态来做这个事情,因为在很多错误情况下它会给你带来麻烦。你需要将确认/收据功能融入到你的通信协议中。对于基于文本的协议,每行的第一个字符用于状态/确认非常有效。
TIOCOUTQ
(也是SIOCOUTQ
)ioctl来确定传出缓冲区中剩余的数据量。好的,我记得在90年代后期为解决这个问题而烦恼了很久。最终我找到了一份不太常见的文档,指出从一个已断开的套接字读取数据会返回 0。我至今仍在利用这个事实。
你最好使用ZeroMQ。它可以发送整个消息,或者根本不发送消息。如果将其发送缓冲区长度设置为1(最短长度),则可以测试发送缓冲区是否已满。如果没有,那么消息可能已成功传输。如果您的系统中存在不可靠或间歇性的网络连接,则ZeroMQ也非常好用。
但这仍然不完全令人满意。你最好在ZeroMQ之上实现自己的发送确认机制。这样,你就有绝对的证据表明消息已被接收。你无法证明消息未被接收(在发出和接收确认之间可能会出现问题,而你无法解决“两个将军的问题”)。但这是可以实现的最佳方案。这样做的结果是,在ZeroMQ的Actor模型之上实现了一个通信顺序进程体系结构,该模型本身是基于TCP流实现的。最终速度会稍慢,但您的应用程序可以更加确定发生了什么。