Boost.Beast SSL WebSocket服务器连接后报995:I/O操作中止
Boost.Asio/Beast SSL WebSocket服务器错误995(连接后立即断开)排查与解决
问题背景
使用Boost.Asio与Boost.Beast搭建带SSL的WebSocket服务器,客户端可成功建立连接但随即断开,报错:Read error: The I/O operation has been aborted because of either a thread exit or an application request (Code: 995)。服务器日志显示连接被正常接收并添加到ConnectionManager后触发该错误,已排查ConnectionManager跟踪正常、Ping保活启动、SSL上下文配置正确、监听端点正常。
1. 错误995的可能成因
- 连接实例生命周期提前终止:
SslWebSocket实例未被正确持有(比如用局部变量而非智能指针,或ConnectionManager的存储方式有误),导致异步async_read操作还在pending时实例被销毁,操作系统强制中止I/O操作。 - IO上下文提前停止:
io_context在异步操作完成前被调用stop(),或运行io_context的线程提前退出,所有未完成的异步操作都会被中止,触发995错误。 - strand缺失导致并发冲突:WebSocket对象的异步操作(接受、读、Ping)未绑定到同一strand,引发并发访问WebSocket内部状态的未定义行为,间接导致连接被中止。
- SSL握手未完成就启动读操作:日志显示"连接接受成功"仅代表TCP连接建立,SSL握手可能还在异步执行,此时提前调用
async_read会导致操作异常中止。 - Ping保活时机错误:在SSL握手完成前就发送Ping请求,导致连接被主动关闭,进而触发读操作的中止错误。
2. 线程/strand配置的解决办法
- 为每个连接绑定专属strand:所有与
SslWebSocket相关的异步操作(SSL握手、读、写、Ping)必须通过同一strand调度,确保串行执行,避免并发冲突。示例代码:// 在SslWebSocket类中初始化strand class SslWebSocket : public std::enable_shared_from_this<SslWebSocket> { public: explicit SslWebSocket(boost::asio::io_context& io_ctx, boost::asio::ssl::context& ssl_ctx) : ws_(io_ctx, ssl_ctx), strand_(io_ctx.get_executor()) {} void start_read() { // 通过strand调度读操作 boost::asio::dispatch(strand_, [self = shared_from_this()]() { self->ws_.async_read(self->buffer_, boost::beast::bind_front_handler(&SslWebSocket::on_read, self)); }); } private: boost::beast::ssl_stream<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>> ws_; boost::asio::strand<boost::asio::io_context::executor_type> strand_; boost::beast::flat_buffer buffer_; }; - 保证IO上下文线程池持续运行:在main函数中启动足够数量的线程运行
io_context,且服务器停止前不终止这些线程:int main() { boost::asio::io_context io_context; boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tlsv13_server); // 配置SSL上下文... WebSocketServer server(io_context, ssl_ctx, boost::asio::ip::tcp::endpoint{boost::asio::ip::make_address("0.0.0.0"), 8080}); server.run(); // 启动线程池运行io_context std::vector<std::thread> threads; for (size_t i = 0; i < std::thread::hardware_concurrency(); ++i) { threads.emplace_back([&io_context]() { io_context.run(); }); } // 等待服务器停止信号(比如捕获SIGINT) // ... // 停止io_context并等待线程结束 io_context.stop(); for (auto& t : threads) { if (t.joinable()) t.join(); } return 0; } - 用智能指针管理连接实例:ConnectionManager必须持有
SslWebSocket的std::shared_ptr,确保异步操作期间实例不会被销毁。例如:class ConnectionManager { public: void add(std::shared_ptr<SslWebSocket> client) { std::lock_guard<std::mutex> lock(mutex_); clients_.insert(std::move(client)); } private: std::mutex mutex_; std::unordered_set<std::shared_ptr<SslWebSocket>> clients_; }; - 严格遵循SSL握手流程:在
async_accept完成后,先执行异步SSL握手,仅在握手成功的回调中启动async_read和Ping保活机制:void do_accept() { acceptor_.async_accept( boost::asio::make_strand(io_context_), boost::beast::bind_front_handler( &WebSocketServer::on_accept, shared_from_this())); } void on_accept(boost::system::error_code ec, boost::asio::ip::tcp::socket socket) { if (!ec) { auto client = std::make_shared<SslWebSocket>(io_context_, ssl_ctx_); client->ws_.next_layer().swap(socket); // 启动异步SSL握手 client->ws_.async_handshake(boost::beast::websocket::role_type::server, "/", boost::beast::bind_front_handler( &WebSocketServer::on_handshake, shared_from_this(), client)); } do_accept(); // 继续接受新连接 } void on_handshake(boost::system::error_code ec, std::shared_ptr<SslWebSocket> client) { if (!ec) { connection_manager_.add(client); client->start_read(); // 握手成功后启动读操作 client->start_ping(); // 启动Ping保活 } }
内容的提问来源于stack exchange,提问作者sleepyhead
相关产品推荐
相关产品推荐

