天天看點

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);