【问题标题】:How can you detect when a TCP client disconnects with Python's select()?如何检测 TCP 客户端何时与 Python 的 select() 断开连接?
【发布时间】:2018-12-08 20:13:06
【问题描述】:

我有一个使用 select() 的简单服务器,如下所示:

#!/usr/bin/env python2

import select, socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.bind(('localhost', 50000))
server.listen(5)

# TCP Keepalive Options
#server.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
#server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
#server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
#server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)

inputs = [server]

print "Listening on port 50000"

while True:
    readable, writable, exceptional = select.select(inputs, [], inputs)

    for s in readable:
        if s is server:
            connection, client_address = s.accept()
            print "New client connected: %s" % (client_address,)

            connection.setblocking(0)
            inputs.append(connection)
        else:
            data = s.recv(1024)

            if data:
                print "Data from %s: %s" % (s.getpeername(), data.replace('\n', ''))
            else:
                print "%s disconnected" % (s.getpeername(),)

                inputs.remove(s)
                s.close()

    for s in exceptional:
        print "Client at %s dropped out" % (s.getpeername(),)
        inputs.remove(s)
        s.close()

我可以使用 telnet 客户端连接到它,而且效果很好。它不响应客户端,但对于这个简单的示例,这很好。

我看到的问题是:如果客户端在没有发送 TCP FIN 或 TCP RST 的情况下断开连接,服务器似乎永远不会发现客户端已经消失。

我通过这样做来模拟客户端消失:

  • 运行服务器
  • 将 telnet 客户端连接到服务器
  • 使用 iptables 阻止 telnet 客户端与服务器通信

据我所知,正常的解决方案是打开 TCP Keepalive,我通过取消注释 TCP Keepalive 部分来实现。 当我这样做时,并按照相同的测试程序使客户端在连接的会话中间消失时,似乎当套接字 超时,select() 停止阻塞,并在“可读”列表中返回客户端(而不是例外列表)。这 导致我的服务器尝试使用 s.recv(1024) 从该套接字读取数据,这会导致服务器崩溃(s.recv() 会引发 socket.error 异常)。

我知道我可能可以捕获异常并处理它,但我更好奇为什么:

  1. select() 没有注意到客户端消失的事实
    • 我认为 select() 会寻找的最重要的异常类型之一是客户端是否消失。还是在寻找乱码输入?
  2. 即使我明确启用 TCP Keepalive,select() 仍然会将超时套接字放入可读列表而不是异常列表

这是预期的吗?有没有办法让 select() 把消失的客户放到例外列表中?或者重要的是不要仅仅因为 select() 表示套接字已准备好读取,recv() 不会失败?

编辑:这个问题不是我之前问过的问题here 的重复,因为这个问题专门处理 select(),以及它如何处理异常。这实际上包括我从另一个问题中学到的代码。

【问题讨论】:

标签: python sockets networking


【解决方案1】:

当您使用keepalive,并且它检测到故障时,select() 应该将套接字报告为可读和可写。然后,当您尝试执行其中一项操作时,您应该会收到错误消息。在s.recv() 调用周围使用try/except 来检测socket.error

您可能天真地认为这会被报告为“异常情况”,但事实并非如此。这用于有效套接字上的正常异常,例如带外数据。

【讨论】:

  • 我的代码包含您推荐的确切内容,我从您发布的确切链接中借用 - 启用和设置 TCP Keepalive 计时器。我只是想知道如果不是针对消失的客户端,select() 在异常方面正在寻找什么,以及为什么当我设置 TCP keepalive 计时器时,select() 仍然无法判断存在问题,而是认为客户端已准备好发送数据。
  • 抱歉,因为它们被注释掉了,所以没有注意到它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-15
  • 2012-10-11
  • 1970-01-01
相关资源
最近更新 更多