核心是用 async for message in websocket 接收消息,因其封装了缓冲、心跳和异常恢复;手动 await websocket.recv() 易导致漏消息、卡死或断连,仅在需超时控制或精确响应时机时使用,并须配合 asyncio.wait_for 和异常捕获。

用 websockets 接收消息,核心就一条:在 async for 循环里调用 websocket.recv(),别直接用 await websocket.recv() 在循环外反复等——那会漏消息、卡住、甚至断连。
为什么 async for message in websocket 是推荐写法
这是 websockets 库对连接对象做的异步迭代器封装,内部自动处理了消息缓冲、心跳响应和连接异常恢复逻辑。手动 await websocket.recv() 虽然语法合法,但容易写出阻塞式轮询,尤其在没加超时或没捕获 ConnectionClosed 时,一断就连不上了。
常见错误现象:RuntimeError: await wasn't used with future(误把协程当同步函数调)、ConnectionClosedOK 后程序静默退出、收到第一条消息就停住。
- 必须搭配
async def函数使用,不能在普通函数里写 - 底层仍调用
recv(),但封装了重试和 close 帧识别 - 如果需要区分文本/二进制帧,得自己检查
isinstance(message, str)或bytes
websocket.recv() 什么时候该手动用
只在两种场景下绕过 async for:需要精确控制单次接收时机(比如发指令后等特定响应),或要设超时防止卡死。
立即学习“Python免费学习笔记(深入)”;
典型使用场景:客户端发完认证包,必须在 5 秒内收到 {"status": "ok"},否则主动关连接。
- 务必用
asyncio.wait_for(websocket.recv(), timeout=5)包一层,否则超时无效 - 捕获
asyncio.TimeoutError和websockets.exceptions.ConnectionClosed - 不要连续多次
await websocket.recv()而不检查返回值类型——服务端可能发 ping 帧或 close 帧
消息接收后怎么安全解析 JSON
websockets 不自动解析 JSON,所有消息都是 str 或 bytes。直接 json.loads(message) 会崩在二进制帧上,也容易因格式错抛 JSONDecodeError。
最容易被忽略的点:服务端可能发空字符串、纯空白、或非 UTF-8 编码的 bytes(比如 protobuf 数据)。
- 先判断
isinstance(message, bytes),是的话按需用message.decode("utf-8", errors="ignore") - 用
try/except json.JSONDecodeError:包住解析逻辑,别让单条坏数据终止整个连接 - 如果协议明确是 JSON,可在连接建立后发个
{"type": "handshake"}测试通路,而不是等第一业务消息才解析
真正麻烦的从来不是“怎么收”,而是“收到乱七八糟的东西怎么不崩”。WebSocket 连接生命周期长,中间网络抖动、服务端升级、协议微调都会让消息结构变味——留好 except,打上日志,比写完美解析逻辑重要得多。


















