单机支撑5000+ websocket连接需避开三大坑:连接管理(用set+心跳+异常移除)、消息广播(asyncio.gather并发发送+分批+过滤)、阻塞调用(全程异步+线程池+异步db/http)。

WebSocket服务端撑不住50+连接?先确认是不是用了同步阻塞模型
绝大多数“撑不住多客户端”的问题,根源不在并发数本身,而在服务端用的是同步 I/O 模型。比如 C# 示例里 listener.GetContextAsync() 后直接调用 AcceptWebSocketAsync().Result —— 这个 .Result 会阻塞线程,且每个连接独占一个线程,50 个连接就卡死整个服务。Android 上更明显:主线程一阻塞,UI 冻结,系统还可能直接杀掉进程。
必须换非阻塞模型:
- Python 用 websockets 或 FastAPI + Uvicorn(底层是 asyncio)
- Java 用 Netty 或 Spring WebFlux(非 Tomcat 同步 Servlet 容器)
- C# 避免 .Result/.Wait(),全程 await AcceptWebSocketAsync()
- Android 必须用 Netty 或 OkHttp 的异步 WebSocket,不能手写 BIO 循环
为什么别自己用线程池管理 WebSocket 连接
看到“线程池”就开一堆 Thread 或 QThread 去跑 QWebSocket 实例?这在 Qt 客户端侧可行(如把多个 QWebSocket move 到同一子线程),但服务端侧是典型误用。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
原因很直接:
- 每个线程维护一个 TCP 连接 → 线程数 = 连接数 → 5000 连接 = 5000 线程 → 内存爆炸、上下文切换开销压垮 CPU
- 线程间无法共享事件循环,广播消息得逐个锁 + 发送,性能反不如单线程异步
- Android 上超过 10 个线程就触发后台限制,服务被静默终止
真正该池化的不是“连接”,而是:
- CPU 密集型任务(如 JSON 解析、加解密)→ 丢进 asyncio.to_thread() 或 QThreadPool.globalInstance()
- HTTP/DB 调用 → 用异步客户端(httpx.AsyncClient、asyncpg)
- 心跳检测逻辑 → 统一由后台 task 轮询 active_connections set
FastAPI/Uvicorn 下 WebSocket 连接管理的三个硬性动作
默认写法看似能跑,但上 500 连接后内存持续上涨、部分连接假死,基本是因为漏了这三步:
- 用 set 存连接,不用 list:
✓ active_connections = set(),增删 O(1),天然去重
✗ active_connections = [],异常断连后不清理 → 泄漏
- 每次发送前必须 try/except:
✓ 广播时对每个 websocket 包一层 try: await websocket.send_text(...) except (WebSocketDisconnect, ConnectionClosed, Exception): active_connections.discard(websocket)
✗ 只 catch WebSocketDisconnect,漏掉网络抖动导致的 OSError 或 ConnectionResetError
- 心跳必须由服务端主动发 pong:
✓ 在 websocket_endpoint 内启一个后台 task:asyncio.create_task(ping_loop(websocket)),每 30 秒发一次 await websocket.ping()
✗ 依赖客户端 ping → 断网后服务端毫无感知,连接滞留数小时
Qt 客户端用 QWebSocket 做多连接时,线程绑定的关键细节
Qt6 的 QWebSocket 本身不是线程安全的,但可以多实例跨线程 —— 前提是每个实例严格绑定到**唯一一个线程**,且所有操作(open()、sendTextMessage()、信号连接)都发生在线程内。
正确姿势:
- 创建 QObject 派生类(如 WebSocketWorker),把 QWebSocket* 成员和所有通信逻辑写在槽函数里
- 用 moveToThread() 把 worker 对象移入新线程(不是 QThread::start() 启动线程再 new 对象)
- 所有控制信号(如 connectToHost())必须从主线程 emit,由 worker 槽函数在子线程执行
- 千万别在主线程直接调用 websocket->sendTextMessage() —— 会 crash 或静默失败
容易忽略的点:
- QWebSocket 的 errorOccurred() 和 sslErrors() 信号必须在同一线程 connect,否则收不到
- 关闭连接必须调用 close() 并等 closed() 信号,不能只 delete 对象










