boost.asio 学习笔记02——io_service类

2024-06-15 02:48

本文主要是介绍boost.asio 学习笔记02——io_service类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

从第一个boost.asio的教程开始,boost文档就一直在告诉我们:使用boost.asio第一步就是要创建一个io_service对象。那么io_service是个什么东西呢?

boost.asio文档说,io_service为下面的这些异步IO对象提供最核心的IO功能:

  • boost::asio::ip::tcp::socket
  • boost::asio::ip::tcp::acceptor
  • boost::asio::ip::udp::socket
  • deadline_timer.

接着,文档就会说,像下面这样,就可以简简单单声明一个io_service对象了:

int main()

{

boost::asio::io_service io;

上面的一行代码,表明无限地风光与潇洒,看起来简简单单一行,就几乎有了异步IO、网络操作的框架,那么他到底代表了什么,在后面,又有什么事情发生呢?io_service又是如何支撑起这些功能的呢?带着这些问题,我们开始分析吧……

C++的角度看,上面的这一行代码,无非就是定义了一个io_service的实例,而C++的底层机制所隐藏起来的东西,无非就是初始化该对象的所有数据成员。再来看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_;

暂时抛开那几个成员,再来看一下io_service比较重要的一个函数:run() 的实现,发现该函数也就是将真正的功能委托给成员impl_去做事儿了:

std::size_t io_service::run()

{

boost::system::error_code ec;

std::size_t s = impl_.run(ec);

boost::asio::detail::throw_error(ec);

return s;

}

种种迹象表明,impl_是个巨牛的东西了。统揽io_service的实现,我们不难发现,该类的所有功能,几乎都是委托给了impl_成员去干了——典型的“有事秘书干”。不过想想也挺容易理解的,io_service提供了一个上层的接口,充当抛头露面的BOSS,真正的工作则委托给下一层的员工去实现——哪家公司不是这样呢?

所以,要想了解io_service的玄机,就需要弄清楚这三个数据成员到底是什么来历,特别是impl_的来历才行。

io_service的初始化

成员 init_ io_service的各个函数中,并没有显式用到。其存在的价值,就在于该类的构造函数里面,调用了初始化相关的代码,具体到Windows平台,就是WinSock的初始化,也就是调用 ::WSAStartup() 这一WinSock编程所必须调用的第一个函数;而其析构函数则进行清理工作,同样交由WinSock函数 :: WSACleanup() 完成。

也就是说,io_service通过init_数据成员的创建与销毁,自动完成了相关的初始化及清理工作。

io_service的实现委托

前面说过,impl_ 带着无限的神秘默默地完成了io_service::run()的功能。至于他到底是什么style,现在来揭开盖头吧。

从直接声明来看,impl_ 具有 impl_type& 类型。用SourceInsight不难发现该类型只不过是一个类型别名:

typedef detail::io_service_impl impl_type;

一波未平一波又起,这儿又冒出个io_service_impl。继续刨根问底,找到:

#if defined(BOOST_ASIO_HAS_IOCP)

namespace detail { typedef win_iocp_io_service io_service_impl; }

#else

namespace detail { typedef task_io_service io_service_impl; }

#endif

终于知道,在某些情况下,它是 win_iocp_ ,在某些情况下,是 task_ 。事实上,在Win NT 环境下,如果没有禁用IOCP,也就是没有声明 BOOST_ASIO_DISABLE_IOCP 这个预处理器指令,那么asio就采用 win_iocp_io_service 来做那些脏活累活;在剩下的其他平台,或者Win上禁用了IOCP,则使用 task_io_service 来做事儿了。

先稍微提一下,win_iocp_io_service 是对Windows环境下的IOCP(完成端口IO)模型的封装,该类作为boost.asioWindows下的核心,我们在后面详细分析其实现。

io_service的服务管理

io_service的另外一个数据成员 service_registry_,又具备什么样的身份和功能呢?我们来看看 io_service 的构造函数:

io_service::io_service()

: service_registry_

(

new boost::asio::detail::service_registry(

*this,

static_cast<impl_type*>(0),

(std::numeric_limits<std::size_t>::max)()

)

),

impl_(service_registry_->first_service<impl_type>())

{

}

构造函数为了初始化service_registry_,动态分配了一个boost::asio::detail:: service_registry类对象。为了构造该对象,提供了三个参数:

  • *this,这个是io_service对象本身,作为所有服务的owner存在。
  • static_cast<impl_type*>(0),一个空指针对象,主要是为了模版参数类型推导。在service_registry的构造过程中,也会自动创建一个该类型的对象,作为第一个service对象。具体到Win平台,即为构造一个 win_iocp_io_service 对象。
  • (std::numeric_limits<std::size_t>::max)():提供一个最大的整数作为service构造时的参数——目前还没看到这个参数的用途。

再来观察service_registry的实现,发现其实际就是一个链表,管理io_service所容纳的所有service对象(win_iocp_io_service就是一种service)。每种service都有一个id,链表以此id作为标志,在客户通过io_service来请求一种服务时,例如调用 use_service<ServiceType>(io_service&) 时,service_registry会查找链表,如果有对应类型的服务,就返回该类型服务实例的指针;否则就创建一个新的对象,并加入到链表末端,再返回此新创建的实例;通过这种形式,io_service确保每种类型的服务都只有一个实例存在。

asio提供了这样几个函数,来进行service的管理和使用——这也为扩展asio提供了可能,例如可以自己定义一种服务,使用add_service加入io_service进行管理。

  • template <typename Service>

Service& use_service(io_service& ios);

  • template <typename Service>

void add_service(io_service& ios, Service* svc);

  • template <typename Service>

bool has_service(io_service& ios);

io_service::service类型及跨平台策略

boost::asio::io_service


+ size_t run()
+ size_t poll()
+ void dispatch()
+ void stop()

- detail::winsock_init<> init_
- detail::service_registry* service_registry_
- impl_type& impl_

class boost::asio::io_service::::id

class boost::asio::io_service::work

enum boost::asio::io_service::fork_event

class boost::asio::io_service::strand

class boost::asio::io_service::service
- struct key key_
- boost::asio::io_service& owner_;
- service* next_;

io_service内部定义了这样一些类型,来为其服务:

  • io_service::id,所有service具体类的标识
  • io_service::service,所有service具体类的基类
  • io_service::strand,对所有并发提交的hanlder串行化执行
  • io_service::work,通知io_service有事儿要干的类,有点令人讨厌的主儿
  • io_service::fork_eventfork事件通知

针对service类型,asio从其派生出了数十个类分别完成不同的功能,例如在Win上充当io_servicewin_iocp_io_service类,以及为各种IO Object类型提供服务的类,如对应于TCPstream_socket_service,对应于UDPdatagram_socket_ service。下图显示了asio常用到的服务类,及其针对不同平台的适配类之间的关系。

boost.asio 学习笔记02——io_service类 - 地线 - 别再让虚假消息充斥这本已混乱的世界

图中左边的类,会在我们的应用程序中直接用到(由于asio又对这些类提供了一层动态组装,所以代码中不会去直接声明这些类型的实例,但是剥掉动态组装的外衣,我们声明的仍然是这些类的实例,具体在下面一部分说明),作为应用层的类;而右边部分,则是针对不同平台所提供的不同实现,作为平台适配层

应用层类在编译时,根据所在平台(其实是喂给编译器的各种预处理宏,如BOOST_ASIO_ HAS_IOCP),选择对应的类进行编译。例如用于TCP的服务类stream_socket_service是这样进行选择的:

template <typename Protocol>

class stream_socket_service

{

private:

// The type of the platform-specific implementation.

#if defined(BOOST_ASIO_HAS_IOCP)

typedef detail::win_iocp_socket_service<Protocol> service_impl_type;

#else

typedef detail::reactive_socket_service<Protocol> service_impl_type;

#endif

// The platform-specific implementation.

service_impl_type service_impl_;

};

其他需要进行平台决策的类型,都是采用这种技术,来选择不同的实现的。

io_service::service派生的完整的类列表如下:

  • boost::asio::detail::win_iocp_io_service
  • boost::asio::detail::task_io_service
  • boost::asio::detail::select_reactor
  • boost::asio::detail::epoll_reactor
  • boost::asio::detail::dev_poll_reactor
  • boost::asio::detail::kqueue_reactor

  • boost::asio::ip::resolver_service
  • boost::asio::socket_acceptor_service

  • boost::asio::stream_socket_service
  • boost::asio::datagram_socket_service
  • boost::asio::raw_socket_service
  • boost::asio::seq_packet_socket_service

  • boost::asio::serial_port_service

  • boost::asio::signal_set_service

  • boost::asio::deadline_timer_service
  • boost::asio::waitable_timer_service

  • boost::asio::detail::strand_service

  • boost::asio::posix::stream_descriptor_service

  • boost::asio::ssl::old::context_service
  • boost::asio::ssl::old::stream_service
  • boost::asio::ssl::old::detail::openssl_context_service
  • boost::asio::ssl::old::detail::openssl_stream_service

  • boost::asio::windows::object_handle_service
  • boost::asio::windows::random_access_handle_service
  • boost::asio::windows::stream_handle_service

还有几个非常重要的类,他们作为劳苦大众在金字塔底层默默提供service功能,但却没有从io_service::service派生;他们是上述那些服务类在各个平台的具体实现,为金字塔中间层的服务类提供再服务的(不难想象,提供服务的方式,又是那种“有事儿秘书干”的方式):

  • boost::asio::detail::win_iocp_socket_service
  • boost::asio::detail::win_iocp_handle_service
  • boost::asio::detail::reactive_descriptor_service
  • boost::asio::detail::reactive_serial_port_service
  • boost::asio::detail::reactive_socket_service
  • boost::asio::detail::win_iocp_serial_port_service
  • boost::asio::detail::win_object_handle_service

这篇关于boost.asio 学习笔记02——io_service类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062220

相关文章

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

使用TomCat,service输出台出现乱码的解决

《使用TomCat,service输出台出现乱码的解决》本文介绍了解决Tomcat服务输出台中文乱码问题的两种方法,第一种方法是修改`logging.properties`文件中的`prefix`和`... 目录使用TomCat,service输出台出现乱码问题1解决方案问题2解决方案总结使用TomCat,

解决systemctl reload nginx重启Nginx服务报错:Job for nginx.service invalid问题

《解决systemctlreloadnginx重启Nginx服务报错:Jobfornginx.serviceinvalid问题》文章描述了通过`systemctlstatusnginx.se... 目录systemctl reload nginx重启Nginx服务报错:Job for nginx.javas

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss