WebSocket双向读写时程序阻塞问题求助(附代码)
Boost WebSocket 双向读写阻塞问题解决思路
你的代码出现阻塞的核心原因是:同步ws_.read()调用会一直阻塞当前线程,直到从WebSocket连接中读取到数据。如果对方没有及时返回消息,你的程序会卡死在这个调用上,同时消息队列的pop_back()也无法执行,导致后续消息无法处理。
解决方案分三种场景给出:
1. 优先使用异步操作(Boost Beast官方推荐模型)
异步模型不会阻塞线程,读写操作各自通过回调完成,完全解耦。示例代码如下:
// 持有WebSocket连接的类需要继承enable_shared_from_this,保证生命周期 class WsSession : public std::enable_shared_from_this<WsSession> { public: explicit WsSession(boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws) : ws_(std::move(ws)) {} // 异步发送消息 void do_write(std::string msg) { auto self(shared_from_this()); boost::beast::async_write(ws_, boost::asio::buffer(msg), [self](boost::beast::error_code ec, std::size_t bytes_sent) { if (!ec) { // 发送完成后,启动下一次异步读取 self->do_read(); } else { std::cerr << "写入错误: " << ec.message() << std::endl; } }); } // 异步读取消息 void do_read() { auto self(shared_from_this()); ws_.async_read(buffer_, [self](boost::beast::error_code ec, std::size_t bytes_read) { if (!ec) { std::string received = boost::beast::buffers_to_string(buffer_.data()); std::cout << "收到数据: " << received << std::endl; buffer_.consume(bytes_read); // 清空缓冲区,准备下一次读取 // 持续监听下一条消息 self->do_read(); } else { std::cerr << "读取错误: " << ec.message() << std::endl; } }); } private: boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws_; boost::beast::flat_buffer buffer_; };
启动时先调用do_read()开始监听,之后通过do_write()发送消息即可,读写互不阻塞。
2. 同步模型下用多线程分离读写
如果必须用同步调用,需要单独开一个线程负责持续读取,主线程处理写入逻辑,避免读写互相阻塞:
// 读取线程函数 void run_read_loop(boost::beast::websocket::stream<boost::asio::ip::tcp::socket>& ws) { boost::beast::flat_buffer buffer; while (true) { boost::system::error_code ec; ws.read(buffer, ec); if (ec) { std::cerr << "读取线程退出: " << ec.message() << std::endl; break; } std::string received = boost::beast::buffers_to_string(buffer.data()); std::cout << "收到数据: " << received << std::endl; buffer.consume(buffer.size()); // 清空缓冲区 } } // 主线程处理写入 void main_process(std::queue<std::string>& messageQ, boost::beast::websocket::stream<boost::asio::ip::tcp::socket>& ws) { // 启动读取线程 std::thread read_thread(run_read_loop, std::ref(ws)); read_thread.detach(); // 根据需求选择detach或join管理线程 // 处理消息队列的写入 while (!messageQ.empty()) { auto msg = messageQ.front(); messageQ.pop(); // 先弹出消息,避免队列积压 boost::system::error_code ec; ws.write(boost::asio::buffer(msg), ec); if (ec) { std::cerr << "写入错误: " << ec.message() << std::endl; break; } } }
3. 修正现有同步代码的逻辑缺陷
如果不想改架构,至少要修复队列处理和超时问题,避免无限阻塞:
if (!messageQ.empty()) { // 先弹出消息,避免read阻塞导致队列无法处理后续内容 auto msg = *messageQ.front(); messageQ.pop_back(); boost::beast::flat_buffer buffer; // 执行写入 boost::system::error_code write_ec; ws_.write(boost::asio::buffer(msg), write_ec); if (write_ec) { std::cerr << "写入错误: " << write_ec.message() << std::endl; return; } // 给read设置超时,避免无限阻塞 ws_.next_layer().expires_after(std::chrono::seconds(3)); boost::system::error_code read_ec; ws_.read(buffer, read_ec); if (read_ec == boost::asio::error::operation_aborted || read_ec == boost::beast::error::timeout) { std::cout << "读取超时" << std::endl; } else if (!read_ec) { std::string stringVal = boost::beast::buffers_to_string(buffer.cdata()); std::cout << "stringVal = " << stringVal << std::endl; } else { std::cerr << "读取错误: " << read_ec.message() << std::endl; } }
关键提醒
- 同步
read()是阻塞调用,必须确保对方会发送数据,否则必然卡死; - 异步模型是Boost Beast的设计初衷,能更好地处理高并发和连接管理;
- 消息队列的操作要放在阻塞调用之前,避免队列积压。
内容的提问来源于stack exchange,提问作者Taki
相关产品推荐
相关产品推荐

