You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python3异步Socket无法接入多客户端问题求助

解决异步Socket服务器单客户端限制与断开延迟问题

问题根源分析

  1. 单客户端限制:当前代码中Server.start()方法里的await Connection(...).messaging()会阻塞主循环,必须等当前客户端的会话处理完毕,才会执行下一次server.accept(),导致无法同时处理多个客户端连接。
  2. 断开检测延迟:原生socket的recv是同步阻塞调用,会卡住asyncio事件循环,无法及时响应客户端断开事件;手动轮询时间戳的超时检测方式效率低,且无法精准触发断开逻辑。

解决方案:改用asyncio异步套接字API

使用asyncio原生的异步套接字接口,配合任务并发机制处理多客户端,同时用异步IO操作替代阻塞调用,解决上述问题:

import asyncio
from asyncio import StreamReader, StreamWriter

class Server:
    def __init__(self, port: int):
        self.port = port

    async def start(self):
        # 用asyncio.start_server创建异步服务器,自动处理并发连接
        server = await asyncio.start_server(
            self.handle_client, "0.0.0.0", self.port
        )
        print(f"Server started on {self.port}!")
        async with server:
            await server.serve_forever()

    async def handle_client(self, reader: StreamReader, writer: StreamWriter):
        ip = writer.get_extra_info('peername')[0]
        print(f"{ip} connected!")
        try:
            # 为每个客户端创建独立的会话任务,不阻塞主循环
            await Connection(reader, writer, ip).messaging()
        finally:
            writer.close()
            await writer.wait_closed()
            print(f"{ip} disconnected!")

class Connection:
    def __init__(self, reader: StreamReader, writer: StreamWriter, ip: str):
        self.reader = reader
        self.writer = writer
        self.ip = ip
        self.timeout = 7  # 客户端超时断开时间(秒)

    async def receive_exact(self, length: int) -> bytes:
        # 异步读取指定长度的数据,超时自动触发异常
        data = await asyncio.wait_for(self.reader.read(length), timeout=self.timeout)
        if len(data) < length:
            # 数据读取不足,判定客户端已断开
            raise ConnectionResetError("Client disconnected prematurely")
        return data

    async def messaging(self):
        while True:
            try:
                # 异步读取7字节头部
                header = await self.receive_exact(7)
                mtype = int.from_bytes(header[:2], "big")
                mlen = int.from_bytes(header[2:5], "big")
                # 异步读取指定长度的消息体
                mdata = await self.receive_exact(mlen)
                print(f"[C] {mtype} from {self.ip}")
            except (asyncio.TimeoutError, ConnectionResetError, OSError):
                # 超时或连接异常,退出会话循环
                break

asyncio.run(Server(9339).start())

关键改动说明

  • 并发连接处理:asyncio.start_server会为每个新客户端连接创建独立的handle_client任务,主循环无需等待单个客户端会话结束,实现多客户端同时在线。
  • 异步IO替代阻塞调用:用StreamReader.read替代原生socket的recv,不会阻塞事件循环,能及时响应客户端断开事件。
  • 精准超时检测:用asyncio.wait_for实现超时控制,替代手动时间戳轮询,既精准又节省CPU资源。
  • 统一断开处理:receive_exact方法封装了数据读取逻辑,统一处理数据不足的断开场景,避免重复代码。

内容的提问来源于stack exchange,提问作者KittenDEV

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:18:31