boost asio 应用方法学(二)——深入框架

2023-05-16

要用好它,就必须先了解它,而且不能停止于表面,必须深入到内部。而了解一件事物,先要了解它的框架,再了解它的细节。了解了框架,我们就有了提纲挈领的认识。

关于 boost asio 框架结构,在其文档中,用了这样一张图来描述:

 

proactor

简单解释一下:

这里由使用者(Initiator)启动一个异步操作(Asynchronous Operation),在启动异步的同时它要负责创建一个异步回调对象(Completion Handler),然后该异步操作被交给了异步操作执行者(Asynchronous Operation Processor),由它负责执行异步操作,并在完成后将一个完成事件插入完成事件队列(Completion Event Queue);另一方面,前摄器(Proactor,这个词很难准确翻译,也有翻译为主动器,可能借义于proactive)驱动异步事件分派器(Asynchronous Event Demultiplexer)从完成事件队列中获取事件,这是一个阻塞的过程,一旦获取到完成事件,前摄器从事件上找出与该事件关联的回调对象,并执行回调。

这是一个标准的前摄器模式,这个模式是在 ACE 网络库中使用的概念。关于该模式的研究很多,搜索一下 ACE Proactor 就可以找到很多资料。上面的描述也比较容易理解,唯一比较难搞懂的是异步事件分派器(Asynchronous Event Demultiplexer),好像它的存在并不起多大作用,其实它的作用大着呢,特别是在多线程中,它要保证异步完成事件的及时分派,提高多线程并发度,以及降低线程切换开销。在 windows 完成端口的文档中有这方面的机制介绍。

总得来说,这是一个概念性的模型,仅用这个模型来描述 boost asio,根本体现不了 boost asio 的优点。即使从使用者的角度,仅掌握这样的模型也是不够,boost asio 还有很多值得学习借鉴的地方。

我们需要结合这个模型来深入 boost asio 的实现框架。

 boost asio 是如何实现前摄器模式的呢?我们使用 boost asio 第一步都需要创建一个 boost::asio::io_service,我们就从 io_service 开始开启我们的探秘之旅。

 io_service 类的定义很简单,总共就三个成员变量:

#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
  detail::winsock_init<> init_;
#elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \
  || defined(__osf__)
  detail::signal_init<> init_;
#endif

  // The service registry.
  boost::asio::detail::service_registry* service_registry_;

  // The implementation.
  impl_type& impl_;


其实简单反而意味着强大,因为这表明 boost asio 已经把功能结构划分的很清晰了。

三个成员变量中的 init_ 与结构没有太大关系,windows 平台的 winsock_init 里面调用了 WSAStartup,linux 平台的 signal_init 里面屏蔽了 SIG_PIPE 信号。这两个东东几乎是在我们刚开始接触网络编程就遇到的知识点。

其次我们再来看看 impl_ 成员,从名字上看应该是 io_service 的实现类,确实 io_service 的很多接口是直接转发给了 impl_ 成员,比如 run、poll、stop、reset、post、dispatch

inline std::size_t io_service::run(boost::system::error_code& ec)
{
  return impl_.run(ec);
}
inline std::size_t io_service::poll(boost::system::error_code& ec)
{
  return impl_.poll(ec);
}
inline void io_service::stop()
{
  impl_.stop();
}

inline void io_service::reset()
{
  impl_.reset();
}

template <typename Handler>
inline void io_service::dispatch(Handler handler)
{
  impl_.dispatch(handler);
}

template <typename Handler>
inline void io_service::post(Handler handler)
{
  impl_.post(handler);
}

但是 impl_type 是什么呢?它的定义如下:

#if defined(BOOST_ASIO_HAS_IOCP)
  typedef detail::win_iocp_io_service impl_type;
#elif defined(BOOST_ASIO_HAS_EPOLL)
  typedef detail::task_io_service<detail::epoll_reactor<false> > impl_type;
#elif defined(BOOST_ASIO_HAS_KQUEUE)
  typedef detail::task_io_service<detail::kqueue_reactor<false> > impl_type;
#elif defined(BOOST_ASIO_HAS_DEV_POLL)
  typedef detail::task_io_service<detail::dev_poll_reactor<false> > impl_type;
#else
  typedef detail::task_io_service<detail::select_reactor<false> > impl_type;
#endif


原来是根据不同的平台支持特性,选择了不同的实现,要把这么多种实现融合起来,没有一个很好的架构是很难做到的。

我们再来看看 service_registry_,对这个成员变量的使用体现在 use_service、add_service、has_service 三个函数中:

