升级Boost 1.78至1.89后Boost.Beast异步连接编译失败,如何修正解析结果获取逻辑?
升级Boost 1.78至1.89后Boost.Beast异步连接编译失败,如何修正解析结果获取逻辑?
你遇到的问题是Boost.Asio在新版本中对net::use_future的行为做了关键调整:对于接受error_code+结果的异步操作,use_future不再默认在成功时返回结果、失败时抛出异常,而是统一返回包含error_code和操作结果的std::tuple。这直接导致你原来的代码把tuple当作resolver_results解引用时,出现编译错误。
下面给你两种清晰的修复方案,按需选择:
方案一:显式处理tuple(推荐,更灵活)
这种方式利用C++17的结构化绑定拆分tuple,显式检查错误码,同时还能优化你原代码中的一个小问题(手动取第一个端点):
修改后的核心代码片段
std::future<void> Websocket::async_connect() { io_thread_ = std::thread([this]() { ioc_.run(); }); // Start resolve auto resolve_fut = resolver_.async_resolve(host_, port_, net::use_future); return std::async(std::launch::async, [this, resolve_fut = std::move(resolve_fut)]() mutable { try { // 1. 拆分resolve返回的tuple,显式检查错误 auto [resolve_ec, resolve_results] = resolve_fut.get(); if (resolve_ec) { boost::throw_exception(boost::system::system_error{resolve_ec}); } // 2. 直接传入resolve_results(端点范围),Asio会自动尝试所有端点(比原代码的*results更鲁棒) auto connect_fut = beast::get_lowest_layer(ws_).async_connect(resolve_results, net::use_future); auto [connect_ec, _] = connect_fut.get(); if (connect_ec) { boost::throw_exception(boost::system::system_error{connect_ec}); } // 后续握手操作同理,显式处理tuple错误 auto ssl_handshake_fut = ws_.next_layer().async_handshake(ssl::stream_base::client, net::use_future); auto [ssl_ec, __] = ssl_handshake_fut.get(); if (ssl_ec) { boost::throw_exception(boost::system::system_error{ssl_ec}); } ws_.set_option(websocket::stream_base::timeout::suggested(beast::role_type::client)); auto ws_handshake_fut = ws_.async_handshake(host_, target_, net::use_future); auto [ws_ec, ___] = ws_handshake_fut.get(); if (ws_ec) { boost::throw_exception(boost::system::system_error{ws_ec}); } webSocketLogger_.info("Connected to WebSocket Successfully!!!"); ReceiveMsg(); } catch (const std::exception& e) { webSocketLogger_.error("Connection Failed: {}", e.what()); throw; } }); }
关键说明
- 用
auto [ec, results] = fut.get()拆分tuple,显式检查每个异步操作的错误码,出错时抛出system_error(和旧版本行为一致)。 - 把原代码的
*results改成直接传resolve_results给async_connect:Asio的async_connect本身支持遍历所有解析到的端点尝试连接,比手动取第一个端点的鲁棒性更强,避免因单个端点不可用直接连接失败。
方案二:恢复旧版use_future的抛出异常行为
如果你想最小化代码改动,完全回到Boost 1.78的逻辑(成功返回结果、失败自动抛异常),可以使用net::throw_if_error(net::use_future)替代原来的net::use_future:
修改后的核心代码片段
std::future<void> Websocket::async_connect() { io_thread_ = std::thread([this]() { ioc_.run(); }); // 用net::throw_if_error包裹use_future,恢复旧版抛出异常的行为 auto resolve_fut = resolver_.async_resolve(host_, port_, net::throw_if_error(net::use_future)); return std::async(std::launch::async, [this, resolve_fut = std::move(resolve_fut)]() mutable { try { // 回到原来的写法,get()直接返回resolver_results,失败自动抛异常 auto results = resolve_fut.get(); // 建议把*results改成results,让Asio自动遍历所有端点 auto connect_fut = beast::get_lowest_layer(ws_).async_connect(results, net::throw_if_error(net::use_future)); connect_fut.get(); auto ssl_handshake_fut = ws_.next_layer().async_handshake(ssl::stream_base::client, net::throw_if_error(net::use_future)); ssl_handshake_fut.get(); ws_.set_option(websocket::stream_base::timeout::suggested(beast::role_type::client)); auto ws_handshake_fut = ws_.async_handshake(host_, target_, net::throw_if_error(net::use_future)); ws_handshake_fut.get(); webSocketLogger_.info("Connected to WebSocket Successfully!!!"); ReceiveMsg(); } catch (const std::exception& e) { webSocketLogger_.error("Connection Failed: {}", e.what()); throw; } }); }
关键说明
- 所有异步操作的
net::use_future都要替换成net::throw_if_error(net::use_future),这样fut.get()就会回到旧逻辑:成功返回结果,失败抛出system_error。 - 同样建议把
*results改成results,优化连接的鲁棒性。
为什么你之前试的结构化绑定没生效?
如果你之前试过auto [err,endpoints] = resolve_fut.get()但还是编译失败,大概率是这两个原因:
- 你的编译器没有开启C17或更高版本的支持(结构化绑定是C17特性),需要在编译选项中添加
-std=c++17(GCC/Clang)或/std:c++17(MSVC)。 - 你只修改了resolve的部分,但后续的
async_connect、async_handshake等操作的use_future也返回tuple,原代码中直接get()后没有处理,导致其他地方也存在编译错误(你没提到,但需要统一修改)。




