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

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

简单解释一下:

这里由使用者(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 类的定义很简单,总共就三个成员变量:

[cpp] view plaincopy
  1. #if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
  2. detail::winsock_init<> init_;
  3. #elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \
  4. || defined(__osf__)
  5. detail::signal_init<> init_;
  6. #endif
  7. // The service registry.
  8. boost::asio::detail::service_registry* service_registry_;
  9. // The implementation.
  10. 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

[cpp] view plaincopy
  1. inline std::size_t io_service::run(boost::system::error_code& ec)
  2. {
  3. return impl_.run(ec);
  4. }
  5. inline std::size_t io_service::poll(boost::system::error_code& ec)
  6. {
  7. return impl_.poll(ec);
  8. }
  9. inline void io_service::stop()
  10. {
  11. impl_.stop();
  12. }
  13. inline void io_service::reset()
  14. {
  15. impl_.reset();
  16. }
  17. template <typename Handler>
  18. inline void io_service::dispatch(Handler handler)
  19. {
  20. impl_.dispatch(handler);
  21. }
  22. template <typename Handler>
  23. inline void io_service::post(Handler handler)
  24. {
  25. impl_.post(handler);
  26. }

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

[cpp] view plaincopy
  1. #if defined(BOOST_ASIO_HAS_IOCP)
  2. typedef detail::win_iocp_io_service impl_type;
  3. #elif defined(BOOST_ASIO_HAS_EPOLL)
  4. typedef detail::task_io_service<detail::epoll_reactor<false> > impl_type;
  5. #elif defined(BOOST_ASIO_HAS_KQUEUE)
  6. typedef detail::task_io_service<detail::kqueue_reactor<false> > impl_type;
  7. #elif defined(BOOST_ASIO_HAS_DEV_POLL)
  8. typedef detail::task_io_service<detail::dev_poll_reactor<false> > impl_type;
  9. #else
  10. typedef detail::task_io_service<detail::select_reactor<false> > impl_type;
  11. #endif

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

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

[cpp] view plaincopy
  1. template <typename Service>
  2. inline Service& use_service(io_service& ios)
  3. {
  4. // Check that Service meets the necessary type requirements.
  5. (void)static_cast<io_service::service*>(static_cast<Service*>(0));
  6. (void)static_cast<const io_service::id*>(&Service::id);
  7. return ios.service_registry_->template use_service<Service>();
  8. }
  9. template <typename Service>
  10. void add_service(io_service& ios, Service* svc)
  11. {
  12. // Check that Service meets the necessary type requirements.
  13. (void)static_cast<io_service::service*>(static_cast<Service*>(0));
  14. (void)static_cast<const io_service::id*>(&Service::id);
  15. if (&ios != &svc->io_service())
  16. boost::throw_exception(invalid_service_owner());
  17. if (!ios.service_registry_->template add_service<Service>(svc))
  18. boost::throw_exception(service_already_exists());
  19. }
  20. template <typename Service>
  21. bool has_service(io_service& ios)
  22. {
  23. // Check that Service meets the necessary type requirements.
  24. (void)static_cast<io_service::service*>(static_cast<Service*>(0));
  25. (void)static_cast<const io_service::id*>(&Service::id);
  26. return ios.service_registry_->template has_service<Service>();
  27. }

看起来是个集合容器,里面的元素是服务(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 这一端。这就是上层使用者角度看到的景象。

boost asio 应用方法学(二)——深入框架相关推荐

  1. Boost asio学习笔记之二—— 网络编程

    boost库中的网络编程的例子比较复杂,不太好理解,所以,从网上找了一个简单点的例子.网址如下:http://blog.chinaunix.net/u3/93184/showart_1846119.h ...

  2. CS106A编程方法学二

    机器人卡雷尔的四个指令: move(); //向前移一格;turnLeft(); //向左转;pcickBeeper(); //捡起方块:putBeeper();//放下方块: 定义向右转 publi ...

  3. Boost asio 官方教程简介

    1. 概述 本章介绍了 Boost C++ 库 Asio,它是异步输入输出的核心. 名字本身就说明了一切:Asio 意即异步输入/输出. 该库可以让 C++ 异步地处理数据,且平台独立. 异步数据处理 ...

  4. 基于boost asio实现的支持ssl的通用socket框架

    情景分析    现已存在一个可用稳定的异步客户端类http_client_base,该类基于boost asio实现了连接服务器,发送请求,获取响应和解析http数据等操作,该类的大致实现框架如下   ...

  5. 笨方法学Python(二)

    笨方法学Python,习题16 - 21 版本:3.8.0 编辑器:Visual Studio Code 习题16到21讲的是文件的读写和函数的基础,可以通过一个实例来同时练习他们.在下列情景中,我将 ...

  6. boost asio ——深入框架

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

  7. boost::asio async_write也不能保证一次发完所有数据 二

    只有看boost源码才能弄明白发生了什么.首先我是将vector里面写入了数据,然后用boost::asio::buffer将vector构造成了mutable_buffer_1对象. 参考该文档的重 ...

  8. Boost.Asio,libevent和ACE之间关于Socket编程的比较(★firecat推荐★)

    文章来源:http://blog.163.com/miky_sun/blog/static/3369405201041753652505/ ACE官网 http://download.dre.vand ...

  9. boost.asio包装类st_asio_wrapper开发教程(2014.5.23更新)(一)-----转

    一:什么是st_asio_wrapper 它是一个c/s网络编程框架,基于对boost.asio的包装(最低在boost-1.49.0上调试过),目的是快速的构建一个c/s系统: 二:st_asio_ ...

最新文章

  1. 今年下半年,中日合拍的《Git游记》即将正式开机,我将...(上集)
  2. u盘被分区之后怎么合并linux,U盘格式化做启动盘后从16GB变成200MB的解决方法
  3. AI基础:词嵌入基础和Word2vec
  4. 一个整数数组,每个数字都出现K次,只有一个数字出现M次,找出这个数字(线性时间)
  5. SpringBoot整合Shiro实现登录认证和授权CHCache
  6. 新RSS reader
  7. FFmpeg开发实战(三):FFmpeg 打印音视频Meta信息
  8. python惰性求值_让Python中类的属性具有惰性求值的能力
  9. Python中Permission denied怎么解决
  10. 关于Ubuntu下apt的一些用法及和yum的比较
  11. 指标的检测及检测仪器
  12. eclipse安装maven3
  13. 古体字与简体字对照表_王力_简体字与繁体字对照表
  14. 微信小程序样式-元素选择器的使用
  15. 论文阅读:Attention-based Dropout Layer for Weakly Supervised Object Localization
  16. tc275的flash
  17. TimestampType.nullSafeGet(203) - could not read column value from result set: xxx; An SQLExc
  18. STM32 使用SYN6288语音模块
  19. Tomcat启动后闪退或一会儿后退出
  20. TypeError: norm() received an invalid combination of arguments解决办法

热门文章

  1. MD5加密以及验证加密-加盐
  2. 如何修改和查看tomcat内存大小
  3. Java学习笔记——显示当前日期的三种方式
  4. Practical Java 摘录(四)--多线程
  5. python同时对文件进行读写操作-Python实现的读取文件内容并写入其他文件操作示例...
  6. python爬虫教程网-Python爬虫全集
  7. python3语法错误-【Python3之异常处理】
  8. python工作招聘-爬了招聘网站之后,给你几点学习Python的建议
  9. python编写一个软件-软件代做:利用Python编写一个行业专用的小计算器
  10. python pandas读取excel-Python使用Pandas读写EXCEL文件教程