C++的Boost库:asio::io_context 替换掉asio::io_service的问题

新版 ASIO 必须以 asio::io_context 替换 asio::io_service

  • io_context -> io_service
  • io_context.post() -> io_context.get_executor().post()
  • io_context.dispatch() -> io_context.get_executor().dispatch()
  • io_context::strand -> strand<io_context::executor_type>

there were also changes to composed operation customization hooks - there are only 2 now - boost::asio::associated_allocator and boost::asio::associated_executor, which default to looking for get_allocator()get_executor()T::allocator_typeT::executor_type members of the composed operation function object.

简介

boost::asio::io_context看字面意思是io的上下文, 可以理解为, boost的任何io都会涉及到一个io_context, 同步io会隐式地启动一个io_context, 而异步io需要我们指定一个, 然后在合适的时机调用io_context::run()函数, 之后会进入io事件循环, 如果io完成会调用回调函数, 保证实现有关的功能. 根据官方文档, 在已经启动io_context::run()的情况下, 如果此时没有了io操作, 那么io_context会自动取消事件循环, 那么此时如果再有异步io回调, 也不会发生作用了. 参考下面的代码:

#include <iostream>
#include <boost/asio.hpp>

std::string raw_ip_addr = "127.0.0.1";
unsigned short port_num = 6768;

int main() {
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint
    ep(boost::asio::ip::address::from_string(raw_ip_addr), port_num);
    boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock1(ioc, ep.protocol());

    sock.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });
    ioc.run();

    // 这个函数也会失败, 但是此时不会其调用回调函数了, 因为run的事件循环已经结束了
    sock1.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    return 0;
}
/*
只会输出一个Connection refused消息, sock1的回调函数不会被调用
*/

但是有些情况下, 我们希望run()函数的事件循环在没有io事件的情况下, 也不会退出事件循环, 而是一直等待, 当有了新的异步io调用的时候, 还可以继续使用该循环.

放在上述的代码中, 意思就是sock1的异步连接的回调函数还可以使用

具体实现

需要借助boost::asio::io_context::work来实现, 这个类的核心左右就是防止io_context在没有io事件的情退出. 直接使用:

boost::asio::io_context::work(boost::asio::io_context & io_context);

的方式进行构造, 当work对象被销毁时, 它的作用就自动停止了.下面给出代码说明.

代码示例:

#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <chrono>

std::string raw_ip_addr = "127.0.0.1";
unsigned short port_num = 6768;

int main() {
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint
    ep(boost::asio::ip::address::from_string(raw_ip_addr), port_num);
    boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock1(ioc, ep.protocol());
    boost::asio::ip::tcp::socket sock2(ioc, ep.protocol());

    boost::asio::io_context::work worker(ioc);
    std::thread t([&ioc](){ioc.run();});

    std::cout << "Main thread will for 1 seconds...\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 这里是防止回调启动过快
    std::cout << "Main thread weak up...\n";

    sock.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    sock1.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    sock2.async_connect(ep, [](const boost::system::error_code & err) {
        if (err.value() != 0) {
            std::cout << err.message() << std::endl;
        }
    });

    std::cout << "Main thread will for 1 seconds...\n";  // 这里是为了放置stop()执行过快
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Main thread weak up...\n";
    ioc.stop();  // 先显式停止io_context, 否则无法终止
    t.join();
    return 0;
}

正常输出:

Main thread will for 1 seconds...
Main thread weak up...
Main thread will for 1 seconds...
Connection refused
Connection refused
Connection refused
Main thread weak up...

如果注释掉boost::asio::io_context::work worker(ioc);这一句, 那么会输出:

Main thread will for 1 seconds...
Main thread weak up...
Main thread will for 1 seconds...
Main thread weak up...

也就是说, 事件循环在没有IO事件的情况下, 就自动结束了.

总结

在不确定io异步事件回调发生的情况下, 如果想要io_context事件循环一直进行, 可以使用boost::asio::io_context::work worker(ioc);来执行, 需要开启一个新的线程, 专门执行IO事件即可, 具体参考上述代码.

 