template <typename Service>
inline Service& use_service(io_service& ios)
{
  // Check that Service meets the necessary type requirements.
  (void)static_cast<io_service::service*>(static_cast<Service*>(0));
  (void)static_cast<const io_service::id*>(&Service::id);

  return ios.service_registry_->template use_service<Service>();
}

template <typename Service>
void add_service(io_service& ios, Service* svc)
{
  // Check that Service meets the necessary type requirements.
  (void)static_cast<io_service::service*>(static_cast<Service*>(0));
  (void)static_cast<const io_service::id*>(&Service::id);

  if (&ios != &svc->io_service())
    boost::throw_exception(invalid_service_owner());
  if (!ios.service_registry_->template add_service<Service>(svc))
    boost::throw_exception(service_already_exists());
}

template <typename Service>
bool has_service(io_service& ios)
{
  // Check that Service meets the necessary type requirements.
  (void)static_cast<io_service::service*>(static_cast<Service*>(0));
  (void)static_cast<const io_service::id*>(&Service::id);

  return ios.service_registry_->template has_service<Service>();
}


看起来是个集合容器,里面的元素是服务(Service),服务有编号(id)。从 service_registry 的实现可以进一步了解到下面细节:

  1. 服务都是用一个类来实现的,唯一的接口要求是必须有 shutdown_service 接口
  2. 服务是延迟创建的,只有第一次被使用的时候才创建
  3. 每种类型的服务在同一个 service_registry (也是同一个io_service)里面最多只有一个实例
  4. 当service_registry (也就是io_service)被销毁(析构)时,服务会被 shutdown ,然后被销毁

 继续分析每个服务,还可以看到服务有下列特性:

  1. 服务在构造后就开始运作
  2. 服务在 shutdown 后停止运作
  3. 服务通过句柄(implementation_type)对外暴露其功能
  4. 服务之间有依赖性

最后,总结出所有的服务大概分为三类:

第一类服务是底层系统服务,是对操作系统平台提供功能的封装,有:

  • detail::win_iocp_io_service
  • detail::win_iocp_socket_service
  • detail:: task_io_service
  • detail::reactive_socket_service
  • detail::epoll_reactor
  • detail::kqueue_reactor
  • detail::dev_poll_reactor
  • detail::select_reactor
  • detail::resolver_service

中间四个都是 reactor,不能想象,所有的 reactor 应该有相同的对外服务接口。这里的 task_io_service 和 reactive_socket_service 是对 reactor 的再封装,所以上层的服务不会直接依赖 reactor,而是依赖 task_io_service 和 reactive_socket_service。

第二类服务是上层接口服务,面向具体的功能对象(Object),他们会针对运行平台选择依赖对应的底层服务

  • socket_acceptor_service(ip::basic_socket_acceptor -> ip::tcp::acceptor)
  • stream_socket_service(ip::basic_stream_socket -> ip::tcp::socket)
  • datagram_socket_service(ip::basic_datagram_socket -> ip::udp::socket)
  • raw_socket_service(ip::basic_raw_socket -> ip::icmp::socket)
  • deadline_timer_service(basic_deadline_timer -> deadline_timer)
    • detail::deadline_timer_service
  • ip::resolver_service(ip::basic_resolver -> ip::tcp::resolver, ip::udp::resolver, ip::icmp::resolver)

 前四个 socket 相关的服务会在不同的平台,选择依赖 win_iocp_socket_service 和 reactive_socket_service<xxx_reactor>,deadline_timer_service (具体实现在

detail::deadline_timer_service中)会选择 win_iocp_io_service 和 task_io_service<xxx_reactor>,ip::resolver_service 没得选择,只有依赖 detail::resolver_service。

第三类服务是一些特殊功能的服务,比如 detail::strand_service 等,他们对整体框架没有影响。

虽然 boost asio 中提供了这么多服务,但是上层应用并不会直接使用这些服务,服务通过句柄对外暴露其功能,而句柄被功能对象(Object)封装,然后提供给上层应用使用。

这里的功能对象(Object),就是我们在第二类服务后面的“()”里面给出的类,每个对象都包含着一个对相应服务的C++引用,以及服务对外暴露的句柄。

至此,我们了解了 boost asio 中通过一系列的服务封装了操作系统的底层功能,并且通过动态组装的方式把这些服务管理起来。可以看出,boost asio 使用了一种相当简单的方式,就解决了平台的多样性,甚至于模式的多样性;同时服务的动态加载和集中管理,为功能扩展提供了方便途径;另外对象中只包含句柄,提高了安全性。

