以当前最新版本的boost为例(1.53.0,之后的代码也是以1.53.0为准)。
ASIO Examples:http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/examples.html
io_service在ASIO中有着举足轻重的地位置如果对io_service的实现不感兴趣可以选择性无视这篇文章。在开始说io_service之前,先介绍一下ASIO给我们提供的可用于TCP服务器开发的组件。
boost::asio::ip::tcp::acceptor | 用于监听与接受连接的类。 |
boost::asio::ip::tcp::socket | 套接字,提供同步/异步IO方法,一个socket对象面向一个客户端连接。 |
boost::asio::deadline_timer | 计时器,提供同步/异步计时功能并在计时结束之后回调到一个函数。 |
io_service的工作方式
把io_service想象为一个Worker Pool,也可以把它当作一个IO队列,他是一个联系用户和ASIO核心实现的类。如果你看过ASIO的例子或者用过ASIO,在构造上面表格中的对象时,往往需要给它们传递一个io_service对象,因为io_service担任的是一个Worker Pool的作用,这些组件的任何IO操作都会通过io_service.post()方法去投递一个任务。
为了更直观地看到io_service的作用,来一段异步定时器(deadline_timer)工作的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include
#include
int main()
{
void on_expired();
boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_from_now(boost::posix_time::milliseconds(3000));
timer.async_wait(boost::bind(on_expired, boost::asio::placeholders::error));
io_service.run();
return 0;
}
void on_expired( const boost:: system ::error_code& error)
{
if (!error)
{
std::cout << "timer expired." << std::endl;
}
}
|
上面代码演示了一个异步定时器的工作流程:
1.定义一个io_service对象。
2.定义一个deadline_timer对象,并把io_service对象作为唯一参数传递给它。
3.使用expires_from_now()方法设置一个以当前时间为起始点的超时时间。
4.调用deadline_timer的async_wait()方法使定时器进入等待。
5.调用io_service的run()方法。
第4步调用async_wait()方法之后,deadline_timer的内部就做了一些和io_service打交道的事,它实则是向io_service提交了一个异步任务,io_service会把它放到队列里。第5步我们调用run()方法时,io_service就开始了它的工作,并把队列中的任务进行处理。
io_service用例图:
这个用例图展示了io_service的工作方式:
1.用户的应用程序提交一个操作。
2.操作转发给io_service,告诉它我有一个异步操作可能要执行,此时io_service便会把该操作放入一个队列里。
3.调用run()方法启动io_service。
4.io_service启动后,会把队列中的操作提交给操作系统,相当于一个联系了用户和操作系统的媒介。
5.操作系统把执行结果反馈给io_service,io_service会根据结果构造一个boost::system::error_code对象并回调给用户,通过该对象可获知所投递操作的执行结果。
流程很简单,从我们的应用程序逐层向下,完成之后再逐层向上反馈。但是,我们看到的其实只是冰山一角。ASIO是一个网络库,除了给我们提供了定时器这类开发组件之外,更重要的是它的网络通信功能。ASIO提供的网络通信功能,不仅封装了操作系统提供的socket api,同时也用自己的思想把不同平台的网络通信模型封装了起来。下面我们来深入io_service,揭开其面纱。
io_service的跨平台策略
考虑到在多平台下的实现细节,io_service可谓是做了不少功夫。要根据不同平台提供的api去设计一个统一的框架提供给用户,而且用户不必关心其实现细节,除了工程庞大之外,也必须要有一个良好的框架。我们从io_service的源码入手,探讨它如何去适应不同平台的实现。(推荐用Source Insight, Sublime Text 2或VC编辑器阅读源码)。
由io_service.run()开始,先转到run()的定义:
1
|
std:: size_t s = impl_.run(ec);
|
该方法实则上是调用了impl_对象的run()方法,看起来是io_service在impl_的基础上封装了一层,impl_是io_service的成员,不难看出,impl_担任了一个实现功能细节的角色。在io_service类的声明中,找到impl_的声明:
1
2
|
// The implementation.
impl_type& impl_;
|
然后是impl_type类型的定义:
1
|
typedef detail::io_service_impl impl_type;
|
原来impl_type的原本类型是detail::io_service_impl,实质上它是由宏来决定其最终类型。转到定义可以发现:
1
2
3
4
5
|
#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
|
上面代码很清晰可以看到,如果定义了BOOST_ASIO_HAS_IOCP这个宏,那么io_service_impl的类型就是win_iocp_io_service,否则就是task_io_service. 如此说来,io_service_impl这个类型完全是由BOOST_ASIO_HAS_IOCP宏来决定的。跟踪一下这个宏,看它如何定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// Windows: IO Completion Ports.
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
# if !defined(UNDER_CE)
# if !defined(BOOST_ASIO_DISABLE_IOCP)
# define BOOST_ASIO_HAS_IOCP 1
# endif // !defined(BOOST_ASIO_DISABLE_IOCP)
# endif // !defined(UNDER_CE)
# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
// Linux: epoll, eventfd and timerfd.
#if defined(__linux__)
# include
# if !defined(BOOST_ASIO_DISABLE_EPOLL)
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
# define BOOST_ASIO_HAS_EPOLL 1
# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
# endif // !defined(BOOST_ASIO_DISABLE_EVENTFD)
# if !defined(BOOST_ASIO_DISABLE_EVENTFD)
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
# define BOOST_ASIO_HAS_EVENTFD 1
# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
# endif // !defined(BOOST_ASIO_DISABLE_EVENTFD)
# if defined(BOOST_ASIO_HAS_EPOLL)
# if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
# define BOOST_ASIO_HAS_TIMERFD 1
# endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
# endif // defined(BOOST_ASIO_HAS_EPOLL)
#endif // defined(__linux__)
|
如果在Windows或者CYGWIN环境并且IOCP未被禁用的情况下,那么ASIO的网络模型就会被定义为IOCP;而在linux下,则有epoll, eventfd, timerfd三种情况,但结合io_service_impl的定义,可以发现除了BOOST_ASIO_HAS_IOCP在打开时io_service_impl被定义为win_iocp_io_service之外,其它情况都是task_io_service. 因此可以把ASIO归纳为只有两种实现,分别是win_iocp_io_service和task_io_service. 而在io_service中impl_成员的最终类型,也只有这两种。无论采用了哪种模型,io_service暴露的方法都一样,他们的具体实现都必须转给impl_去做。
这一章简要说了一下io_service的组成和工作机制,接着将介绍io_service的Proactor框架实现和io_service的service管理。因为这一章的内容和接下来要说的内容关联不算太大,而且说到Proactor实现部分比较长。其实下一章内容写的差不多了,不过还要写几天再发,先发这篇短的。