Python套接字客户端消失,服务器无法告诉

时间:2015-10-23 00:02:20

标签: python sockets network-programming

我疯了在python中编写一个小套接字服务器。一切都工作正常,但我注意到,在客户端刚刚消失的情况下,服务器无法分辨。我通过拉动客户端和服务器之间的以太网电缆来模拟这一点,关闭客户端,然后重新插入电缆。服务器永远不会听到客户端断开连接并将永远等待,从不允许更多客户端连接。

我想通过在读取循环中添加一个超时来解决这个问题,这样它就会每隔10秒尝试一次读取。我想也许如果它试图从套接字读取它会注意到客户端丢失了。但后来我才意识到服务器真的无法知道这一点。

所以我加了一个心跳。如果服务器在没有读取的情况下运行10秒,它将向客户端发送数据。但是,即使这是成功的(意味着不会抛出任何异常)。所以我能够读取和写入不再存在的客户端。有没有办法知道客户端已经消失而没有在客户端和服务器之间实现某种挑战/响应协议?在这种情况下,这将是一个突破性的变化,我想避免它。

以下是我的代码的核心:

  def _loop(self):
      command = ""
      while True:
          socket, address = self._listen_socket.accept()
          self._socket = socket
          self._socket.settimeout(10)
          socket.sendall("Welcome\r\n\r\n")
          while True:
              try:
                  data = socket.recv(1)
              except timeout: # Went 10 seconds without data
                  pass
              except Exception as e: # Likely the client closed the connection
                 break
             if data:
                 command = command + data
                 if data == "\n" or data == "\r":
                     if len(command.strip()) > 0:
                         self._parse_command(command.strip(), socket)
                         command = ""
                 if data == '\x08':
                     command = command[:-2]
             else: # Timeout on read
                 try:
                     self._socket.sendall("event,heartbeat\r\n") # Send heartbeat
                 except:
                     self._socket.close()
                     break

心跳的sendall永远不会抛出异常,并且recv只会抛出超时(如果客户端在正常情况下正确关闭连接,则会发生另一个异常)。

有什么想法吗?我错了,发送到没有ACK的客户端最终会产生异常(我已经测试了几分钟)。

1 个答案:

答案 0 :(得分:0)

您正在观察的行为是TCP套接字连接的预期行为。特别是,一般情况下,TCP堆栈无法知道以太网电缆已被拉动或(现在物理上断开连接的)远程客户端程序已关闭;所有它知道的是它已经停止接收来自远程对等体的确认数据包,并且它知道所有数据包都可能被某个过载的路由器丢弃而且该问题会暂时解决。鉴于此,当它的数据包未得到确认时,它会执行TCP始终执行的操作:它会降低其传输速率及其数据包传输限制,并重新传输未确认的数据包,希望它们能够通过此时间。

假设服务器的套接字有待处理的传出数据,TCP堆栈最终将(即几分钟后)确定没有数据经过足够长的时间,并单方面关闭连接。因此,如果您的问题检测时间只有几分钟,那么避免僵尸连接问题的最简单方法就是确保定期通过TCP连接发送一些心跳数据,如您所述。当TCP堆栈尝试(并反复失败)以获取发送和确认的传出数据时,最终将触发它关闭连接。

如果你想要比这更快的东西,你需要实现你自己的挑战/响应系统超时(通过TCP套接字,或通过单独的TCP套接字,或通过UDP),但请注意这样做您可能会自己遭受误报(例如,您最终可能会切断一个实际上并未死亡的TCP连接,但只会因拥塞而导致丢失数据包的临时情况)。这是否值得权衡取决于你正在编写什么类型的程序。 (另请注意,UDP有自己的问题,特别是如果您希望系统跨防火墙工作等)