再回过头来看看,我们心中还有个疑问,那就是 boost asio 是怎么实现前摄器(Proactor)模式的呢?其实前摄器(Proactor)的各个角色都是通过服务来表达的。

先看 windows 平台

Asynchronous Operation Processor 的功能是由 win_iocp_socket_service、resolver_service 完成,Proactor 功能由 win_iocp_io_service 完成,win_iocp_io_service 也包含 Asynchronous Event Demultiplexer 和 Completion Event Queue 的功能,不过其实是对 windows IOCP 的系统功能的封装。

再看看 linux 平台

Asynchronous Operation Processor 的功能是由 reactive_socket_service、resolver_service 完成,Proactor、Asynchronous Event Demultiplexer 和 Completion Event Queue功能都是由 task_io_service 完成。

最后,io_service 其实代表了Proator 这一端,socket、timer、resolver 等等代表了 Initiator 这一端。这就是上层使用者角度看到的景象。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

boost asio 应用方法学(二)——深入框架 的相关文章

  • Qt Creator使用CMake配置第三方库

    语法 include directories 添加第三方库头文件路径 这里的 include directories 直接向括号里加入第三方库的头文件路径即可 span class token function include direct
  • QtGlobal中常见的一些函数和宏

    lt QtGlobal gt 头文件包含了 Qt 类库的一些全局定义 xff0c 包括基本数据类型 函数和宏 xff0c 一般的 Qt 类的头文件都会包含该文件 xff0c 所以不用显式包含这个头文件也可以使用其中的定义 全局变量定义 为了
  • 传感器之激光雷达简介与使用

    激光雷达是现今机器人尤其是无人车领域及最重要 最关键也是最常见的传感器之一 xff0c 是机器人感知外界的一种重要手段 概念 激光雷达 LiDAR xff0c 英文全称为 Light Detection And Ranging xff0c
  • sci_loopback_int的例程(中断程序)

    例程代码如下 xff1a include 34 DSP28x Project h 34 Device Headerfile and Examples Include File define CPU FREQ 40E6 Default 61
  • python中使用subprocess.Popen中的返回值总结:

    usr bin python coding UTF 8 import sys import subprocess import traceback author by zhangheng timestamp 2018 06 08 gennl
  • SPI工作模式

    1 SPI总线条数 MISO xff1a 主设备输入 从设备输出引脚 该引脚在从模式下发送数据 xff0c 在主模式下接收数据 MOSI xff1a 主设备输出 从设备输入引脚 该引脚在主模式下发送数据 xff0c 在从模式下接收数据 SC
  • 游戏常用算法:四种迷宫生成算法

    简介 所谓迷宫生成算法 xff0c 就是用以生成随机的迷宫的算法 迷宫生成算法是处于这样一个场景 xff1a 一个row行 xff0c col列的网格地图 xff0c 一开始默认所有网格四周的墙是封闭的 要求在网格地图边缘 xff0c 也就
  • OPEN alliance工作小组

    Open Alliance TC 8小组 TC 8 xff1a 汽车以太网ECU测试规范 TC 8分配了汽车以太网ECU测试规范 它根据这些共享要求定义了适用于汽车以太网网络中所有ECU的规范 TC8定义了测试流程和支持建立能够执行ECU测
  • 测试PCB线路的阻抗的方法

    1 TDR测试 TDR是利用短脉冲信号发送到测试信号线上 xff0c 当信号到达另一端或者遇到不匹配点的时候就会发生反射回来 通过测量反射信号的时间和特征来判断线路的阻抗和不匹配点的位置 TDR测试需要专业的测试设备 xff0c 如时域反射
  • 开关电源的特性阻抗

    一 开关电源的特性阻抗好坏可以用以下几个量化指标来评估 xff1a 1 交流阻抗 xff08 AC Impedance xff09 xff1a 交流阻抗是指开关电源在交流信号下的电阻 电感和电容等电学特性 交流阻抗的好坏直接影响开关电源的驱
  • 学网络比不可少的网络协议分析神器-wireshark

    Wireshark是一款网络协议分析器 xff0c 可以用于捕获和分析网络数据包 xff0c 以便深入了解网络通信的细节和性能 xff0c 同时也可以用于网络安全分析和故障排除 Wireshark的主要功能包括 xff1a 1 捕获网络数据
  • C语言return的用法详解,C语言函数返回值详解

    C语言return的用法详解 xff0c C语言函数返回值详解 函数的返回值是指函数被调用之后 xff0c 执行函数体中的代码所得到的结果 xff0c 这个结果通过 return 语句返回 return 语句的一般形式为 xff1a spa
  • 网络编程——多线程编程

    文章目录 目的内容源代码及结果 1 Linux下的线程同步 1 1 编程使用互斥量实现线程同步 xff1b 1 2 编程使用信号量实现线程同步 xff0c 要求实现以下功能 xff1a 线程A从用户输入得到值后存入全局变量num xff0c
  • ARM-MPU内存保护单元详解

    ARM MPU 详解 简介 MPU Memory Protection Unit 内存保护单元 本文主要讲 armv7 m 架构 架构下的 MPU 在 armv7 m 架构下 xff0c Cortex M3 和 Cortex M4 处理器对
  • 玩转doxygen 之RT-THREAD

    玩转doxygen 之RT THREAD 文章目标 经常会看到小伙伴们遇到怎么写函数注释头疼 xff0c 以及如何生成漂亮的代码注释文档头疼 据我了解 xff0c 目前C语言中的代码注释规则有且只有一种比较常用 xff0c 就是doxyge
  • STM32如何将文件放到内部flash里面

    STM32如何将文件放到内部flash里面 背景介绍 上一篇讲到如何将STM32的FLASH改成文件系统 xff1a 如何不用外设在STM32片上FLASH做一个文件系统 https club rt thread org ask artic
  • 营运型手游开发、测试、正式的三阶段开发架构

    在手机游戏的畅销排行榜上 xff0c 可以看到大多数的游戏都是营运型的游戏 所谓的营运型游戏 xff0c 指的是游戏的开发并不是上架后就结束 xff0c 而是需要持续的配合游戏营运的需求 xff0c 进行游戏的更新 内容调整以及后续内容的开
  • 【github】【action】如何给软件包添加CI集成

    github action 如何给软件包添加CI集成 简介 github有自己的CI集成工具 action 很少有小伙伴关注到 xff0c 如果你有自己的软件包 xff0c 想要对其进行维护的话 xff0c 添加CI集成能够方便你快速验证你
  • Access 标准表达式中数据类型不匹配

    Access 标准表达式中数据类型不匹配 Access标准表达式中数据类型不匹配 今天在做一个小程序时 要求用到Access数据库 在调试运行一个SELECT语句时 老是提示标准表达式中数据类型不匹配 弄了好久 原来发现是数据类型不匹配的问
  • c#中new一个对象以后,是否需要手动释放?

    c 中new一个对象以后 xff0c 是否需要手动释放 xff1f 2012 04 28 23 43 wshbfzdzb 分类 xff1a C NET 浏览723次 c 43 43 中 class1 a 61 new class1 需要在用

