高并发、低延迟场景优先选uwebsockets;需深度定制协议或已有boost生态则选boost.beast。前者基于libusockets,零拷贝无锁,单核扛10w+连接;后者依赖asio,内存管理较重,易因shared_ptr分配卡堆。

WebSocket 服务器选 uWebSockets 还是 Boost.Beast?
直接说结论:高并发、低延迟场景优先选 uWebSockets;需要深度定制协议或已有 Boost 生态的项目,再考虑 Boost.Beast。前者基于 libusockets,零拷贝、无锁事件循环,单核轻松扛 10w+ 连接;后者依赖 ASIO,对象生命周期和内存管理稍重,压测时容易因 std::shared_ptr 频繁分配卡在堆上。
常见错误现象:Boost.Beast 里忘记调用 stream.set_option(websocket::stream_base::timeout::suggested(beast::role_type::server)),导致空闲连接不自动断开,连接数缓慢泄漏;uWebSockets 中误用 res->end() 替代 res->close() 关闭 WebSocket,结果触发 HTTP 断连逻辑而非 WS 正常关闭。
-
uWebSockets编译必须开-DUSING_UWS,否则uWS::App()构造失败且报错极隐晦(只提示undefined reference to uWS::App::App()) -
Boost.Beast的websocket::stream不是线程安全的,每个连接必须绑定唯一io_context::strand,否则并发write可能 crash - 二者都不建议在
onMessage回调里做阻塞 IO(如读文件、查数据库),必须异步调度到 worker 线程池,否则整个事件循环卡住
如何避免 uWebSockets 的内存泄漏和连接堆积?
根本原因不是代码写错,而是默认行为太“省事”:它不自动清理断连残留的 WebSocket 对象,尤其当客户端异常掉线(比如 kill -9 浏览器进程),服务端收不到 close 帧,onClose 根本不触发。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 启用心跳:在
App().ws(...)配置里加.pingInterval(10000).maxBacklog(1024),并实现onPing和onPong,配合ws->getBufferedAmount() > 1024 * 1024主动ws->close() - 给每个
ws绑定一个std::chrono::steady_clock::time_point记录最后活跃时间,在onMessage/onPing里更新,在定时器中扫描超时(比如 60s 无活动)的连接并ws->close() - 不要在
onConnection里 new 对象存到全局 map,改用ws->setUserData(new MySession),并在onClose或onDisconnection里delete static_cast<mysession>(ws->getUserData())</mysession>
Boost.Beast 的 async_read 为什么总卡住不回调?
不是网络问题,大概率是 flat_buffer 没预分配空间,或者 read 调用时机不对。Beast 默认 buffer 初始容量为 0,第一次 async_read 会先分配内存再 copy,但若此时远端发的是小包(比如 4 字节 ping),可能因 buffer 不足被截断,后续帧永远等不到完整消息体。
使用场景:适合需要解析混合帧(如 WebSocket + 自定义二进制头)或需与现有 ASIO TCP 服务共用 io_context 的情况。
- 声明 buffer 时务必指定初始大小:
beast::flat_buffer buffer{4096},而不是beast::flat_buffer buffer -
websocket::stream的async_read必须在前一次回调完成后再发起,不能在on_message里立刻再 call 一次 —— 否则会触发boost::asio::error::operation_not_supported - 如果用了
stream.binary(true),但客户端发的是文本帧,async_read会静默失败(不报错也不回调),需检查ws.got_text()/ws.got_binary()
并发模型:用 std::thread 手搓线程池还是交给 uWebSockets 自带的 run?
uWebSockets 的 App().run() 内部已封装多线程 epoll/kqueue 循环,调用它就等于启动了最优并发模型;自己额外开 std::thread 跑多个 App 实例反而降低性能 —— 因为每个实例都独占 fd 和事件队列,TCP accept 会竞争,连接分布不均。
真正要做的,是把 CPU 密集型任务(如 protobuf 解析、加密验签)从主线程挪出去:
- 用
std::async(std::launch::async, []{ /* heavy work */ })包裹耗时操作,完成后用ws->send()推送结果(注意:ws对象不能跨线程访问,必须用ws->getNativeHandle()或自定义 ID 在主线程查表) - 避免用
std::queue+std::mutex做任务分发 —— 锁争用会成为瓶颈;改用moodycamel::ConcurrentQueue或boost::lockfree::queue - 如果必须多
io_context,别手写调度,直接用uWS::Loop::get()->defer([]{ /* main thread only */ })把回调安全投递回主事件循环
最易被忽略的一点:所有 WebSocket 库都假设你的业务逻辑不修改 ws 生命周期外的状态。比如在 onMessage 里修改了某个全局 std::map,但没加锁 —— 这不是并发模型的问题,是 C++ 基础错误,调试时表现为随机 core dump,跟网络库无关。











