我有一些代码可以连接到主机,除了监听传入数据外什么都不做,直到客户端关闭或主机发送关闭语句。为此,我的代码运行良好。
然而,当主机在没有发送关闭语句的情况下死机时,我的客户端会按预期一直监听传入的数据。为了解决这个问题,我每隔 foo 秒使套接字超时并开始检查连接是否存在的过程。来自 Python socket我如何找到这个:
One very nasty problem with select: if somewhere in those input lists of sockets is one which has died a nasty death, the select will fail. You then need to loop through every single damn socket in all those lists and do a select([sock],[],[],0) until you find the bad one. That timeout of 0 means it won’t take long, but it’s ugly.
# 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
这段文字提出了三个问题。
- 有人告诉我,要检查连接是否存在,必须写入套接字并查看是否有响应返回。如果不是,则必须假定连接已断开。在文本中它说要检查是否有错误的连接,一个人挑出每个套接字,将它传递给选择的第一个参数并将超时设置为零。这将如何确认套接字是否已死?
- 为什么不通过尝试写入套接字来测试套接字是死的还是活的?
- 当连接处于事件状态和连接停止时,我在寻找什么?选择将立即超时,因此没有数据将无法证明任何事情。
我知道像 gevent
、asyncore
和 twisted
这样的库可以帮助我解决这个问题,但我选择了自己做 self 更好地了解正在发生的事情,并更好地控制我自己的来源。
最佳答案
如果连接的客户端崩溃或退出,但其主机操作系统和计算机仍在运行,则其操作系统的 TCP 堆栈将向您的服务器发送一个 FIN 数据包,让您计算机的 TCP 堆栈知道 TCP 连接已关闭。您的 Python 应用程序会将此视为 select(),表示客户端的套接字已准备好读取,然后当您在套接字上调用 recv() 时,recv() 将返回 0。发生这种情况时,您应该通过关闭来响应 socket 。
另一方面,如果连接的客户端的计算机永远没有机会发送 FIN 数据包(例如,因为有人伸手将其以太网线或电源线从 socket 中拔出),那么您的服务器将不会意识到TCP 连接已经失效了很长一段时间——可能永远失效。避免“僵尸套接字”的最简单方法就是让您的服务器每隔一段时间就在套接字上发送一些虚拟数据,例如每分钟一次或其他。客户端应该知道丢弃虚拟数据。发送虚拟数据的好处是您的服务器的 TCP 堆栈会注意到它没有为它发送的数据包返回任何 ACK 数据包,并将重新发送它们;在重新发送几次之后,您的服务器的 TCP 堆栈将放弃并确定连接已断开,此时您将看到我在第一段中描述的相同行为。
关于Python;无法通过 select 控制死套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19795529/