随机推荐

  • ARM M0+各种定时器驱动的编写

    systick 系统滴答时间 这个定时器之前的文章已经讲过 这个是一个递减的定时器 xff0c 有个模数寄存器 在此不多说 就是一个系统的模块 xff0c 这个模块是集成在ARM M0 43 内核中的 xff0c 其实主要是集成在NVIC
  • MG323所有命令使用

    AT 43 CGMR 61 OK AT 43 GMR 61 OK AT 43 GMR 12 210 10 05 00 OK AT 43 CGSN 351869042318140 OK AT 43 CIMI 460021734971641 O
  • BAT文件的常用语法

    bat文件中常用的命令有 xff1a echo 64 rem pause goto call if copy等 下面简要给出这几个命令的用法 1 echo命令 echo 表示显示此命令后的字符 例如echoHello World choHe
  • c++ http请求,json解析

    一 文章内容 解决c 43 43 http请求以及对返回结果json串进行解析 xff0c 使用jsoncpp库 二 安装jsoncpp插件 vs2015通过NuGet直接安装jsoncpp到项目下 安装好之后 xff0c 会在项目下有个p
  • Linux安装Oracle12c操作手册

    1 基本环境 服务器 xff1a 64位 16核CPU 384G内存 16T硬盘 操作系统 xff1a CentOS 7 4 Oracle版本 xff1a 12c 版本号12 1 0 2 0 2 安装必要的软件包 查看rpm包是否安装 xf
  • tiny6410按键驱动总结

    写了7个版本的按键驱动 xff1a 1 查询法 xff1a 在应用程序的while循环里不停的调用read函数读取按键值 xff0c 太耗费CPU资源了 2 中断发 xff1a 同样是在一个while循环里不停的调用read函数读按键值 x
  • linux中shell的常用命令

    shell 常用命令 什么是shell xff1f shell 也是操作系统中的一个软件 xff0c 它包在 linux 内核的外面 为用户和内核之间的交互提供了一个接口 一 diff命令 diff b表示忽略空格 xff0c B表示忽略空
  • 空心杯电机学习笔记

    空心杯电机学习笔记 1 空心杯电机 xff08 直流电机 xff09 的硬核拆解2 空心杯电机的驱动模块学习 xff08 1 xff09 无人机飞控原理学习的流程介绍 xff08 空心杯四旋翼DIY xff09 xff08 2 xff09
  • C++ 中“空引用”与“空指针”的区别

    网络上有很多讨论C 43 43 的 引用 与 指针 的区别的文章 xff0c 谈到区别 xff0c 其中有一条 xff1a 引用不能为空 xff08 NULL xff09 xff0c 引用必须与合法的存储单元关联 xff0c 指针则可以是N
  • 关于 std::vector 的下标越界检查

    当要获取 std vector 的第 n 个元素 xff0c 下面几种方式都可以 xff1a std vector lt int gt vec size t n 61 1 int amp i 61 vec n int amp j 61 ve
  • 【第三篇】 基于 Qt 的 REST 网络框架

    本文是 Qt 框架性开发实践 基础框架篇 的第三篇 本文所讲的内容已经开源 xff0c 你可以在 这里 找到源代码 在 Java 以及其他语言中 xff0c 处理与后端的 HTTP 通讯 xff0c 有专门的工具库 xff0c 使用起来特别
  • Qt/QML 实现图片圆角剪切效果

    在很多 UI 设计中 xff0c 需要将图片按照一定的方式整形 比如下面的 VIP 图片就是用一个圆形剪切原始图片 xff0c 形成的效果 其实它的原始图片是这样的 xff1a 要在 QML 中实现这样的效果 xff0c 可以使用 Opac
  • 实现 DirectShow 虚拟 Camera 驱动

    今天我们要实现一个虚拟 Camera 驱动 有这个驱动 xff0c 在 播放软件 xff08 如 VLC xff09 视频会议软件 主播视频制作软件 xff08 如 OBS xff09 中 xff0c 就可以播放 加入我们的各种特制内容了
  • 通过 ffmpeg 串流对接 OBS 等直播软件

    我们要将设备通过私有通道输出到 H264 流 xff0c 传给 OBS 等直播软件使用 为此 xff0c 设计了上图所示的串流工具 设计思路 私有通道通过 API 接口提供 H264 流 xff0c 要传给 ffmpeg xff0c 最简单
  • 排查 Edge WebView2 在某个设备上不出图像的问题

    我们在 Windows 应用内嵌入 Edge WebView2 xff0c 来展示部分用网页实现的界面 总得来说还是不错的 xff0c 比如 xff1a 渲染很快 xff0c 基本上内置网页100毫秒以内控件样式很清爽 xff0c 没有多余
  • 一个 Qml MenuBar 的问题

    基本情况 使用 QQuick Control 中的 MenuBar 实现主菜单栏 菜单栏包括 File Edit View Help 菜单项 点击菜单项 xff0c 会弹出对应的菜单 ApplicationWindow id window
  • 在 Linux 中安装微信

    微信 xff0c 是我在 Linux 中最难以舍弃的一个软件 因为安装经常遇到奇怪的问题 xff0c 而知乎上完整的教程并不多 xff0c 本文试图补上这个遗憾 适用范围 xff1a 本文以最流行的发行版 Manjaro xff08 KDE
  • TCP连接被意外重置的原因

    今天在做服务器压力测试的时候 xff0c 出现了很奇怪的情况 xff0c 与服务器建立连接会成功 xff0c 但是很快会被重置 RESET 掉 花了半天时间 xff0c 终于找到原因所在 xff0c 我把过程和结果写下来与大家分享 服务器正
  • 进程互斥锁

    进程间共享数据的保护 xff0c 需要进程互斥锁 与线程锁不同 xff0c 进程锁并没有直接的C库支持 xff0c 但是在Linux平台 xff0c 要实现进程之间互斥锁 xff0c 方法有很多 xff0c 大家不妨回忆一下你所了解的 下面
  • boost asio 应用方法学(二)——深入框架

    要用好它 xff0c 就必须先了解它 xff0c 而且不能停止于表面 xff0c 必须深入到内部 而了解一件事物 xff0c 先要了解它的框架 xff0c 再了解它的细节 了解了框架 xff0c 我们就有了提纲挈领的认识 关于 boost