<think>好的,用户遇到了一个关于Boost.Asio的编译错误,提示类“boost::asio::basic_stream_socket<...>”没有默认构造函数。我需要帮他分析这个问题。 首先,我应该回忆一下Boost.Asio中的basic_stream_socket类。这个类是用来处理流式套接字的,比如TCP套接字。通常,Boost.Asio的套接字类需要传入一个io_context或者executor来构造,因为它们需要与I/O服务关联。 用户提到的错误信息是缺少默认构造函数,这可能是因为在代码中尝试无参构造basic_stream_socket对象。例如,可能写了类似“boost::asio::ip::tcp::socket socket;”而没有传入io_context或者executor。旧版本的Boost.Asio可能允许这样的构造,但新版本可能需要显式传递executor。 接下来,我需要考虑Boost.Asio的版本变化。在较新的版本中,比如1.70之后,引入了executor的概念,取代了之前的io_context。因此,套接字构造函数可能需要一个executor参数,而不是io_context。如果用户升级了Boost版本,但没有修改旧代码,就可能出现这个问题。 另外,用户可能在模板中使用basic_stream_socket,并尝试默认构造它。因为basic_stream_socket的构造函数需要executor,所以必须显式传递。如果没有提供,就会导致编译错误。 可能的解决方案包括:在构造套接字时传入io_context或executor,检查Boost版本,查看文档确认构造函数的参数,或者确保所有依赖项正确。此外,如果用户使用了移动语义或延迟初始化,也需要正确管理构造过程。 需要提醒用户检查他们的代码中构造套接字的地方,确保传递了必要的参数。比如,使用io_context构造时应该是“boost::asio::ip::tcp::socket socket(io_context);”。如果使用executor,可能需要获取executor对象并传入。 最后,总结问题的根本原因,并给出具体的代码示例,帮助用户修改他们的代码。同时,建议查阅Boost官方文档,确认构造函数的正确用法,确保版本兼容性。</think>在 C++ 中使用 Boost.Asio 时遇到 "类不存在默认构造函数" 的错误,通常是因为没有正确初始化 `basic_stream_socket` 的依赖项。以下是分步解析和解决方案: --- ### 错误原因分析 `boost::asio::basic_stream_socket` 是 **需要显式构造** 的模板类,其构造函数必须关联 I/O 执行器(executor)。这个错误表明你尝试了以下非法操作: ```cpp // 错误示例:尝试无参构造 boost::asio::ip::tcp::socket socket; // ❌ 缺少必需的 executor 参数 ``` --- ### 核心机制说明 1. **依赖执行器 (Executor)** Boost.Asio (1.70+ 版本) 要求所有 I/O 对象必须绑定到执行器,用于管理异步操作的执行上下文。这是通过构造函数的 **必选参数** 实现的。 2. **模板参数解析** 你看到的模板实例化形式 `basic_stream_socket<boost::asio::ip::tcp, any_io_executor>` 表明: - 协议类型:`boost::asio::ip::tcp` - 执行器类型:`any_io_executor`(通用 I/O 执行器) --- ### 正确使用方式 #### 方法 1:通过 io_context 构造(传统方式) ```cpp boost::asio::io_context io_context; boost::asio::ip::tcp::socket socket(io_context); // ✅ 传入 io_context 隐式获取执行器 ``` #### 方法 2:显式传递执行器(现代方式) ```cpp boost::asio::io_context io_context; // 通过 get_executor() 获取执行器 boost::asio::ip::tcp::socket socket(io_context.get_executor()); // ✅ 显式传递执行器 ``` --- ### 常见修复场景 #### 场景 1:类成员初始化 ```cpp class MyClass { boost::asio::ip::tcp::socket socket_; public: // 必须通过初始化列表构造 MyClass(boost::asio::io_context& io_ctx) : socket_(io_ctx) {} // ✅ 正确初始化 }; ``` #### 场景 2:使用移动语义 ```cpp boost::asio::ip::tcp::socket create_socket(boost::asio::io_context& io_ctx) { return boost::asio::ip::tcp::socket(io_ctx); // ✅ 允许移动构造 } ``` --- ### 版本适配建议 - **Boost 1.70+**:必须显式传递执行器或 `io_context` - **Boost 1.69- **:旧版本允许默认构造,但建议升级时代码 --- ### 扩展验证 如果问题仍然存在,请检查: 1. 是否包含正确头文件:`#include <boost/asio.hpp>` 2. 是否使用兼容的 Boost 版本(通过 `BOOST_VERSION` 宏检查) 3. 确保没有误用 `basic_stream_socket` 的其他模板特化 建议参考官方文档:[Boost.Asio TCP Sockets](https://www.boost.org/doc/libs/master/doc/html/boost_asio/reference/ip__tcp/socket.html) 确认接口定义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值