首页 文章

如何检测TCP客户端何时与Python的select()断开连接?

提问于
浏览
0

我有一个使用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异常) .

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

  • select()没有了解客户端消失的事实

  • 我认为select()要寻找的最重要的异常类型之一是客户端是否消失了 . 还是在寻找乱码?

  • 即使我明确启用TCP Keepalive,select()仍然将超时套接字放入可读列表而不是例外列表

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

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

1 回答

  • 1

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

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

相关问题