直接选 websockets 库——轻量、标准、可控;Flask-SocketIO 引入 Socket.IO 协议层会导致原生 WebSocket 连接失败,需避免协议不匹配、并发写入乱序、连接泄漏等问题。

WebSocket 服务端用 websockets 还是 Flask-SocketIO?
直接选 websockets 库——轻量、标准、可控。除非你已经在用 Flask 且必须共享 session,否则 Flask-SocketIO 会悄悄引入 Socket.IO 协议层,导致 Web 端用原生 WebSocket 连接失败,错误通常是 Unexpected response code: 200 或连接后立即断开。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 服务端只处理连接管理、消息广播,不掺杂 HTTP 路由逻辑
- 客户端必须用原生
WebSocket(不是Socket.IO客户端库),否则协议不匹配 -
websockets默认不支持多进程,单机部署时别配 gunicorn 多 worker,会丢连接
如何让多个客户端实时收到新消息?
靠维护一个全局的连接集合,每次收到消息就遍历发送——但要注意:连接可能已断开,await websocket.send() 会抛 ConnectionClosed 异常,不捕获就会中断整个协程。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
set存活连接,每次发消息前用try/except包裹send() - 在
websocket.recv()循环里加timeout或心跳检测,避免僵尸连接占资源 - 别用全局变量存历史消息,需要持久化就写进 Redis 或 SQLite,Web 端首次连接时拉取最近 20 条
前端怎么正确处理连接断开和重连?
浏览器 WebSocket 对象断开后不会自动重连,而且 onclose 触发时状态已是 CLOSED,不能再调 send()。常见错误是用户发消息时连接已断,却没提示,导致消息“消失”。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 监听
onclose后立刻设置标志位,并启动指数退避重连(如 1s → 2s → 4s) - 发消息前检查
websocket.readyState === WebSocket.OPEN,否则缓存到队列,重连成功后再 flush - 服务端主动断连时,发个
{"type": "disconnect", "reason": "timeout"}消息,前端据此清空未确认 UI 状态
为什么消息收发偶尔乱序或丢失?
websockets 本身保证 TCP 层顺序,但乱序通常来自并发写入:多个协程同时对同一 websocket 调 send(),底层 write 缓冲区竞争导致粘包或错序。更隐蔽的问题是,Python 的 asyncio 事件循环里,没加锁的共享状态(比如用户在线列表)被并发修改,造成消息漏推。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 每个
websocket实例配一个asyncio.Lock,所有send()必须持锁 - 广播消息时,用
asyncio.create_task()并发发送,但每个任务内部仍要锁住对应连接 - 避免在
recv()循环里直接操作全局数据结构,改用asyncio.Queue中转指令
CLOSE_WAIT socket 把端口耗尽;又或者前端重连时没清空旧消息队列,导致同一条消息发三遍。这些没法靠框架自动解决。


















