如何处理boost::beast::websocket的多async_write异步调用?
解决Boost.Beast WebSocket异步服务器多写冲突问题
核心问题是Boost.Beast的WebSocket异步操作不允许同时存在多个未完成的同类型操作(比如并发发起async_write),这会触发断言导致程序终止。strand仅能保证回调串行执行,但无法阻止你在已有写操作未完成时发起新的写请求,因此必须通过消息队列+串行触发的方式解决。
具体实现方案
给每个WebSocket会话添加消息队列,确保同一时间只有一个async_write在运行:
会话类增加成员
为每个会话维护:- 一个消息队列(比如
std::queue<std::string>或std::queue<boost::beast::flat_buffer>),缓冲待发送的消息 - 绑定到WebSocket executor的strand,保证队列操作的线程安全
- 一个消息队列(比如
封装线程安全的发送接口
提供公共的send方法,通过strand将消息投递到会话的io_context线程中处理,避免并发修改队列:void send(std::string msg) { boost::asio::post(ws_.get_executor(), boost::beast::bind_front_handler( &session::do_send, shared_from_this(), std::move(msg))); }内部处理消息入队与写触发
在strand上下文内将消息加入队列,若队列之前为空,则立即触发写操作:void do_send(std::string msg) { bool was_empty = write_queue_.empty(); write_queue_.push(std::move(msg)); if (was_empty) { do_write(); } }串行执行写操作
实现do_write发起异步写,在回调中检查队列是否还有剩余消息,若有则继续触发下一次写:void do_write() { auto self(shared_from_this()); boost::beast::websocket::async_write( ws_, boost::beast::buffer(write_queue_.front()), boost::asio::bind_executor( strand_, [self](boost::beast::error_code ec, std::size_t bytes_transferred) { self->on_write(ec, bytes_transferred); })); } void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if (ec) { return fail(ec, "write"); } write_queue_.pop(); if (!write_queue_.empty()) { do_write(); } }
关键注意点
- 所有对队列的操作必须在strand上下文内执行,避免多线程并发修改
- 监听线程发送事件时,只需调用会话的
send接口,无需直接操作WebSocket的写方法 - Beast的WebSocket stream默认会自动响应ping/pong,若需主动发送ping,也需将ping操作加入队列或遵循串行执行规则
内容的提问来源于stack exchange,提问作者Ghasem Ramezani
相关产品推荐
相关产品推荐

