boost.asio 学习笔记 1-3
2014-10-26 23:14
351 查看
boost.asio 学习笔记01——概述
分类: 学习笔记2013-05-2314:03 458人阅读 评论(0) 收藏 举报
boost.asio为异步IO提供了一份标准的C++的跨平台实现,特别针对网络IO提供了良好的支持,使之成为C++网络编程利器。关于如何使用asio,boost文档中已经有了详尽说明,而且附带的例子也很直观,我们不必再造轮子;本文则结合asio的基本应用,侧重于源代码的分析,特别是针对windows平台上的实现进行分析。
纵观asio源码,在统一的接口层之下,asio提供了大量的类来支持不同的平台(Windows、Unix...)、不同的IO类型(同步、异步)及IO模型(IOCP、Select、Poll)及网络协议(TCP,UDP,ICMP)。归纳起来,这一大堆类可以分为三层,分别是:
IO Object层
basic_ 模版层
服务实现层
本系列文章从io_service类型入手,开启代码分析行程,采用自底向上的方法,逐层推导,最后得到asio的体系结构;如果想先了解asio的整体结构,可以先跳到asio体系结构部分。
(理论上来说,行文伊始还是该先举个简单的例子说一下asio如何使用,不过这样的例子少说也要六七十行代码,为节约篇幅就不贴了;真想先看一个完整例子的,烦请移步下面链接瞧下那只麻雀:http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html)
boost.asio 学习笔记02——io_service类
分类: 学习笔记2013-05-2314:04 670人阅读 评论(0) 收藏 举报
目录(?)[+]
从第一个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; |
从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_; |
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; } |
所以,要想了解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 { typedefwin_iocp_io_service io_service_impl; }
#else
namespace detail { typedeftask_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.asio在Windows下的核心,我们在后面详细分析其实现。
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类对象。为了构造该对象,提供了三个参数:
*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::id,所有service具体类的标识
io_service::service,所有service具体类的基类
io_service::strand,对所有并发提交的hanlder串行化执行
io_service::work,通知io_service有事儿要干的类,有点令人讨厌的主儿
io_service::fork_event,fork事件通知
针对service类型,asio从其派生出了数十个类分别完成不同的功能,例如在Win上充当io_service的win_iocp_io_service类,以及为各种IO
Object类型提供服务的类,如对应于TCP的stream_socket_service,对应于UDP的datagram_socket_
service。下图显示了asio常用到的服务类,及其针对不同平台的适配类之间的关系。
图中左边的类,会在我们的应用程序中直接用到(由于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学习笔记03——io objects
分类: 学习笔记2013-05-2314:04 654人阅读 评论(0) 收藏 举报
目录(?)[+]
asio的文档,告诉我们在声明一个io_service对象之后,就可以创建io对象去干活了,例如:
int main(int argc, char* argv[]) { boost::asio::io_service io_service; tcp::resolver resolver(io_service); tcp::resolver::query query("www.boost.org","80"); tcp::resolver::iterator iterator = resolver.resolve(query); |
typedef basic_resolver<tcp> resolver;
是否似曾相识呢?是的,这和我们STL中的string,
iostream等一样,使用的都是某个模板类的一个实例。这里的resolver是basic_resolver在tcp模版参数下的实例,string是basic_string在char模版参数下的实例——boost库和STL库统一风格的一个体现。
asio大量采用这种技术,所有和resolver一样提供io功能的类,都是某个baisc_模版类的实例化。下面我们来研究一下asio的io
object逻辑。
io object类关系网
继续向上追溯basic_resolver,知道该类从basic_io_object派生,主要负责地址解析相关的操作,提供的主要接口有resolver(),async_resolve()等。
查看整个asio的源代码,我们发现从basic_io_object派生的类不少,他们分别负责一些具体的事务,例如basic_socket_acceptor可以作为一个服务器进行侦听,提供了诸如bind(),
listen()等接口;再如basic_socket类是对socket
IO 操作的封装,提供了receive(), async_receive(), read_some(), async_readsome(), write_some(), async_write_some()等接口。
整个asio中的io
object关系网,我们用下图来显示:
这些io object的功能,简述如下:
basic_deadline_timer 提供定时器功能,可以同步等待,也可以异步等待。
basic_waitable_timer和basic_deadline_timer具有同样的功能,主要区别为该定时器可以和C++
11中引入的chrono库协作。
basic_signal_set支持信号相关的操作,异步方式等待单个或者多个信号的发生。
basic_socket_acceptor作为服务器进行侦听,接收连接请求。
basic_serial_port对串口操作进行支持。
basic_resolver地址解析类。
basic_stream_socket提供同步、异步方式的基于流的socket操作。
basic_datagram_socket提供同步、异步方式的基于数据报文的socket操作。
basic_raw_socket提供同步、异步方式的基于raw数据的socket操作
basic_seq_packet_socket提供同步、异步方式的基于有序包的socket操作
basic_socket_streambuf ?
basic_object_handle对windows handle的封装,可以以异步或者同步方式等待
basic_random_access_handle对windows可随机访问的handle的封装,可以以异步或者同步方式等待
basic_stream_handle对windows面向流handle的封装,可以以异步或者同步方式等待
basic_descriptor对POSIX描述符进行封装。
另外一点,所有这些io object的构造函数,都要求有一个io_service&作为参数,使用这一参数,这些io_object对象知道了自己的归属,之后自己所要派发出去的同步、异步操作请求,都将通过自己所在的这个io_service对象来完成。这也就说明了,为什么创建io_service对象是整个asio程序的第一步。
io object服务委托
上述的这些io object类,提供了应用开发中常用的各种“实际”功能,例如地址解析,以及socket读写等,那么这些ioobject类和第一部分中的service类之间存在着什么样的关系呢?会不会是这些io
object只是一个应用的接口,而具体的功能则委托给service类来完成呢?如果是这样的,那么又是如何实现的呢?
带着这些问题,我们继续研究resolver类的源代码,看看他所提供的功能,到底是如何实现的。
resolver是如何委托的
我们知道,resolver类是在boost::asio::ip::tcp类中的一个类型别名:typedef basic_resolver<tcp> resolver;
那么,resolver类所提供的async_resolve()接口,就来自于basic_resolver类。再来看看basic_resolver::async_resolve()的实现——该函数有两个重载,我们以其中一个为例:
template <typenameResolveHandler> void async_resolve(constquery&q,BOOST_ASIO_MOVE_ARG(ResolveHandler)handler) { // If you get an error on the following line it means that your handler does // not meet the documented type requirements for a ResolveHandler. BOOST_ASIO_RESOLVE_HANDLER_CHECK( ResolveHandler, handler, iterator)type_check; returnthis->service.async_resolve(this->implementation,q, BOOST_ASIO_MOVE_CAST(ResolveHandler)(handler)); } |
通过跟踪代码的执行,知道该service其实是第一部分曾经提到过的boost::asio::ip::resolver_service,而它又委托给了boost::asio::detail::resolver_service——这家伙再无其他可以委托的对象了,只有苦逼的自己做事儿了——其基本思路就是先创建一个用以地址解析的resolve_op,用这个op来代表本次异步操作,之后启动op去做事情。至于op又是什么东西,稍后在operation部分做介绍;先贴出这段苦主:
// boost::asio::detail::resolver_service template <typenameHandler> void async_resolve(implementation_type&impl, constquery_type&query,Handlerhandler) { // Allocate and construct an operation to wrap the handler. typedefresolve_op<Protocol,Handler>op; typenameop::ptr p = { boost::addressof(handler), boost_asio_handler_alloc_helpers::allocate( sizeof(op),handler), 0 }; p.p =new (p.v)op(impl,query, io_service_impl_,handler); BOOST_ASIO_HANDLER_CREATION((p.p,"resolver", &impl,"async_resolve")); start_resolve_op(p.p); p.v = p.p = 0; } |
- 做过Symbian开发的对这种形式似曾相识,这多少和Active
Object异曲同工
- 那几行创建op的代码还不是很明白
OK,至此,整个async_resolve()从上到下就拉通了。概括起来,就是io
object将具体功能委托给服务类,服务类又委托给和平台实现相关的服务类来完成最后的功能。
io_object的服务创建
通过resolver的执行,我们知道了其层层委托关系,那么resolver所委托的this->service又是怎么来的呢?下面部分,我们来分析io_object所做的服务管理工作。首先来看该service的具体类型。
要想知道service是如何创建的,我们就要追根朔源找到resolver的具体类型声明,看看到底是如何将service拉上贼船的。好吧,我们再次从头开始(别嫌啰嗦):
typedef basic_resolver<tcp> resolver;
针对这个模版实例,将basic_resolver展开:
template <typename InternetProtocol,
typename ResolverService = resolver_service<InternetProtocol> >
class basic_resolver: public basic_io_object<ResolverService>
à
template <tcp,
typename ResolverService = resolver_service<tcp> >
class basic_resolver: public basic_io_object< resolver_service<tcp> >
再对basic_io_object进行展开:
template <typename IoObjectService>
class basic_io_object
à
template < resolver_service<tcp> >
class basic_io_object
在basic_io_object内部展开:
typedef IoObjectService service_type;
à
typedef resolver_service<tcp> service_type;
于是,可以确定basic_io_object::service的类型为resolver_service<tcp>&;从而将io
object和下层的service关联起来。
再来看basic_io_object::service的初始化。我们来看其构造函数:
explicit basic_io_object(boost::asio::io_service&io_service) : service(boost::asio::use_service<IoObjectService>(io_service)) { service.construct(implementation); } |
除此之外,在构造函数体中,调用了service的construct函数,做进一步的初始化。(PS:是否有Symbian中的二段构造的影子呢?)
asio的io逻辑总结
前面部分以resolver为例,分析了resolver的功能和对应service之间的关系,纵观所有的ioobject,都是采用了这种模式,总结起来有如下几点:
· asio提供了多个basic_模版类。
· 应用层使用对应的basic_模版类的typedef的具体类,对外提供服务接口。
· io
object内部,将操作委托给底层服务类。
· 底层服务类再次将操作委托给平台实现层,完成实际的工作。
相关文章推荐
- boost.asio 学习笔记02——io_service类
- boost.asio 学习笔记05——asio的windows实现
- boost.asio 学习笔记07——总结
- boost asio io_service学习笔记
- boost asio io_service学习笔记
- boost.asio 学习笔记02——io_service类
- boost asio io_service学习笔记
- boost.asio 学习笔记06——其他
- boost.asio 学习笔记02——io_service类
- boost.asio 学习笔记01——概述
- boost.asio 学习笔记04——asio的体系结构
- Boost asio学习笔记之二—— 网络编程
- boost.asio 学习笔记02——io_service类
- boost asio io_service学习笔记
- boost.asio 学习笔记04——asio的体系结构
- boost.asio 学习笔记05——asio的windows实现
- boost.asio 学习笔记01——概述
- boost asio io_service学习笔记
- boost.asio 学习笔记06——其他
- boost.asio 学习笔记04——asio的体系结构