You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

升级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;
        }
    });
}

关键说明

  1. auto [ec, results] = fut.get()拆分tuple,显式检查每个异步操作的错误码,出错时抛出system_error(和旧版本行为一致)。
  2. 把原代码的*results改成直接传resolve_resultsasync_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;
        }
    });
}

关键说明

  1. 所有异步操作的net::use_future都要替换成net::throw_if_error(net::use_future),这样fut.get()就会回到旧逻辑:成功返回结果,失败抛出system_error
  2. 同样建议把*results改成results,优化连接的鲁棒性。

为什么你之前试的结构化绑定没生效?

如果你之前试过auto [err,endpoints] = resolve_fut.get()但还是编译失败,大概率是这两个原因:

  1. 你的编译器没有开启C17或更高版本的支持(结构化绑定是C17特性),需要在编译选项中添加-std=c++17(GCC/Clang)或/std:c++17(MSVC)。
  2. 你只修改了resolve的部分,但后续的async_connectasync_handshake等操作的use_future也返回tuple,原代码中直接get()后没有处理,导致其他地方也存在编译错误(你没提到,但需要统一修改)。

火山引擎 最新活动