http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-
started-with-boostasio?pg=8
7. Networking basics: connectors and acceptors (TCP)
我們來學習boost的TCP網絡編程。之前的篇章已經介紹了網絡系統框架。我們只需要學習網絡API函數即可
我們首先學習如何同步的連接主機。我們的代碼作為客戶端運行,使用tcp::socket對象.tcp::socket對象針對不同協議有不同的socket類型.我們需要確認使用正確的對象。當我們連接一個遠端主機
,我們需要獲得遠端的地址。為了達到這個目標,我們使用tcp::resolver。
#include <boost/asio.hpp> #include <boost/shared_ptr.hpp> #include <boost/thread.hpp> #include <boost/thread/mutex.hpp> #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> #include <iostream> #include <string> boost::mutex global_stream_lock; void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Thread Start" << std::endl; global_stream_lock.unlock(); while( true ) { try { boost::system::error_code ec; io_service->run( ec ); if( ec ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Error: " << ec << std::endl; global_stream_lock.unlock(); } break; } catch( std::exception & ex ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Exception: " << ex.what() << std::endl; global_stream_lock.unlock(); } } global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Thread Finish" << std::endl; global_stream_lock.unlock(); } int main( int argc, char * argv[] ) { boost::shared_ptr< boost::asio::io_service > io_service( new boost::asio::io_service ); boost::shared_ptr< boost::asio::io_service::work > work( new boost::asio::io_service::work( *io_service ) ); boost::shared_ptr< boost::asio::io_service::strand > strand( new boost::asio::io_service::strand( *io_service ) ); global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Press [return] to exit." << std::endl; global_stream_lock.unlock(); boost::thread_group worker_threads; for( int x = 0; x < 2; ++x ) { worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) ); } boost::asio::ip::tcp::socket sock( *io_service ); try { boost::asio::ip::tcp::resolver resolver( *io_service ); boost::asio::ip::tcp::resolver::query query( "www.google.com", boost::lexical_cast< std::string >( 80 ) ); boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve( query ); boost::asio::ip::tcp::endpoint endpoint = *iterator; global_stream_lock.lock(); std::cout << "Connecting to: " << endpoint << std::endl; global_stream_lock.unlock(); sock.connect( endpoint ); } catch( std::exception & ex ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Exception: " << ex.what() << std::endl; global_stream_lock.unlock(); } std::cin.get(); boost::system::error_code ec; sock.shutdown( boost::asio::ip::tcp::socket::shutdown_both, ec ); sock.close( ec ); io_service->stop(); worker_threads.join_all(); return 0; }
這個例子簡單的開啟了一個連接。程序將返回它實際嘗試連接的端口和IP。如果我們開啟一個命令提示窗口運行 "netstat -n",我們會看見這個程序的TCP連接
例子中我們使用query對象去重用這段代碼。代碼更適用於數字而不是字符串,所以我們使用函數將端口從數字轉化為字符串.
有時候我們可能需要異步的去連接一個遠程主機.例如GUI程序通過一個按鈕開啟連接,但是我們不希望GUI界面在連接完成之前就凍結住。boost::asio提供了一個異步連接方式。
使用bind shared_ptr。
#include <boost/asio.hpp> #include <boost/shared_ptr.hpp> #include <boost/thread.hpp> #include <boost/thread/mutex.hpp> #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> #include <iostream> #include <string> boost::mutex global_stream_lock; void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Thread Start" << std::endl; global_stream_lock.unlock(); while( true ) { try { boost::system::error_code ec; io_service->run( ec ); if( ec ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Error: " << ec << std::endl; global_stream_lock.unlock(); } break; } catch( std::exception & ex ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Exception: " << ex.what() << std::endl; global_stream_lock.unlock(); } } global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Thread Finish" << std::endl; global_stream_lock.unlock(); } void OnConnect( const boost::system::error_code & ec, boost::shared_ptr< boost::asio::ip::tcp::socket > sock ) { if( ec ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Error: " << ec << std::endl; global_stream_lock.unlock(); } else { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Connected!" << std::endl; global_stream_lock.unlock(); } } int main( int argc, char * argv[] ) { boost::shared_ptr< boost::asio::io_service > io_service( new boost::asio::io_service ); boost::shared_ptr< boost::asio::io_service::work > work( new boost::asio::io_service::work( *io_service ) ); boost::shared_ptr< boost::asio::io_service::strand > strand( new boost::asio::io_service::strand( *io_service ) ); global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Press [return] to exit." << std::endl; global_stream_lock.unlock(); boost::thread_group worker_threads; for( int x = 0; x < 2; ++x ) { worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) ); } boost::shared_ptr< boost::asio::ip::tcp::socket > sock( new boost::asio::ip::tcp::socket( *io_service ) ); try { boost::asio::ip::tcp::resolver resolver( *io_service ); boost::asio::ip::tcp::resolver::query query( "www.google.com", boost::lexical_cast< std::string >( 80 ) ); boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve( query ); boost::asio::ip::tcp::endpoint endpoint = *iterator; global_stream_lock.lock(); std::cout << "Connecting to: " << endpoint << std::endl; global_stream_lock.unlock(); sock->async_connect( endpoint, boost::bind( OnConnect, _1, sock ) ); } catch( std::exception & ex ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Exception: " << ex.what() << std::endl; global_stream_lock.unlock(); } std::cin.get(); boost::system::error_code ec; sock->shutdown( boost::asio::ip::tcp::socket::shutdown_both, ec ); sock->close( ec ); io_service->stop(); worker_threads.join_all(); return 0; }
如果想傳遞boost::asio對象,我們一般使用shared_ptr智能指針.因為大多數對象是不能拷貝的non-copyable,並且我們確定對象在等待調用期間依然有效。
我們使用bind設置我們的自定義處理程序。
最后一個例子我們異步連接遠端地址
#include <boost/asio.hpp> #include <boost/shared_ptr.hpp> #include <boost/thread.hpp> #include <boost/thread/mutex.hpp> #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> #include <iostream> #include <string> boost::mutex global_stream_lock; void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Thread Start" << std::endl; global_stream_lock.unlock(); while( true ) { try { boost::system::error_code ec; io_service->run( ec ); if( ec ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Error: " << ec << std::endl; global_stream_lock.unlock(); } break; } catch( std::exception & ex ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Exception: " << ex.what() << std::endl; global_stream_lock.unlock(); } } global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Thread Finish" << std::endl; global_stream_lock.unlock(); } void OnAccept( const boost::system::error_code & ec, boost::shared_ptr< boost::asio::ip::tcp::socket > sock ) { if( ec ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Error: " << ec << std::endl; global_stream_lock.unlock(); } else { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Accepted!" << std::endl; global_stream_lock.unlock(); } } int main( int argc, char * argv[] ) { boost::shared_ptr< boost::asio::io_service > io_service( new boost::asio::io_service ); boost::shared_ptr< boost::asio::io_service::work > work( new boost::asio::io_service::work( *io_service ) ); boost::shared_ptr< boost::asio::io_service::strand > strand( new boost::asio::io_service::strand( *io_service ) ); global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Press [return] to exit." << std::endl; global_stream_lock.unlock(); boost::thread_group worker_threads; for( int x = 0; x < 2; ++x ) { worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) ); } boost::shared_ptr< boost::asio::ip::tcp::acceptor > acceptor( new boost::asio::ip::tcp::acceptor( *io_service ) ); boost::shared_ptr< boost::asio::ip::tcp::socket > sock( new boost::asio::ip::tcp::socket( *io_service ) ); try { boost::asio::ip::tcp::resolver resolver( *io_service ); boost::asio::ip::tcp::resolver::query query( "127.0.0.1", boost::lexical_cast< std::string >( 7777 ) ); boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve( query ); acceptor->open( endpoint.protocol() ); acceptor->set_option( boost::asio::ip::tcp::acceptor::reuse_address( false ) ); acceptor->bind( endpoint ); acceptor->listen( boost::asio::socket_base::max_connections ); acceptor->async_accept( *sock, boost::bind( OnAccept, _1, sock ) ); global_stream_lock.lock(); std::cout << "Listening on: " << endpoint << std::endl; global_stream_lock.unlock(); } catch( std::exception & ex ) { global_stream_lock.lock(); std::cout << "[" << boost::this_thread::get_id() << "] Exception: " << ex.what() << std::endl; global_stream_lock.unlock(); } std::cin.get(); boost::system::error_code ec; acceptor->close( ec ); sock->shutdown( boost::asio::ip::tcp::socket::shutdown_both, ec ); sock->close( ec ); io_service->stop(); worker_threads.join_all(); return 0; }
這個例子與上一個例子很類似。事實上僅僅有一點點改變。之前提到過,asio庫是一個很優秀的庫。我們學習他的一些組件,就能理解它的其他組件。
在端口7777上運行上面這段代碼,我們可以從命令行窗口運行命令"telnet localhost 7777",開啟一個到服務器的連接來激發代碼中的 OnAccept函數
然而服務器將不能再接收更多的連接,這是因為我們僅僅只呼叫了async_accept一次並且只有一個socket 對象。稍后我們將處理服務器的設計策略。我們僅僅只是需要開啟核心API。
例子中,我們使用錯誤變量來確認沒有異常發生。
討論完基本的連接和接收。下章節將討論socket的讀寫