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
相关产品推荐
相关产品推荐

