boost::asio基本使用
一、Asio網絡庫
截止到C++17,C++標準庫都沒有加入網絡通信庫。實際項目網絡編程是非常常見的功能,直接使用操作系統API是低效率且不穩定的,比較好的方法是借助第三方成熟可靠的網絡庫。據我所知C++中目前比較有名的網絡庫有ACE、libevent和boost.Asio,這三個庫都是跨平臺的,各有特色,對於一般的應用來說,使用這些庫都是沒什麽問題。由於C++標準庫和boost庫的親緣關系,我經常使用boost中其它的庫,所以選擇了Asio庫。在網絡上搜索了大家對Asio的評價,發現Asio庫作為跨平臺的網絡庫還是相當優秀的。
Linux系統高效網絡I/O是epoll,windows系統高效的網絡I/O是iocp,epoll是一種同步I/O復用技術,iocp是異步I/O。同步I/O指內核通知應用程序數據有了,應用程序可以獲取了;異步I/O是內核負責讀數據,讀好後通知應用程序可以用了。所以一般來說異步I/O相比同步I/O的效率更高一些,但是Linux中異步I/O(aio)並不比epoll高效。為了使的Asio庫跨平臺,最終Asio選擇Linux系統在epoll的基礎上用iocp的思想的封裝一層,這在一定上損失了Linux平臺I/O的效率。總的來說Asio網絡通信庫是主動器模式(proactor),而libevent使用的是reactor模式。
二、反應器模式/主動器模式
反應器是比較容易理解的I/O模式,也是使用比較多的模式。主要內容來自於《IO設計模式:Reactor和Proactor對比》。
Reactor包含如下角色:
- Handle 句柄;用來標識socket連接或是打開文件;
- Synchronous Event Demultiplexer:同步事件多路分解器:由操作系統內核實現的一個函數;用於阻塞等待發生在句柄集合上的一個或多個事件;(如select/epoll;)
- Event Handler:事件處理接口
- Concrete Event HandlerA:實現應用程序所提供的特定事件處理邏輯;
- Reactor:反應器,定義一個接口,實現以下功能:
1)供應用程序註冊和刪除關註的事件句柄;
2)運行事件循環;
3)有就緒事件到來時,分發事件到之前註冊的回調函數上處理;
Proactor主動器模式包含如下角色
- Handle 句柄;用來標識socket連接或是打開文件;
- Asynchronous Operation Processor:異步操作處理器;負責執行異步操作,一般由操作系統內核實現;
- Asynchronous Operation:異步操作
- Completion Event Queue:完成事件隊列;異步操作完成的結果放到隊列中等待後續使用
- Proactor:主動器;為應用程序進程提供事件循環;從完成事件隊列中取出異步操作的結果,分發調用相應的後續處理邏輯;
- Completion Handler:完成事件接口;一般是由回調函數組成的接口;
- Concrete Completion Handler:完成事件處理邏輯;實現接口定義特定的應用處理邏輯;
主動和被動以主動寫為例,Reactor將handle放到select(),等待可寫就緒,然後調用write()寫入數據;寫完處理後續邏輯;Proactor調用aoi_write後立刻返回,由內核負責寫操作,寫完後調用相應的回調函數處理後續邏輯;可以看出,Reactor被動的等待指示事件的到來並做出反應;它有一個等待的過程,做什麽都要先放入到監聽事件集合中等待handler可用時再進行操作;Proactor直接調用異步讀寫操作,調用完後立刻返回。
三、基本使用
1.基本的同步客戶端架構
1 using boost::asio; 2 io_service service; 3 ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 2001); 4 ip::tcp::socket sock(service); 5 sock.connect(ep);
2.基本的同步服務器架構
1 typedef boost::shared_ptr<ip::tcp::socket> socket_ptr; 2 io_service service; 3 ip::tcp::endpoint ep( ip::tcp::v4(), 2001)); // listen on 2001 4 ip::tcp::acceptor acc(service, ep); 5 while ( true) { 6 socket_ptr sock(new ip::tcp::socket(service)); 7 acc.accept(*sock); 8 boost::thread( boost::bind(client_session, sock)); 9 } 10 void client_session(socket_ptr sock) { 11 while ( true) { 12 char data[512]; 13 size_t len = sock->read_some(buffer(data)); 14 if ( len > 0) 15 write(*sock, buffer("ok", 2)); 16 }
3.基本的異步客戶端架構
1 using boost::asio; 2 io_service service; 3 ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 2001); 4 ip::tcp::socket sock(service); 5 sock.async_connect(ep, connect_handler); 6 service.run(); 7 void connect_handler(const boost::system::error_code & ec) { 8 // 如果ec返回成功我們就可以知道連接成功了 9 }
4.基本的異步服務器架構
1 using boost::asio; 2 typedef boost::shared_ptr<ip::tcp::socket> socket_ptr; 3 io_service service; 4 ip::tcp::endpoint ep( ip::tcp::v4(), 2001)); // 監聽端口2001 5 ip::tcp::acceptor acc(service, ep); 6 socket_ptr sock(new ip::tcp::socket(service)); 7 start_accept(sock); 8 service.run(); 9 void start_accept(socket_ptr sock) { 10 acc.async_accept(*sock, boost::bind( handle_accept, sock, _1) ); 11 } 12 void handle_accept(socket_ptr sock, const boost::system::error_code & 13 err) { 14 if ( err) return; 15 // 從這裏開始, 你可以從socket讀取或者寫入 16 socket_ptr sock(new ip::tcp::socket(service)); 17 start_accept(sock); 18 }
參考:IO設計模式:Reactor和Proactor對比
《Boost.Asio C++網絡編程》
boost::asio基本使用