asyncio原生支持异步tcp/udp:tcp客户端用await asyncio.open_connection获取(reader, writer),服务端用asyncio.start_server配async handler;udp必须继承datagramprotocol实现回调,无await版recvfrom/sendto。

Python 的 asyncio 原生支持异步 TCP/UDP,但直接用 asyncio.open_connection 或 asyncio.start_server 就够用,不需要手动封装底层 socket;UDP 则必须用 asyncio.DatagramProtocol,没有“await 版 sendto/recvfrom”这种便利函数。
如何用 asyncio 建立异步 TCP 连接(client 端)
别碰 socket.socket + loop.create_connection 这类低阶 API,除非你要控制 SSL 或自定义 transport。标准做法是直接 await asyncio.open_connection:
- 它返回
(reader, writer),reader.read(n)和writer.write(data)都是 awaitable -
reader.read()默认读到 EOF,TCP 流无消息边界,你得自己处理粘包(比如按行reader.readline()或带长度头) - 写完记得
writer.drain(),否则背压时会内存暴涨;writer.close()后要await writer.wait_closed() - 超时必须显式加
asyncio.wait_for(..., timeout=5),open_connection本身不接受 timeout 参数
示例片段:
import asyncio
async def tcp_client():
reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
writer.write(b'HELLO\n')
await writer.drain()
data = await reader.readline()
print(data.decode().strip())
writer.close()
await writer.wait_closed()
如何实现异步 TCP 服务端(server 端)
用 asyncio.start_server,不是 loop.create_server(后者返回 protocol factory,更底层)。关键点:
- handler 函数必须是
async def,且第一个参数是reader,第二个是writer - 每个新连接都会并发调用一次 handler,不用手动
await asyncio.create_task——start_server内部已处理 - 务必在 handler 结束前调用
writer.close()+await writer.wait_closed(),否则连接泄漏 - 如果要支持长连接并循环收发,用
while True:+try/except捕获ConnectionResetError或asyncio.IncompleteReadError
为什么 UDP 必须用 DatagramProtocol 而不能 await recvfrom
因为 UDP 是无连接、无状态的 datagram 接口,asyncio 无法像 TCP 那样抽象出“流式 reader/writer”。你必须继承 asyncio.DatagramProtocol 并实现 datagram_received 和 error_received:
-
datagram_received(self, data, addr)是回调,不是协程,不能await;如需异步操作,得用asyncio.create_task或loop.create_task - 发送用
self.transport.sendto(data, addr),同步调用,不阻塞 event loop - 绑定端口用
loop.create_datagram_endpoint,返回(transport, protocol),transport 用于发,protocol 用于收 - 没有内置超时或重传,丢包、乱序、重复全靠上层逻辑处理
常见误操作:试图在 datagram_received 里直接 await asyncio.sleep —— 这会让后续 datagram 排队,实际吞吐暴跌。
asyncio 与多进程/多线程混用时的坑
asyncio event loop 是线程绑定的:默认只在主线程运行,且一个线程只能有一个 running loop。所以:
- 子线程里不能直接
asyncio.run(),会报RuntimeError: asyncio.run() cannot be called from a running event loop - 想在子线程跑协程,得先
new_loop = asyncio.new_event_loop(),再asyncio.set_event_loop(new_loop),最后new_loop.run_until_complete(...) - 多进程更简单:每个进程调用
asyncio.run(main())即可,但注意fork后的子进程不能复用父进程的 loop - 千万别在协程里调用阻塞 IO(如
time.sleep、requests.get),必须用asyncio.to_thread(3.9+)或loop.run_in_executor
UDP server 如果要高吞吐,常搭配 SO_REUSEPORT 和多进程,但每个进程得有自己的 DatagramProtocol 实例和独立绑定 —— 这点比 TCP 复杂得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











