为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

胡说叔叔 2021-09-11 10:38:12
我有一个使用 select() 的简单服务器,如下所示:#!/usr/bin/env python2import select, socketserver = 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 异常)。我知道我可能可以捕获异常并处理它,但我更好奇为什么:select() 没有发现客户端消失的事实我认为 select() 会寻找的最重要的异常类型之一是客户端是否消失。或者它正在寻找乱码的输入?即使我显式启用 TCP Keepalive,select() 仍然将超时套接字放入可读列表而不是异常列表这是预期的吗?有没有办法让 select() 将消失的客户端放入例外列表中?或者重要的是不要假设仅仅因为 select() 表示套接字已准备好读取,recv() 不会失败?编辑:这个问题不是我之前在这里问的问题的重复,因为这个问题专门处理 select() 以及它如何处理异常。这个实际上包括我从另一个问题中学到的代码。
查看完整描述

1 回答

?
犯罪嫌疑人X

TA贡献2080条经验 获得超4个赞

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

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


查看完整回答
反对 回复 2021-09-11
  • 1 回答
  • 0 关注
  • 504 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信