【问题标题】:Python; Troubles controlling dead sockets through selectPython;通过选择控制死套接字的问题
【发布时间】:2013-11-05 17:53:51
【问题描述】:

我有一些代码将连接到主机,除了侦听传入数据之外什么都不做,直到客户端关闭或主机发送关闭语句。为此,我的代码运行良好。

但是,当主机在没有发送关闭语句的情况下死亡时,我的客户端会按预期一直监听传入的数据。为了解决这个问题,我每隔 foo 秒设置一次套接字超时,并开始检查连接是否处于活动状态。从 Python socket howto 我发现了这个:

select 的一个非常讨厌的问题:如果在这些套接字的输入列表中的某个地方是一个已经严重死亡的套接字,则 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

这段文字提出了三个问题。

  1. 我被告知要检查连接是否处于活动状态,必须写入套接字并查看响应是否返回。如果不是,则必须假定连接已死。在文本中它说要检查错误的连接,一个单独的每个套接字,将它传递给选择的第一个参数并将超时设置为零。这将如何确认套接字是否已死?
  2. 为什么不通过尝试写入套接字来测试套接字是死是活?
  3. 当连接处于活动状态和断开连接时,我在寻找什么? Select 将立即超时,因此没有数据将证明什么都没有。

我意识到有像 geventasyncoretwisted 这样的库可以帮助我解决这个问题,但我选择自己做这件事是为了更好地了解正在发生的事情并获得更多控制权在源头上我自己。

【问题讨论】:

  • 我不确定 Python howto 是否正确,必须遍历所有套接字以确定哪个套接字“死得很惨”。以我的经验,只有当你向它传递一个无效的文件描述符时,select() 才会失败,即使是远程对等端突然断开的套接字仍然是一个完全有效的套接字,直到你明确地关闭它。因此,请注意这一点。
  • @JeremyFriesner 我认为写入死套接字会发现它已死(在尝试重新传输段后,客户端将关闭套接字并将 RST 发送到服务器。
  • @Den 是的,你是对的。

标签: python sockets select tcp blocking


【解决方案1】:

如果连接的客户端崩溃或退出,但其主机操作系统和计算机仍在运行,则其操作系统的 TCP 堆栈将向您的服务器发送一个 FIN 数据包,让您计算机的 TCP 堆栈知道 TCP 连接已关闭。您的 Python 应用程序会将此视为 select(),表示客户端的套接字已准备好读取,然后当您在套接字上调用 recv() 时,recv() 将返回 0。发生这种情况时,您应该通过关闭来响应插座。

另一方面,如果连接的客户端的计算机永远没有机会发送 FIN 数据包(例如,因为有人伸手将其以太网线或电源线从插座中拔出),那么您的服务器将不会意识到TCP 连接失效了很长一段时间——可能永远失效。避免出现“僵尸套接字”的最简单方法就是让您的服务器每隔一段时间在套接字上发送一些虚拟数据,例如每分钟一次或什么的。客户端应该知道丢弃虚拟数据。发送虚拟数据的好处是您的服务器的 TCP 堆栈会注意到它没有为它发送的数据包返回任何 ACK 数据包,并将重新发送它们;并且在重新发送几次后,您的服务器的 TCP 堆栈将放弃并确定连接已失效,此时您将看到与我在第一段中描述的相同的行为。

【讨论】:

    【解决方案2】:
    1. 如果您向套接字写入内容,然后等待应答以检查连接,则服务器应支持此“ping”消息。并非总是如此。否则,如果服务器不等待此消息,服务器应用程序可能会自行崩溃或断开您的客户端。 如果 select 以您描述的方式失败,则套接字框架知道哪个套接字已死。你只需要找到它。但是,如果套接字因服务器应用程序崩溃等令人讨厌的死亡而死亡,这并不意味着客户端的套接字框架必须检测到这一点。例如。在客户端等待来自服务器的一些消息并且服务器崩溃的情况下,在某些情况下客户端可以永远等待。例如Putty,为了避免这种情况,可以使用服务器的应用程序的协议级ping(SSH ping选项)来检查连接; SSH 服务器可以使用 TCP keepalive 来检查连接并防止网络设备在没有活动的情况下断开连接。
    2. (参见第 1 页)。
    3. 你是对的,选择超时并且没有数据证明什么都没有。正如文档所说,您必须在选择失败时检查每个插座。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多