cpp

Boost.Asio 简介

Boost.Asio 简介

Posted by vxiaozhi on April 8, 2025

概述

Boost.Asio是一个跨平台的、主要用于网络和其他一些底层输入/输出编程的C++库。

Asio,即「异步 IO」(Asynchronous Input/Output),本是一个 独立的 C++ 网络程序库,似乎并不为人所知,后来因为被 Boost 相中,才声名鹊起。

从设计上来看,Asio 相似且重度依赖于 Boost,与 thread、bind、smart pointers 等结合时,体验顺滑。从使用上来看,依然是重组合而轻继承,一贯的 C++ 标准库风格。

Boost.Asio代码风格

Asio为了可读性将部分较复杂的类的声明和实现分成了两个头文件,在声明的头文件末尾include负责实现的头文件。impl文件夹包含这些实现的头文件。另外,还有一个常见的关键词是detail,不同操作系统下的各种具体的代码会放在detail文件夹下。

例如: shceduler.hpp 包含: scheduler.ipp

1
2
3
4
5
6
7
8
9
...
#include <boost/asio/detail/pop_options.hpp>

#if defined(BOOST_ASIO_HEADER_ONLY)
# include <boost/asio/detail/impl/scheduler.ipp>
#endif // defined(BOOST_ASIO_HEADER_ONLY)

#endif // BOOST_ASIO_DETAIL_SCHEDULER_HPP

关键类

io_context/io_service

Boost.Asio中最核心的类——io_service。io_service是这个库里面最重要的类;它负责和操作系统打交道,等待所有异步操作的结束,然后为每一个异步操作调用其完成处理程序。

每个 Asio 程序都至少有一个 io_context 对象,它代表了操作系统的 I/O 服务(io_context 在 Boost 1.66 之前一直叫 io_service),把你的程序和这些服务链接起来。

io_service 其实就是 io_context, 其存在只是为了向后兼容:

1
2
3
4
#if !defined(BOOST_ASIO_NO_DEPRECATED)
/// Typedef for backwards compatibility.
typedef io_context io_service;
#endif // !defined(BOOST_ASIO_NO_DEPRECATED)

io_service:io_service是线程安全的。几个线程可以同时调用io_service::run()。大多数情况下你可能在一个单线程函数中调用io_service::run(),这个函数必须等待所有异步操作完成之后才能继续执行。然而,事实上你可以在多个线程中调用io_service::run()。这会阻塞所有调用io_service::run()的线程。只要当中任何一个线程调用了io_service::run(),所有的回调都会同时被调用;这也就意味着,当你在一个线程中调用io_service::run()时,所有的回调都被调用了。

Timer

有了 io_context 还不足以完成 I/O 操作,用户一般也不跟 io_context 直接交互。

根据 I/O 操作的不同,Asio 提供了不同的 I/O 对象,比如 timer(定时器),socket,等等。 Timer 是最简单的一种 I/O 对象,可以用来实现异步调用的超时机制,下面是最简单的用法:

1
2
3
4
5
6
7
8
9
10
11
void Print(boost::system::error_code ec) {
  std::cout << "Hello, world!" << std::endl;
}

int main() {
  boost::asio::io_context ioc;
  boost::asio::steady_timer timer(ioc, std::chrono::seconds(3));
  timer.async_wait(&Print);
  ioc.run();
  return 0;
}

Socket

Socket 也是一种 I/O 对象,这一点前面已经提及。相比于 timer,socket 更为常用,毕竟 Asio 是一个网络程序库。

但socket:socket类不是线程安全的。所以,你要避免在某个线程里读一个socket时,同时在另外一个线程里面对其进行写入操作。(通常来说这种操作都是不推荐的,更别说Boost.Asio)。

异步run(), runone(), poll(), poll one()

为了实现监听循环,io_service类提供了4个方法,比如:run(), run_one(), poll()和poll_one()。虽然大多数时候使用service.run()就可以,但是你还是需要在这里学习其他方法实现的功能。

  • run()会一直执行,直到你手动调用io_service::stop()。
  • run_one()方法最多执行和分发一个异步操作:如果没有等待的操作,方法立即返回0,如果有等待操作,方法在第一个操作执行之前处于阻塞状态,然后返回1
  • poll_one()方法以非阻塞的方式最多运行一个准备好的等待操作:如果至少有一个等待的操作,而且准备好以非阻塞的方式运行,poll_one方法会运行它并且返回1; 否则,方法立即返回0
  • poll()方法会以非阻塞的方式运行所有等待的操作。

多线程

有两种思路:

思路 1:每个线程一个 I/O Context[

在多线程的场景下,每个线程都持有一个 io_context ,并且每个线程都调用各自的 io_context 的run()方法。

特点:

  • 在多核的机器上,这种方案可以充分利用多个 CPU 核心。
  • 某个 socket 描述符并不会在多个线程之间共享,所以不需要引入同步机制。
  • 在 event handler 中不能执行阻塞的操作,否则将会阻塞掉 io_context 所在的线程。

思路 2:多个线程共享一个 I/O Context

全局只分配一个io_context ,并且让这个 io_context 在多个线程之间共享,每个线程都调用全局的 io_context 的run()方法。

先分配一个全局 io_context,然后开启多个线程,每个线程都调用这个 io_context的run()方法。这样,当某个异步事件完成时,io_context 就会将相应的 event handler 交给任意一个线程去执行。

然而这种方案在实际使用中,需要注意一些问题:

  • 在 event handler 中允许执行阻塞的操作 (例如数据库查询操作)。
  • 线程数可以大于 CPU 核心数,譬如说,如果需要在 event handler 中执行阻塞的操作,为了提高程序的响应速度,这时就需要提高线程的数目。
  • 由于多个线程同时运行事件循环(event loop),所以会导致一个问题:即一个 socket 描述符可能会在多个线程之间共享,容易出现竞态条件 (race condition)。譬如说,如果某个 socket 的可读事件很快发生了两次,那么就会出现两个线程同时读同一个 socket 的问题 (可以使用strand解决这个问题)。
  • 无锁的同步方式:Asio 提供了 io_context::strand:如果多个 event handler 通过同一个 strand 对象分发 (dispatch),那么这些 event handler 就会保证顺序地执行。

调试实践

捕获线程创建时机:

1
break pthread_create
1
2
3
4
5
6
7
8
9
10
- websocketpp::client<websocketpp::config::asio_client>::connect
    - websocketpp::transport::asio::endpoint<websocketpp::config::asio_client::transport_config>::async_connect
        - boost::asio::ip::basic_resolver
            - boost::asio::async_result<boost::asio::detail::wrapped_handler
                - boost::asio::ip::basic_resolver<boost::asio::ip::tcp, boost::asio::execution::any_executor
                    - boost::asio::detail::resolver_service<boost::asio::ip::tcp>::async_resolve
                        - boost::asio::detail::resolver_service_base::start_resolve_op
                            - boost::asio::detail::resolver_service_base::start_work_thread
                                - boost::asio::detail::posix_thread::posix_thread<boost::asio::detail::resolver_service_base::work_scheduler_runner>
                                    - boost::asio::detail::posix_thread::start_thread

查看 scheduler 创建:

1
break boost::asio::detail::scheduler::scheduler

基于 boost.asio 的应用

参考