Boost::asio核心的概念和功能
asio
的最核心的功能是用于异步的IO通信, 比如通过文件 网络或者控制台等.
asio
提供了一系列的工具来处理这种长时间的IO操作, 而且执行这些操作不需要依赖线程和锁的模型.
笔记参考自官方文档: https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview.html
基础的结构
参考自: https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/core/basics.html
同步IO模型
程序执行同步IO的模型:

在boost.asio中,上述分别的对应接口如下:
io_service
表示程序与操作系统IO服务的链接,对应的boost接口
io_object
相当于一个IO对象, 比如socket的,这在boost中,有很多例子,比如
在类unix系统中, 操作失败都有对应的错误码,boost也是如此, 上述的同步链接中
boost::system::error_code ec;
socket.connect(server_endpoint, ec);
失败的话,
ec
会保存对应的错误码, 该函数会阻塞, 直到建立连接成功或者失败, 这也是同步IO的特点.
异步IO模型
异步IO模型图
异步IO最明显的一个特点是, 进行IO时,不需要阻塞等待返回, 而是可以先执行其它的任务, 然后去读取IO的内容.
程序可能的步骤是:
初始化IO对象
其中
your_completion_handler
的原型是
如果有参数, 可以是
bind
操作, 进行参数绑定操作.
操作系统执行IO的时候, 程序可以进行其它的操作, OS会把有关的结果放到就绪队列中, 等待
io_context
读取. 读取操作在
your_completion_handler
执行, 需要调用
io_context::run()
来启动.
Proactor设计模式
Linux中, 有个很经典的Reactor模型, 这是指有一个主线程负责监听IO事件, 然后把产生IO的文件描述符和对应的操作传递给一个线程中, 由线程池进行驱动. 因此, Reactor模式需要一个主线程和若干个线程.
而Proactor模式一般只需要一个线程即可. 先给出Proactor模式的示例图:
- Asynchronous Operation: 异步操作, 比如对socket的读写
- Asynchronous Operation Processor: 执行异步操作, 同时把读写就绪事件放到就绪队列中
- Completion Event Queue: 存储读写就绪事件
- Completion Handler: 用户自定义的处理IO的函数, 一般需要借助
操作bind
- Asynchronous Event Demultiplexer: 在Completion Event Queue上阻塞等待, 把就绪事件返回给它的调用者.
- Proactor: 调用Asynchronous Event Demultiplexer来使得就绪事件出队, 之后把就绪事件分配给相应的处理句柄, 这由io_service来完成.
- Initiator: 启动整个异步操作流程.
strand, 使用无锁的线程
官方文档: https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/core/strands.html
一个strand可以理解为: 一系列严格有序的操作, 通过strand操作, 可以让这一系列的操作在多线程中不显式地加锁执行.
strand
分为显式和隐式两种, 对应三种情况:
- 调用
函数, 这是隐式执行, 所有的操作序列都在一个线程中执行, 可定不用加锁io_context::run()
- 一组connect操作, 比如HTTP连接, 这是隐式操作
- 通过
或者strand<>
显式说明, 所用的事件处理的函数对象需要通过io_context:strand
绑定, 否则需要boost::asio::bind_executor()
对象进行分发操作.strand
很多时候, 我们面对的是异步操作的组合, 这些组合构成的操作通过一个
strand
. 那么, 我们需要保证跨线程时, 调用者和操作者处于不同线程时, 要有足够的安全性, 通过下面的函数获取
executor
, 比如:
Buffer和流的操作
因为IO操作的数据需要经过内存, 那么必然有buffer的概念, 具体可以查找手册进行处理. 流式的数据, 也需要借助有关的流操作实现, 具体可以参考手册.
Reator模式的操作
借助第三方库等完成操作.
https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/core/reactor.html
Line-Based数据模式操作
相当于是数据流的结束符, 因为很多是面向流的数据.
https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/core/line_based.html
调试追踪异步操作
主要是为了异步调试方便, 具体参考手册:
https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/core/handler_tracking.html
关于协程
可以理解为一个更加轻量级的线程, 具体操作手册:
- https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/core/concurrency_hint.html
- https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/core/coroutine.html
- https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/core/spawn.html
网络编程
-
支持TCP UDP和ICMP通信
https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/networking/protocols.html
- 其他协议 https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/networking/other_protocols.html
- BSD内核与boost对应的函数: https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/networking/bsd_sockets.html
定时器
对于长时间的IO操作, 定时器是至关重要的, Linux的C语言系统调用函数中, 主要有三种类型的定时模式, 分别是:
-
的超时机制,对应笔记: https://blog.csdn.net/qq_35976351/article/details/86530979socket
-
信号操作, https://blog.csdn.net/qq_35976351/article/details/86532889SIGALARM
- IO复用函数的超时机制,比如
函数, https://blog.csdn.net/qq_35976351/article/details/86535924epoll_wait
但是, 在boost::asio中, 有与异步IO更匹配的定时器机制. 直接参考文档:
https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/timers.html
一般的使用方式为:
void handler(boost::system::error_code ec) { ... }
// do something here
io_context i;
// do something here
deadline_timer t(i);
t.expires_from_now(boost::posix_time::milliseconds(400));
t.async_wait(handler);
// do something here
i.run();
信号处理机制
先回顾Linux的信号处理机制: https://blog.csdn.net/qq_35976351/article/details/85524540
给出一个一般的解决方案, 参考自: https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/overview/signals.html
void handler(
const boost::system::error_code& error,
int signal_number)
{
if (!error)
{
// A signal occurred.
}
}
// Construct a signal set registered for process termination.
boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
// Start an asynchronous wait for one of the signals to occur.
signals.async_wait(handler);