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