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

Python3.6+asyncio Windows10简易服务器无法读取数据排查

Troubleshooting Asyncio Server Data Reading Issues on Windows 10 (Python 3.6)

我之前也碰到过一模一样的问题——Windows 10上用Python 3.6+asyncio搭的简易服务器,死活读不到nc发过来的数据,但用原生socket写的同功能服务器却完全正常。试了基于协议和协程API的两种asyncio实现都不行?别慌,这大概率和Windows下asyncio的事件循环限制、数据接收逻辑,或者Python 3.6的版本特性有关,我给你一步步拆解解决方案:

1. 先解决Windows asyncio的核心限制

Python 3.6在Windows上默认用的是SelectorEventLoop,这个事件循环是基于select模型模拟的,对TCP套接字的某些IO场景支持很拉胯。换成Windows原生的ProactorEventLoop(基于IOCP完成端口),很多问题直接就消失了。

怎么改?
在你的代码开头显式设置事件循环策略:

import asyncio

# 针对Windows切换到Proactor事件循环
if asyncio.get_event_loop_policy().get_name() == 'WindowsSelectorEventLoopPolicy':
    asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())

2. 协程版服务器的接收逻辑要避坑

很多人踩坑是因为用reader.readline()但nc发的数据没带换行符,导致一直阻塞;或者用reader.read()不指定长度,等着EOF但nc没断开连接,也会卡住。

给你一个能正常工作的协程版示例:

import asyncio

async def handle_client(reader, writer):
    try:
        # 读取最多1024字节,设置5秒超时防止无限阻塞
        data = await asyncio.wait_for(reader.read(1024), timeout=5.0)
        message = data.decode('utf-8')
        addr = writer.get_extra_info('peername')
        print(f"收到来自 {addr} 的数据: {message!r}")

        # 回显数据给客户端
        writer.write(data)
        await writer.drain()
        print(f"已回显数据给 {addr}")
    except asyncio.TimeoutError:
        print("读取客户端数据超时")
    finally:
        # 确保关闭连接
        writer.close()
        await writer.wait_closed()

async def main():
    # 先处理Windows事件循环问题
    if asyncio.get_event_loop_policy().get_name() == 'WindowsSelectorEventLoopPolicy':
        asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
    
    server = await asyncio.start_server(handle_client, '127.0.0.1', 8888)
    addr = server.sockets[0].getsockname()
    print(f"服务器启动在 {addr}")

    async with server:
        await server.serve_forever()

if __name__ == '__main__':
    asyncio.run(main())

3. 基于协议的服务器要注意数据拼接

用asyncio.Protocol实现时,data_received可能会分批收到数据(比如大一点的数据包),不过你用nc测试的话一般不会有这个问题,但要确保回调逻辑正确处理每一次收到的数据。

正常工作的协议版示例:

import asyncio

class EchoProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
        self.addr = transport.get_extra_info('peername')
        print(f"客户端 {self.addr} 已连接")

    def data_received(self, data):
        message = data.decode('utf-8')
        print(f"收到来自 {self.addr} 的数据: {message!r}")
        # 直接回显数据
        self.transport.write(data)

    def connection_lost(self, exc):
        if exc:
            print(f"客户端 {self.addr} 断开连接,原因: {exc}")
        else:
            print(f"客户端 {self.addr} 正常断开连接")

async def main():
    # 切换到Proactor事件循环
    if asyncio.get_event_loop_policy().get_name() == 'WindowsSelectorEventLoopPolicy':
        asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
    
    loop = asyncio.get_running_loop()
    server = await loop.create_server(EchoProtocol, '127.0.0.1', 8888)
    addr = server.sockets[0].getsockname()
    print(f"服务器启动在 {addr}")

    async with server:
        await server.serve_forever()

if __name__ == '__main__':
    asyncio.run(main())

4. 确认你的nc测试方式没问题

Windows下的nc(比如Git Bash里的或者Nmap附带的)要确保数据确实发出去了:

  • 交互式测试:运行nc 127.0.0.1 8888,输入内容后按回车发送
  • 批量测试:用echo "测试数据" | nc 127.0.0.1 8888直接管道发送

为什么原生socket能正常工作?

原生socket直接调用Windows的套接字API,而Python 3.6默认的SelectorEventLoop是用select模型模拟的多路复用,在Windows下对TCP套接字的支持有缺陷。换成ProactorEventLoop后,asyncio会直接用Windows的IOCP机制,和原生socket的IO处理逻辑一致,自然就兼容了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:50:36