要用好它,就必須先了解它,而且不能停止于表面,必須深入到内部。而了解一件事物,先要了解它的架構,再了解它的細節。了解了架構,我們就有了提綱挈領的認識。
關于 boost asio 架構結構,在其文檔中,用了這樣一張圖來描述:

簡單解釋一下:
這裡由使用者(Initiator)啟動一個異步操作(Asynchronous Operation),在啟動異步的同時它要負責建立一個異步回調對象(Completion Handler),然後該異步操作被交給了異步操作執行者(Asynchronous Operation Processor),由它負責執行異步操作,并在完成後将一個完成事件插入完成事件隊列(Completion Event Queue);另一方面,前攝器(Proactor,這個詞很難準确翻譯,也有翻譯為主動器,可能借義于proactive)驅動異步事件分派器(Asynchronous Event Demultiplexer)從完成事件隊列中擷取事件,這是一個阻塞的過程,一旦擷取到完成事件,前攝器從事件上找出與該事件關聯的回調對象,并執行回調。
這是一個标準的前攝器模式,這個模式是在 ACE 網絡庫中使用的概念。關于該模式的研究很多,搜尋一下 ACE Proactor 就可以找到很多資料。上面的描述也比較容易了解,唯一比較難搞懂的是異步事件分派器(Asynchronous Event Demultiplexer),好像它的存在并不起多大作用,其實它的作用大着呢,特别是在多線程中,它要保證異步完成事件的及時分派,提高多線程并發度,以及降低線程切換開銷。在 windows 完成端口的文檔中有這方面的機制介紹。
總得來說,這是一個概念性的模型,僅用這個模型來描述 boost asio,根本展現不了 boost asio 的優點。即使從使用者的角度,僅掌握這樣的模型也是不夠,boost asio 還有很多值得學習借鑒的地方。
我們需要結合這個模型來深入 boost asio 的實作架構。
boost asio 是如何實作前攝器模式的呢?我們使用 boost asio 第一步都需要建立一個 boost::asio::io_service,我們就從 io_service 開始開啟我們的探秘之旅。
io_service 類的定義很簡單,總共就三個成員變量:
[cpp] view plain copy
- #if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- detail::winsock_init<> init_;
- #elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \
- || defined(__osf__)
- detail::signal_init<> init_;
- #endif
- // The service registry.
- boost::asio::detail::service_registry* service_registry_;
- // The implementation.
- impl_type& impl_;
其實簡單反而意味着強大,因為這表明 boost asio 已經把功能結構劃分的很清晰了。
三個成員變量中的 init_ 與結構沒有太大關系,windows 平台的 winsock_init 裡面調用了 WSAStartup,linux 平台的 signal_init 裡面屏蔽了 SIG_PIPE 信号。這兩個東東幾乎是在我們剛開始接觸網絡程式設計就遇到的知識點。
其次我們再來看看 impl_ 成員,從名字上看應該是 io_service 的實作類,确實 io_service 的很多接口是直接轉發給了 impl_ 成員,比如 run、poll、stop、reset、post、dispatch
- inline std::size_t io_service::run(boost::system::error_code& ec)
- {
- return impl_.run(ec);
- }
- inline std::size_t io_service::poll(boost::system::error_code& ec)
- return impl_.poll(ec);
- inline void io_service::stop()
- impl_.stop();
- inline void io_service::reset()
- impl_.reset();
- template <typename Handler>
- inline void io_service::dispatch(Handler handler)
- impl_.dispatch(handler);
- inline void io_service::post(Handler handler)
- impl_.post(handler);
但是 impl_type 是什麼呢?它的定義如下:
- #if defined(BOOST_ASIO_HAS_IOCP)
- typedef detail::win_iocp_io_service impl_type;
- #elif defined(BOOST_ASIO_HAS_EPOLL)
- typedef detail::task_io_service<detail::epoll_reactor<false> > impl_type;
- #elif defined(BOOST_ASIO_HAS_KQUEUE)
- typedef detail::task_io_service<detail::kqueue_reactor<false> > impl_type;
- #elif defined(BOOST_ASIO_HAS_DEV_POLL)
- typedef detail::task_io_service<detail::dev_poll_reactor<false> > impl_type;
- #else
- typedef detail::task_io_service<detail::select_reactor<false> > impl_type;
原來是根據不同的平台支援特性,選擇了不同的實作,要把這麼多種實作融合起來,沒有一個很好的架構是很難做到的。
我們再來看看 service_registry_,對這個成員變量的使用展現在 use_service、add_service、has_service 三個函數中:
- template <typename Service>
- inline Service& use_service(io_service& ios)
- // Check that Service meets the necessary type requirements.
- (void)static_cast<io_service::service*>(static_cast<Service*>(0));
- (void)static_cast<const io_service::id*>(&Service::id);
- return ios.service_registry_->template use_service<Service>();
- void add_service(io_service& ios, Service* svc)
- if (&ios != &svc->io_service())
- boost::throw_exception(invalid_service_owner());
- if (!ios.service_registry_->template add_service<Service>(svc))
- boost::throw_exception(service_already_exists());
- bool has_service(io_service& ios)
- return ios.service_registry_->template has_service<Service>();
看起來是個集合容器,裡面的元素是服務(Service),服務有編号(id)。從 service_registry 的實作可以進一步了解到下面細節:
- 服務都是用一個類來實作的,唯一的接口要求是必須有 shutdown_service 接口
- 服務是延遲建立的,隻有第一次被使用的時候才建立
- 每種類型的服務在同一個 service_registry (也是同一個io_service)裡面最多隻有一個執行個體
- 當service_registry (也就是io_service)被銷毀(析構)時,服務會被 shutdown ,然後被銷毀
繼續分析每個服務,還可以看到服務有下列特性:
- 服務在構造後就開始運作
- 服務在 shutdown 後停止運作
- 服務通過句柄(implementation_type)對外暴露其功能
- 服務之間有依賴性
最後,總結出所有的服務大概分為三類:
第一類服務是底層系統服務,是對作業系統平台提供功能的封裝,有:
- detail::win_iocp_io_service
- detail::win_iocp_socket_service
- detail::task_io_service
- detail::reactive_socket_service
- detail::epoll_reactor
- detail::kqueue_reactor
- detail::dev_poll_reactor
- detail::select_reactor
- detail::resolver_service
中間四個都是 reactor,不能想象,所有的 reactor 應該有相同的對外服務接口。這裡的 task_io_service 和 reactive_socket_service 是對 reactor 的再封裝,是以上層的服務不會直接依賴 reactor,而是依賴 task_io_service 和 reactive_socket_service。
第二類服務是上層接口服務,面向具體的功能對象(Object),他們會針對運作平台選擇依賴對應的底層服務
- socket_acceptor_service(ip::basic_socket_acceptor -> ip::tcp::acceptor)
- stream_socket_service(ip::basic_stream_socket -> ip::tcp::socket)
- datagram_socket_service(ip::basic_datagram_socket -> ip::udp::socket)
- raw_socket_service(ip::basic_raw_socket -> ip::icmp::socket)
- deadline_timer_service(basic_deadline_timer -> deadline_timer)
- detail::deadline_timer_service
- ip::resolver_service(ip::basic_resolver -> ip::tcp::resolver, ip::udp::resolver, ip::icmp::resolver)
前四個 socket 相關的服務會在不同的平台,選擇依賴 win_iocp_socket_service 和 reactive_socket_service<xxx_reactor>,deadline_timer_service (具體實作在
detail::deadline_timer_service中)會選擇 win_iocp_io_service 和 task_io_service<xxx_reactor>,ip::resolver_service 沒得選擇,隻有依賴 detail::resolver_service。
第三類服務是一些特殊功能的服務,比如 detail::strand_service 等,他們對整體架構沒有影響。
雖然 boost asio 中提供了這麼多服務,但是上層應用并不會直接使用這些服務,服務通過句柄對外暴露其功能,而句柄被功能對象(Object)封裝,然後提供給上層應用使用。
這裡的功能對象(Object),就是我們在第二類服務後面的“()”裡面給出的類,每個對象都包含着一個對相應服務的C++引用,以及服務對外暴露的句柄。
至此,我們了解了 boost asio 中通過一系列的服務封裝了作業系統的底層功能,并且通過動态組裝的方式把這些服務管理起來。可以看出,boost asio 使用了一種相當簡單的方式,就解決了平台的多樣性,甚至于模式的多樣性;同時服務的動态加載和集中管理,為功能擴充提供了友善途徑;另外對象中隻包含句柄,提高了安全性。
再回過頭來看看,我們心中還有個疑問,那就是 boost asio 是怎麼實作前攝器(Proactor)模式的呢?其實前攝器(Proactor)的各個角色都是通過服務來表達的。
先看 windows 平台
Asynchronous Operation Processor 的功能是由 win_iocp_socket_service、resolver_service 完成,Proactor 功能由 win_iocp_io_service 完成,win_iocp_io_service 也包含 Asynchronous Event Demultiplexer 和 Completion Event Queue 的功能,不過其實是對 windows IOCP 的系統功能的封裝。
再看看 linux 平台
Asynchronous Operation Processor 的功能是由 reactive_socket_service、resolver_service 完成,Proactor、Asynchronous Event Demultiplexer 和 Completion Event Queue功能都是由 task_io_service 完成。
最後,io_service 其實代表了Proator 這一端,socket、timer、resolver 等等代表了 Initiator 這一端。這就是上層使用者角度看到的景象。