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

Boost Beast WebSocket客户端无法优雅关闭,线程阻塞问题求助

问题:WebSocket客户端调用close()时阻塞无法返回

我基于Boost.Beast实现了WebSocket客户端,同时用CrowCpp搭建了服务端。但客户端调用close()(或通过析构函数触发)时,会在m_socket.close(boost::beast::websocket::close_code::normal);处阻塞,无法执行后续代码。调试发现客户端在等待服务端的关闭帧,但服务端已经收到关闭请求并发送了响应帧。运行时只能看到服务端的连接开闭日志,客户端的close和析构日志完全没输出。

客户端代码

#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
class  WebsocketClient {
public:
    WebsocketClient(const std::string& hostname, const std::string& port, const std::string& url)
        : m_socket(m_io_context)
    {

        boost::asio::ip::tcp::resolver resolver{ m_io_context };
        auto const results = resolver.resolve(hostname, port);
        auto ep = boost::asio::connect(m_socket.next_layer(), results);
        auto host = hostname + std::string(":") + std::to_string(ep.port());
        m_socket.set_option(boost::beast::websocket::stream_base::decorator(
            [](boost::beast::websocket::request_type& req) {
                req.set(boost::beast::http::field::user_agent,
                        std::string(BOOST_BEAST_VERSION_STRING) +
                        " websocket-client-coro");
            }));

        // Perform the websocket handshake
        m_socket.handshake(host, url);
    }

    std::string read() {
        boost::beast::flat_buffer buffer;
        m_socket.read(buffer);
        char* char_ptr = static_cast<char*>(buffer.data().data());
        return std::string(char_ptr, buffer.size());
    }

    void send(const std::string message) {
        m_socket.write(boost::asio::buffer(message));
    }

void close() {

        if (m_socket.is_open()) {
            m_socket.close(boost::beast::websocket::close_code::normal);
        }

        std::cout << "THIS IS NEVER PRINTED " << std::endl;

}

    ~WebsocketClient() {
        close();
        std::cout << "NEVER REACHED " << std::endl;
    }

private:
    boost::asio::io_context m_io_context;
    boost::beast::websocket::stream<boost::asio::ip::tcp::socket> m_socket;
};

服务端代码

int main() {
    crow::SimpleApp app;
    CROW_ROUTE(app, "/").websocket()
        .onaccept([](const crow::request& ) {
            return true;
        })
        .onopen([&](crow::websocket::connection& ) {
            std::cout << "CLIENT OPENED - Server "<< std::endl;
        })
        .onclose([&](crow::websocket::connection& , const std::string&) {
            std::cout << "Client CLOSED - Server "<< std::endl;
        });
    std::future<void> m_async_thread_server;

    m_async_thread_server = app
        .bindaddr("127.0.0.1")
        .signal_clear()
        .port(8080)
        .run_async();
     std::this_thread::sleep_for(std::chrono::milliseconds(100));
    auto client = WebsocketClient("127.0.0.1", "8080", "/");

  }

问题根源

Boost.Beast的websocket::stream::close()是同步操作,底层依赖IO上下文处理后续网络事件(比如接收服务端的关闭响应帧),但当前客户端类中的m_io_context从未被运行。当调用close()发送关闭帧后,客户端需要等待服务端的响应,但IO上下文未启动,无法处理接收到的响应帧,导致线程永久阻塞。

修复方案

1. 修改客户端类,添加IO上下文运行逻辑

在客户端中启动后台线程运行IO上下文,确保网络事件能被处理,同时在关闭时正确停止IO上下文并回收线程:

#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <thread>
#include <cerrno>

class WebsocketClient {
public:
    WebsocketClient(const std::string& hostname, const std::string& port, const std::string& url)
        : m_socket(m_io_context)
    {
        boost::asio::ip::tcp::resolver resolver{ m_io_context };
        auto const results = resolver.resolve(hostname, port);
        auto ep = boost::asio::connect(m_socket.next_layer(), results);
        auto host = hostname + std::string(":") + std::to_string(ep.port());
        m_socket.set_option(boost::beast::websocket::stream_base::decorator(
            [](boost::beast::websocket::request_type& req) {
                req.set(boost::beast::http::field::user_agent,
                        std::string(BOOST_BEAST_VERSION_STRING) +
                        " websocket-client-coro");
            }));

        m_socket.handshake(host, url);
        // 启动后台线程运行IO上下文,处理网络事件
        m_io_thread = std::thread([this]() { m_io_context.run(); });
    }

    std::string read() {
        boost::beast::flat_buffer buffer;
        m_socket.read(buffer);
        return std::string(static_cast<char*>(buffer.data().data()), buffer.size());
    }

    void send(const std::string message) {
        m_socket.write(boost::asio::buffer(message));
    }

    void close() {
        if (m_socket.is_open()) {
            try {
                m_socket.close(boost::beast::websocket::close_code::normal);
            } catch (const std::exception& e) {
                // 处理连接已断开等异常情况
                std::cerr << "Close error: " << e.what() << std::endl;
            }
        }

        // 停止IO上下文并等待线程结束
        m_io_context.stop();
        if (m_io_thread.joinable()) {
            m_io_thread.join();
        }

        std::cout << "THIS IS NOW PRINTED " << std::endl;
    }

    ~WebsocketClient() {
        close();
        std::cout << "NOW REACHED " << std::endl;
    }

private:
    boost::asio::io_context m_io_context;
    std::thread m_io_thread;
    boost::beast::websocket::stream<boost::asio::ip::tcp::socket> m_socket;
};

2. 关键说明

  • Boost.Asio的IO上下文必须被运行才能处理所有网络事件,包括同步操作背后的异步回调。
  • 后台线程运行m_io_context.run()后,客户端能正常接收服务端的关闭响应帧,避免close()阻塞。
  • 在close()中停止IO上下文并等待线程结束,确保资源被正确释放,避免内存泄漏。

内容的提问来源于stack exchange,提问作者ATK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 20:43:11