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充當接耦的角色,將實現細節進行隱藏。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM