社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

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

John • 5 年前 • 1821 次点击  

我有一个使用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社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/43491
 
1821 次点击  
文章 [ 1 ]  |  最新文章 5 年前
Barmar
Reply   •   1 楼
Barmar    6 年前

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

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