boost.asio学习笔记03——io objects

时间:2022-09-09 12:29:07

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

上图中main()的第二行代码声明了一个tcp::resolver对象,后续进行地址解析的逻辑,都是围绕此resolver对象展开的。那么这个resolver是一个什么样的类型呢?阅读源代码我们在boost::asio::ip::tcp类内部看到了这样的类型别名定义:

typedef basic_resolver<tcp> resolver;

是否似曾相识呢?是的,这和我们STL中的string, iostream等一样,使用的都是某个模板类的一个实例。这里的resolverbasic_resolvertcp模版参数下的实例,stringbasic_stringchar模版参数下的实例——boost库和STL库统一风格的一个体现。

asio大量采用这种技术,所有和resolver一样提供io功能的类,都是某个baisc_模版类的实例化。下面我们来研究一下asioio 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关系网,我们用下图来显示:

boost.asio学习笔记03——io objects

这些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_handlewindows可随机访问的handle的封装,可以以异步或者同步方式等待
  • basic_stream_handlewindows面向流handle的封装,可以以异步或者同步方式等待
  • basic_descriptor POSIX描述符进行封装。

另外一点,所有这些io object的构造函数,都要求有一个io_service&作为参数,使用这一参数,这些io_object对象知道了自己的归属,之后自己所要派发出去的同步、异步操作请求,都将通过自己所在的这个io_service对象来完成。这也就说明了,为什么创建io_service对象是整个asio程序的第一步。

io object服务委托

上述的这些io object类,提供了应用开发中常用的各种“实际”功能,例如地址解析,以及socket读写等,那么这些io object类和第一部分中的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));

}

可见,async_resolve()果然是委托给了某个service类来做事的——再一次,典型的有事儿秘书干。那么,这儿的“this->service”又是什么呢?

通过跟踪代码的执行,知道该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, Handler handler)

{

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

}

PS

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

}

很明显,在构造过程中,使用use_service的返回值来初始化该service成员;我们知道,use_service会在io_service所维护的service链表中查找该类型,如果没有,就创建一个新的service实例;在此,就可以确保resolve_service<tcp>的实例已经被创建出来了,该服务就可以工作了(当然,不要忘记,该service事实上又委托了一层,而这个最底层的service实例,在此resolve_service<tcp>的构建过程中,内部再次调用use_service()创建出来了。过程如下:

boost.asio学习笔记03——io objects

除此之外,在构造函数体中,调用了serviceconstruct函数,做进一步的初始化。(PS:是否有Symbian中的二段构造的影子呢?)

asio io逻辑总结

前面部分以resolver为例,分析了resolver的功能和对应service之间的关系,纵观所有的io object,都是采用了这种模式,总结起来有如下几点:

· asio提供了多个basic_ 模版类。

· 应用层使用对应的basic_模版类的typedef的具体类,对外提供服务接口。

· io object内部,将操作委托给底层服务类。

· 底层服务类再次将操作委托给平台实现层,完成实际的工作。