我有一些代码将连接到主机,除了侦听传入的数据外什么都不做,直到客户端关闭或主机发送关闭语句。为此,我的代码运行良好。
但是,当主机在没有发送 close 语句的情况下死亡时,我的客户端会按预期永远侦听传入的数据。为了解决这个问题,我每隔 foo 秒就让套接字超时,并开始检查连接是否处于活动状态的过程。从 Python 套接字操作方法中,我找到了这个:
select的一个非常令人讨厌的问题:如果这些套接字输入列表中的某个地方已经死了,那么选择将失败。然后,您需要遍历所有这些列表中的每一个该死的套接字,并执行 select([sock],[],[],0),直到找到坏的套接字。0 的超时意味着它不会花费很长时间,但它很丑陋。
# Example code written for this question.
from select import select
from socket include socket, AF_INET, SOCK_STREAM
socket = socket(AF_INET, SOCK_STREAM)
socket.connect(('localhost', 12345))
socklist = [socket,]
attempts = 0
def check_socklist(socks):
for sock in socklist:
(r, w, e) = select([sock,], [], [], 0)
...
...
...
while True:
(r, w, e) = select(socklist, [], [], 60)
for sock in r:
if sock is socket:
msg = sock.recv(4096)
if not msg:
attempts +=1
if attempts >= 10:
check_socket(socklist)
break
else:
attempts = 0
print msg
本文提出了三个问题。
- 我被教导要检查连接是否处于活动状态,必须写入套接字并查看响应是否返回。如果不是,则必须假定连接已失效。在文本中,它说要检查连接是否错误,每个套接字一个,将其传递给 select 的第一个参数并将超时设置为零。这将如何确认套接字是否死亡?
- 为什么不通过尝试写入套接字来测试套接字是死是活 ?
- 当连接处于活动状态和关闭时,我在寻找什么?Select 将立即超时,因此没有数据将证明什么。
我意识到有像gevent
、asyncore
和twisted
这样的库可以帮助我解决这个问题,但我选择自己做这件事,以便更好地了解正在发生的事情,并更好地控制我自己的源代码。
如果连接的客户端崩溃或退出,但其主机操作系统和计算机仍在运行,则其操作系统的 TCP 堆栈将向服务器发送 FIN 数据包,让计算机的 TCP 堆栈知道 TCP 连接已关闭。 您的 Python 应用程序将看到它为 select(),表示客户端的套接字已准备好读取,然后当您在套接字上调用 recv() 时,recv() 将返回 0。 发生这种情况时,您应该通过关闭套接字来响应。
另一方面,如果连接客户端的计算机永远没有机会发送 FIN 数据包(例如,因为有人伸手将其以太网线或电源线从插座中拉出),那么您的服务器将不会意识到 TCP 连接在相当长的一段时间内失效 - 可能是永远。 避免出现"僵尸套接字"的最简单方法是让您的服务器每隔一段时间在套接字上发送一些虚拟数据,例如每分钟一次或其他什么。 客户端应该知道丢弃虚拟数据。 发送虚拟数据的好处是,服务器的 TCP 堆栈会注意到它没有为发送的数据包返回任何 ACK 数据包,并会重新发送它们;在重新发送几次后,服务器的TCP堆栈将放弃并决定连接已死,此时您将看到我在第一段中描述的相同行为。
- 如果您向套接字写入某些内容,然后等待答案来检查连接,则服务器应该支持此"ping"消息。情况并非总是如此。否则,如果服务器不等待此消息,服务器应用程序可能会自行崩溃或断开客户端连接。如果选择以您描述的方式失败,套接字框架将知道哪个套接字已失效。你只需要找到它。但是,如果套接字因服务器的应用程序崩溃等令人讨厌的死亡而死亡,这并不意味着客户端的套接字框架必须检测到这一点。例如,当客户端正在等待来自服务器的某些消息并且服务器崩溃时,在某些情况下,客户端可以永远等待。例如,为了避免这种情况,Putty 可以使用服务器的应用程序的协议级 ping(SSH ping 选项)来检查连接;SSH 服务器可以使用 TCP 保持连接来检查连接并防止网络设备在没有活动的情况下断开连接。
- (见第1页)。
- 你是对的,选择的超时和没有数据证明什么。正如文档所说,当选择失败时,您必须检查每个套接字。