Beast SSL握手阶段调用async_shutdown触发断言失败,如何正确关闭流?
解决Beast HTTPS客户端握手期间关闭流触发断言的问题
这个断言错误的核心原因是:Beast的流对象不允许同时存在多个未完成的异步操作。当async_handshake还在执行时调用async_shutdown,就会触发这个检查逻辑,导致断言失败。
以下是几种可行的解决方式:
1. 先取消握手操作,再执行关闭
直接调用流的cancel()方法取消正在进行的async_handshake,该操作会让握手的完成处理函数以boost::asio::error::operation_aborted错误被唤醒。在处理函数中捕获这个错误后,再执行async_shutdown:
// 触发取消操作 stream.cancel(); // 握手完成的处理函数 void on_handshake(boost::system::error_code ec) { if (ec == boost::asio::error::operation_aborted) { // 取消后执行关闭 stream.async_shutdown([](boost::system::error_code ec) { // 处理关闭后的逻辑,忽略SSL层的一些常见错误 if (ec && ec != boost::asio::error::eof) { // 记录错误 } // 最后关闭底层socket stream.next_layer().close(); }); } else if (ec) { // 处理握手失败的其他情况 stream.next_layer().close(); } }
2. 用strand序列化所有流操作
使用boost::asio::io_context::strand确保所有对Beast流的异步操作(握手、关闭、读写)都串行执行,从根源上避免并发操作冲突:
boost::asio::io_context io_ctx; boost::asio::io_context::strand strand(io_ctx); // 提交握手操作 strand.post([&]() { stream.async_handshake(boost::beast::ssl::stream_base::client, strand.wrap(&on_handshake)); }); // 需要关闭时,通过strand提交关闭逻辑 strand.post([&]() { if (stream.next_layer().is_open()) { // 如果握手还在进行,先取消 stream.cancel(); } else { stream.async_shutdown(strand.wrap(&on_shutdown)); } });
3. 用状态标记延迟关闭操作
设置一个布尔标志(比如should_shutdown),当需要关闭时先标记该状态,等握手操作完成(无论成功或失败)后,再根据标志执行关闭:
std::atomic<bool> should_shutdown = false; // 触发关闭请求时设置标志 should_shutdown = true; // 握手完成处理函数 void on_handshake(boost::system::error_code ec) { if (should_shutdown) { stream.async_shutdown([](boost::system::error_code ec) { // 处理关闭逻辑 stream.next_layer().close(); }); return; } // 正常握手后的逻辑 }
注意事项
- 调用
cancel()后,要确保握手的处理函数能正确处理operation_aborted错误,避免遗漏关闭逻辑 - SSL流关闭时可能会返回
eof错误,这属于正常情况,可以忽略该错误 - 所有涉及流的异步操作必须保证序列化,避免并发调用导致的未定义行为
内容的提问来源于stack exchange,提问作者Кирилл Волков
相关产品推荐
相关产品推荐

