boost.asio源码阅读(1) - 从chat_server开始

1. 关于示例代码 chat

先从简单的入手,
在如下路径:boost_1_63_0/libs/asio/example/cpp11/chat中找到chat_server.cpp
查看其成员,

private:
tcp::acceptor acceptor_;
tcp::socket socket_;

仅关注上述两个即可,其它的请自动忽略,在本文中不相关。使用过asio的都知道,io_service绝对不能少
,在本例中由main传入。这次分析代码全部从本例出发,通过逐步查看acceptor_和socket知道asio的工作原理。
正式开工!!

2. acceptor

先来看看例子中的acceptor_的定义,如下:

class tcp
{
public:
typedef basic_socket_acceptor<tcp> acceptor;
// 忽略代码....
};

template <typename Protocol,
typename SocketAcceptorService = socket_acceptor_service<Protocol> >
class basic_socket_acceptor
  : public basic_io_object<SocketAcceptorService>,
    public socket_base
{
//忽略代码....
};

acceptor是一个继承自basic_io_object和socket_base,同时具有模板参数
Protocol和SocketAcceptorService的类。Protocol即本例中的tcp。
搜索tcp.hpp所在路径,会发现udp.hpp。两个文件同在ip这个路径下面,表示要使用的内容是属于
哪种协议。对比两个类的定义,能够知道仅在protocol()函数返回的值IPPROTO_UDP和IPPROTO_TCP不同。

2.1 acceptor的构造

chat_server(boost::asio::io_service& io_service,
  const tcp::endpoint& endpoint)
: acceptor_(io_service, endpoint),
  socket_(io_service)

在构造中,传入了两个参数io_service和endpoint,reuse_addr标记是否开启地址复用。

basic_socket_acceptor(boost::asio::io_service& io_service,
      const endpoint_type& endpoint, bool reuse_addr = true)
    : basic_io_object<SocketAcceptorService>(io_service)
  {
    boost::system::error_code ec;
    1. const protocol_type protocol = endpoint.protocol();
    2. this->get_service().open(this->get_implementation(), protocol, ec);
    boost::asio::detail::throw_error(ec, "open");
    if (reuse_addr)
    {
      this->get_service().set_option(this->get_implementation(),
	  socket_base::reuse_address(true), ec);
      boost::asio::detail::throw_error(ec, "set_option");
    }
    this->get_service().bind(this->get_implementation(), endpoint, ec);
    boost::asio::detail::throw_error(ec, "bind");
    this->get_service().listen(this->get_implementation(),
	socket_base::max_connections, ec);
    boost::asio::detail::throw_error(ec, "listen");
  }

line1: endpoint 提供了一个函数protocol() 返回的类型是protocol_type,定义如下:

typedef Protocol protocol_type;

其它的位置暂时没有相关的内容了,所以为了避免在开始就把摊子铺的太开,可以暂时不鸟endpoint的细节,
仅知道在basic_socket_acceptor中,它帮助类识别当前是什么协议(tcp/udp).
line2: 函数中open/set_option/bind/listen是相似操作。get_service隐藏了关键的细节,而在boost的调性中,this表明“我要啃老了”。

2.2 被啃老的basic_io_object

在chapter 2.0中,basic_io_object的模板参数传入了类型,SocketAcceptorService,当然啦在本例中即默认参数
socket_acceptor_service。

template <typename IoObjectService>
class basic_io_object
{
public:
  /// The type of the service that will be used to provide I/O operations.
  typedef IoObjectService service_type;

  /// The underlying implementation type of I/O object.
  typedef typename service_type::implementation_type implementation_type;

到这里已经不用向下看了,在chapter2.1中使用的操作都集中在了socket_acceptor_service中。
在asio中,basic_io_object提供了*_acceptor 和 *acceptor_service的衔接桥梁,明确了接口暴露和功能实现的区分方式。
类似的映射关系还存在于如下:

basic_deadline_timer -> deadline_timer_service
basic_serial_port    -> serial_port_service
basic_signal_set     -> signal_set_service

由于_acceptor_service的实现过于复杂包含很多作为acceptor不需要知道的细节,其实看到这里通过命名也可以知道,
_acceptor_service具有io_service和acceptor的衔接者身份。
前方高能!!!!

2.3 socket_acceptor_service

从上文中可以知道acceptor的功能实现都集中于其对应的acceptor_service中,在2.1中还留下一个坑
this->get_implementation()到底是个啥子?

typedef detail::reactive_socket_service<Protocol> service_impl_type;
typedef typename service_impl_type::implementation_type implementation_type;

reactive_socket_service提供了我们最终的类型定义:

struct base_implementation_type
  {
    // The native socket representation.
    socket_type socket_;

    // The current state of the socket.
    socket_ops::state_type state_;

    // Per-descriptor data used by the reactor.
    reactor::per_descriptor_data reactor_data_;
  };

终于出现了和2.1节函数名称相符合的参数了,socket_type(int)。
io_service在本类中作为reactive_socket_service的构造函数参数,reactive_socket_service啃老reactive_socket_service_base

reactive_socket_service_base::reactive_socket_service_base(
    boost::asio::io_service& io_service)
  : reactor_(use_service<reactor>(io_service))
{
  reactor_.init_task();
}

终于出现能和io_service扯上关系的地方了。查看reactor_

#if defined(BOOST_ASIO_WINDOWS_RUNTIME)
typedef class null_reactor reactor;
#elif defined(BOOST_ASIO_HAS_IOCP)
typedef class select_reactor reactor;
#elif defined(BOOST_ASIO_HAS_EPOLL)
typedef class epoll_reactor reactor;
#elif defined(BOOST_ASIO_HAS_KQUEUE)
typedef class kqueue_reactor reactor;
#elif defined(BOOST_ASIO_HAS_DEV_POLL)
typedef class dev_poll_reactor reactor;
#else
typedef class select_reactor reactor;
#endif

linux下当然要关心epoll_reactor,惊喜啊,原来关于epoll操作的也出现在这里了,epoll_reactor就是我们要
找的关键操作。

void epoll_reactor::init_task()
{
  io_service_.init_task();
}

调用io_service的init_task, 经过复杂的过程终于,在这里又绕回了io_service(上帝)。这里的io_service实际是
task_io_service。

3. 小结

本文从char_server的acceptor出发,通过逐步代码逐步查看,从关键操作中,逐步找到关于系统操作的封装(epoll_reactor)及如何使acceptor和io_service产生联系。basic_io_object充当接耦的角色,将实现细节进行隐藏。

posted @ 2017-03-20 17:57  eskylin的笔记  阅读(1919)  评论(0编辑  收藏  举报