TCP 客户端和服务器连接断开

84 阅读5分钟

为了在一个 Python TCP 服务器中管理客户端连接,需要确定何时断开客户端连接。为了实现这个目标,可以为每个客户端创建一个任务,每隔一段时间(如 1-2 秒)向服务器发送一个数据包,以报告其活动状态。服务器上创建一个任务定期更新所有连接的超时时间,如果某一连接的超时时间超过 6 秒,则认为该连接已断开,并将其从服务器中移除。需要确定这种方式是否合适,是否存在更好的方法,以及 6 秒是否是一个合理的超时时间。

2、解决方案

方案一:使用队列和线程进行管理

  1. 每个客户端连接由以下几个部分组成:

    • 套接字句柄(由 accept 调用获取)
    • 队列(称为“逻辑队列”)
    • 线程(称为“套接字线程”)
    • 线程(称为“逻辑线程”)
  2. 这些组件之间的关系如下:

    • 套接字线程只从套接字句柄中读取数据,并将消息放入队列中。
    • 逻辑线程只向套接字句柄中写入数据,只从队列中读取消息。
    • 套接字线程将在队列中放置以下类型的消息:
      • 从客户端收到的输入
      • 客户端超时或客户端关闭连接
  3. 逻辑线程从队列中读取消息并进行处理,它可以接收以下类型的消息:


    - 客户端关闭连接/客户端超时 - 从客户端接收到的输入 - 另一个客户端连接/断开

4. 另外,需要创建其他一些线程和队列:
- 一个用于执行 accept() 并创建新连接的线程(称为“接收线程”)
- 一个用于向其他逻辑线程广播消息的线程(称为“广播线程”)
- 一个供广播线程读取、其他线程写入的队列(称为“广播队列”)
- 所有线程都需要访问广播队列。
- 只有广播线程从广播队列中读取数据,accept 线程和所有逻辑线程都可以向广播队列中写入数据。
- 广播线程需要知道所有逻辑线程的队列。

5. 以下是客户端超时时发生的情况:

- 客户端超时
- 该客户端的套接字线程将“客户端超时”消息放入其队列中
- 逻辑线程获取消息,关闭套接字,终止套接字线程,并将消息放入主服务器线程的输入队列中
- 主服务器线程获取消息并将其广播到所有其他客户端的队列中
- 每个客户端的逻辑线程获取客户端断开连接的消息,并通过其套接字发出通知

6. 以下是创建新客户端连接时发生的情况:

- accept 线程检测到一个新连接
- accept 线程创建一个新的套接字线程、逻辑线程和队列
- accept 线程将“新连接”消息排入广播队列
- 广播线程看到新连接消息,并将其广播到其他逻辑队列
- 每个逻辑线程获取消息并通过套接字发送一些通知

方案二:使用 select 函数

  1. 可以使用 select 函数来监控客户端连接的活动状态。select 函数可以同时监视多个文件描述符,并返回已准备好进行读/写操作的文件描述符列表。

  2. 对于每个客户端连接,可以创建一个套接字并将其添加到 select 函数的监视列表中。

  3. 服务器可以定期调用 select 函数来检查是否有任何客户端连接已经超时。如果某个客户端连接已经超时,则可以将其从服务器中移除。

以下是 select 函数的使用示例:

import select

# 创建一个套接字并将其添加到 `select` 函数的监视列表中
# 假设套接字对象为 `client_socket`

select_list = [client_socket]

while True:
    # 调用 `select` 函数来检查是否有任何客户端连接已经准备好进行读/写操作
    ready_list, _, _ = select.select(select_list, [], [], 1)

    # 如果 `client_socket` 在 `ready_list` 中,则表示该客户端连接已经准备好进行读/写操作
    if client_socket in ready_list:
        # 从客户端连接中读取数据
        data = client_socket.recv(1024)

        # 处理从客户端连接中读取到的数据

    # 如果 `client_socket` 不在 `ready_list` 中,则表示该客户端连接已经超时
    else:
        # 从服务器中移除该客户端连接
        select_list.remove(client_socket)
        client_socket.close()

方案三:使用 Python socketserver 模块

  1. Python socketserver 模块可以帮助你更轻松地创建 TCP 服务器。

  2. socketserver 模块提供了几个预定义的类,可以用来创建不同的类型的服务器。如果要创建一个简单的 TCP 服务器,则可以使用 TCPServer 类。

  3. TCPServer 类提供了一个 handle_timeout() 方法,可以用来处理客户端连接的超时。如果某个客户端连接已经超时,则可以调用 handle_timeout() 方法来将其从服务器中移除。

以下是用 socketserver 模块创建 TCP 服务器的示例:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        # 处理客户端连接
        pass

    def handle_timeout(self):
        # 从服务器中移除该客户端连接
        self.request.close()

class MyTCPServer(socketserver.TCPServer):
    def handle_timeout(self):
        # 从服务器中移除该客户端连接
        self.close_request(self.request)

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    server = MyTCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()