我有一个套接字编程情况,客户端关闭套接字的写入端,让服务器知道输入已完成(通过接收EOF),但保持读取端打开以读回结果(一行文本) )。服务器知道客户端已成功读取结果并关闭套接字(或至少关闭读取端)将是有用的。有没有一种检查/等待这种状态的好方法?
答案 0 :(得分:5)
没有。您可以知道的是,您的发送是否成功,并且由于TCP缓冲,即使在对等读取关闭之后,其中一些也会成功。
这是糟糕的设计。如果服务器需要知道客户端收到数据,则客户端需要确认它,这意味着它无法关闭其写入端。客户应该:
服务器应检测带内终止消息,并且:
OR,如果目标只是确保客户端和服务器同时结束,则每一端都应关闭其套接字以进行输出,然后读取输入直到流结束,然后关闭插座。这样,最终结束将在两端或多或少同时发生。
答案 1 :(得分:5)
getsockopt
的 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)
(几乎与关闭相同)显然,你的操作系统可以区分这些状态中的很多,但不是全部。我无法想到一个API就是这个冗长的......
某些系统允许您查询剩余的发送缓冲区空间。也许如果你这样做,并且套接字已经关闭,你会得到一个整洁的错误吗?
好消息是因为套接字被关闭,并不意味着你无法查询它。我可以在关机后使用TCP_INFO
(已关闭)获取所有state=7
。在某些情况下报告state=8
(等待等待)。
http://lxr.free-electrons.com/source/net/ipv4/tcp.c#L1961包含Linux TCP状态机的所有细节。
答案 2 :(得分:1)
TL; DR:
不要依赖套接字状态;它可以在许多错误情况下削减你。您需要将确认/接收工具烘焙到您的通信协议中。用于status / ack的每一行的第一个字符对于基于文本的协议非常有效。
在许多(但不是全部)类Unix / POSIXy系统上,可以使用TIOCOUTQ
(也SIOCOUTQ
)ioctl来确定传出缓冲区中剩余的数据量
对于TCP套接字,即使另一端已关闭其写入端(因此不会向此发送更多数据),也会确认所有传输。仅当收到来自收件人内核的确认时,才会删除传出缓冲区中的数据。因此,当传出缓冲区中没有更多数据时,我们知道另一端的内核已经收到了数据。
不幸的是,这并不意味着应用程序已经接收并处理了数据。同样的限制适用于依赖套接字状态的所有方法;这也是为什么从根本上说,确认收到/接受最终状态行必须来自其他应用程序,并且无法自动检测到。
这反过来意味着在最终的收到/确认消息之前,任何一方都不能关闭其发送方。你不能依赖TCP - 或任何其他协议' - 自动套接字状态管理。您必须将关键收据/确认信息烘焙到流协议本身。
在OP的情况下,流协议似乎是简单的基于行的文本。这非常有用且易于解析。一种强有力的方式来扩展"这样的协议是为状态代码保留每一行的第一个字符(或者,保留某些单字符行作为确认)。
对于大型飞行中二进制协议(即发送方和接收方实际上并不真正同步的协议),用增加的(循环)整数标记每个数据帧并使另一端偶尔响应是有用的,有一个更新,让发件人知道哪些帧已经完全处理,哪些帧收到,以及是否应该很快/很快就到达其他帧。这对于消耗大量数据的基于网络的设备非常有用,数据提供者希望不断更新进度和所需的数据速率(想想3D打印机,CNC机器等,数据内容所在的位置)动态更改最大可接受数据速率。)
答案 3 :(得分:-2)
好的,所以我记得在90年代后期试图解决这个问题。我终于找到了一个不起眼的文档,声明对断开连接的套接字的读取调用将返回0.我今天使用这个事实。
答案 4 :(得分:-3)
你可能最好使用ZeroMQ。这将发送一条完整的消息,或根本没有消息。如果将其发送缓冲区长度设置为1(最短时间),则可以测试发送缓冲区是否已满。如果没有,则可能是成功传输了消息。如果您的系统中存在不可靠或间歇性的网络连接,ZeroMQ也非常好。
那还不完全令人满意。你甚至可能更好地在ZeroMQ之上实现你自己的发送确认机制。这样你就可以绝对地证明收到了一条消息。您没有证据表明没有收到消息(发送和接收确认之间可能出错,而您无法解决两个常规问题)。但这是可以实现的最好的。您之前所做的是在ZeroMQ的Actor模型之上实现一个通信顺序进程架构,它本身是在TCP流之上实现的。最终它会慢一些,但是你的应用程序有更加确定知道发生了什么。