天天看点

Boost::asio概览Boost::asio核心的概念和功能网络编程定时器信号处理机制

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概览Boost::asio核心的概念和功能网络编程定时器信号处理机制

在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模型图

Boost::asio概览Boost::asio核心的概念和功能网络编程定时器信号处理机制

异步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模式的示例图:

Boost::asio概览Boost::asio核心的概念和功能网络编程定时器信号处理机制
  • 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语言系统调用函数中, 主要有三种类型的定时模式, 分别是:

  • socket

    的超时机制,对应笔记: https://blog.csdn.net/qq_35976351/article/details/86530979
  • SIGALARM

    信号操作, https://blog.csdn.net/qq_35976351/article/details/86532889
  • IO复用函数的超时机制,比如

    epoll_wait

    函数, https://blog.csdn.net/qq_35976351/article/details/86535924

但是, 在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);