核心三件事:启服务端用serve()、连客户端用connect()、收发消息用await recv()/send();但重连、心跳、异常处理需手动补全。

直接说结论:用 websockets 库实现 WebSocket 通信,核心就三件事——启服务端靠 serve(),连客户端靠 connect(),收发消息统一用 recv() 和 send();但默认不处理重连、心跳、异常断开,这些得自己补。
怎么快速跑通一个最小可用服务端和客户端
别碰 asyncio 底层循环,直接用 websockets.serve() 启服务,用 websockets.connect() 建连接。关键不是“写全”,而是让两端能互相看到消息:
- 服务端函数必须是
async,且接受两个参数:websocket(连接对象)和path(请求路径) - 客户端用
async with websockets.connect("ws://localhost:8765") as ws:管理生命周期,避免忘记关闭 - 收发必须用
await ws.recv()和await ws.send("hello"),同步调用会报RuntimeError: no running event loop
示例服务端片段:
import asyncio
import websockets
<p>async def echo(websocket, path):
async for message in websocket:
await websocket.send(f"echo: {message}")</p><p>asyncio.get_event_loop().run_until_complete(
websockets.serve(echo, "localhost", 8765)
)
asyncio.get_event_loop().run_forever()</p>
为什么客户端一断开,服务端就抛 ConnectionClosedOK 或 ConnectionClosedError
这不是 bug,是 websockets 的正常行为:它把连接终止当作可预期的异常来抛出,而不是静默忽略。你得显式捕获,否则整个协程崩掉:
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
-
ConnectionClosedOK表示对方干净断开(如浏览器关标签) -
ConnectionClosedError表示非正常中断(如网络闪断、强制 kill 进程) - 必须在
async for message in websocket:外层包一层try/except,不能只 catch 循环内部
正确写法:
async def handler(websocket, path):
try:
async for message in websocket:
await websocket.send(f"got: {message}")
except websockets.exceptions.ConnectionClosedOK:
pass # 客户端主动断开,无需处理
except websockets.exceptions.ConnectionClosedError:
logging.warning("connection closed uncleanly")
怎么让客户端自动重连,又不疯狂重试
websockets 不提供重连逻辑,得自己控制重试间隔和退出条件。重点不是“加个 while True”,而是避免雪崩式重连:
- 不要裸写
while True: await connect(); ... except: continue,没延迟会打满 CPU 并触发服务端限流 - 每次失败后用
asyncio.sleep(1)做退避,首次失败等 1 秒,第二次等 2 秒,第三次等 4 秒(指数退避) - 设最大重试次数(比如 5 次),超限后抛出异常或发告警,而不是无限卡住
- 注意:
connect()超时需显式传ping_timeout=10和close_timeout=10,否则默认 20 秒才放弃
为什么发大消息会卡住或报 WebSocketPayloadTooBig
服务端默认限制单条消息 1MB,客户端也一样。超过就直接断连,不会截断或分片:
- 服务端启动时加参数:
websockets.serve(..., max_size=10 * 1024 * 1024)(设为 10MB) - 客户端连接时加:
websockets.connect(..., max_size=10 * 1024 * 1024) - 如果消息实在太大(比如百 MB 文件),别走 WebSocket,改用 HTTP 上传 + WebSocket 通知状态
- 另外,
send()是异步但不保证立刻发出去,大消息可能阻塞事件循环;真要高吞吐,得配合asyncio.to_thread()把序列化操作移出主线程
最常被忽略的一点:websockets 的 ping_interval 默认是 20 秒,但很多云厂商的负载均衡器(如 AWS ALB、阿里云 SLB)默认 60 秒无流量就断连接。结果就是——客户端没断,服务端没断,中间网关默默把连接掐了。解决办法只有两个:调短 ping_interval(比如设成 45),或者在业务层加自定义心跳包。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










