You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 13:10:44