以前使用ACE實現Server框架,但是覺得太笨重,決定採用boost.asio來寫服務器程序:
1.服務器構建在linux上面;當然也可以在windows下運行
2.io部分採用非阻塞模式、業務邏輯部分採用同步線程池實現
3.封裝io操作及狀態,用戶應用程序無需關心io詳細操作
所以決定採用boost::asio框架來寫服務器:
boost::asio::io_service提供了核心IO功能、和異步IO對象,它包括:
boost::asio::ip::tcp::socket
boost::asio::ip::tcp::acceptor
boost::asio::ip::udp::socket
boost::asio::deadline_timer
io_service支持線程安全、共享對象安全;調用run()函數未完成時會引發reset();
boost.asio異步方式的函數前面都加有async_前綴,函數參數中會要求放入一個回調函數(或仿函數);異步操作執行完後無論有沒有完成都會立即返回,這時候可以處理其他事情,等到回調函數被調用就說明異步操作已經完畢。
boost.asio的很多回調函數值接收boost::system::error_code參數,在實際使用中是不夠的,所以一般的仿函數都會攜帶一堆數據作爲回調,或使用boost::bind來綁定一堆數據。
只有boost.asio.run()運行後回調對象纔會被調用,否則即使系統已經完成了異步操作也不會有任何動作!
//下面是一個異步模式的簡單的Tcp echo服務器 #include <iostream> #include <string> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/smart_ptr.hpp> using namespace boost::asio; using boost::system::error_code; using ip::tcp; struct CHelloWorld_Service { //類的初始化創建:設置io_service, 設置1000端口 CHelloWorld_Service(io_service &iosev) :m_iosev(iosev),m_acceptor(iosev, tcp::endpoint(tcp::v4(), 1000)) { } //創建一個tcp的socket;且還是偵聽 void start() { // 開始等待連接(非阻塞) boost::shared_ptr<tcp::socket> psocket(new tcp::socket(m_iosev)); // 觸發的事件只有error_code參數,所以用boost::bind把socket綁定進去 m_acceptor.async_accept(*psocket, boost::bind(&CHelloWorld_Service::accept_handler, this, psocket, _1) ); } // 有客戶端連接時accept_handler觸發 void accept_handler(boost::shared_ptr<tcp::socket> psocket, error_code ec) { if(ec) return; // 繼續等待連接 start(); // 顯示遠程IP std::cout << psocket->remote_endpoint().address() << std::endl; // 發送信息(非阻塞) boost::shared_ptr<std::string> pstr(new std::string("hello async world!")); psocket->async_write_some(buffer(*pstr), boost::bind(&CHelloWorld_Service::write_handler, this, pstr, _1, _2) ); } // 異步寫操作完成後write_handler觸發 void write_handler(boost::shared_ptr<std::string> pstr, error_code ec, size_t bytes_transferred) { if(ec) std::cout<< "發送失敗!" << std::endl; else std::cout<< *pstr << " 已發送" << std::endl; } private: io_service &m_iosev; ip::tcp::acceptor m_acceptor; }; int main(int argc, char* argv[]) { //建立io服務器 io_service iosev; CHelloWorld_Service sev(iosev); //開始偵聽socket的連接;和開始接收遠程數據 sev.start(); //開始執行回調函數 iosev.run(); return 0; }
例子分析:
1.調用sev.start()開始接受客戶端連接。async_accept()其實就是註冊了一個回調函數;所以它會立即返回。
2.iosev.run()方法是一個循環,負責分發異步回調函數,只有當所有的異步操作執行完後纔會返回。
3.爲了保證start()中的m_accptor.async_accept操作所用的socket在整個異步操作期間都是有效的,而且以後所有的客戶端連接進來後該socket都是有效地,這裏的解決辦法是使用一個帶計數的智能指針,shared_ptr,並將該指針綁定到回調函數上。該智能指針的生存週期等同於sev的生存週期。
4.一旦有客戶端連接,回調函數accept_handler()就會執行,在該函數中首先調用start()繼續異步等待其他客戶端連接;然後使用start()綁定進來的socket進行接收遠程客戶端的連接
5.例子程序中發送數據也使用了異步模式async_write_some,同樣需要保證整個異步發送期間緩衝區的有效性,所以使用了shared_ptr<string>參數
6.對於客戶端connect, read_some前面也可加入async_前綴,按照異步方式執行;