Boost.Asio C++ 網路程式設計之七:基於TCP的同步客戶端
阿新 • • 發佈:2019-01-17
從本篇開始,我們會深入學習怎樣使用Boost.Asio建立更加複雜的客戶端和服務端應用。你可以執行並測試它們,而且在理解之後,你可以把它們做為框架來構造自己的應用。
在接下來的例子中:
1.客戶端使用一個使用者名稱(無密碼)登入到服務端
2.所有的連線由客戶端建立,當客戶端請求時服務端迴應
3.所有的請求和回覆都以換行符結尾(’\n’)
4.對於5秒鐘沒有ping操作的客戶端,服務端會自動斷開其連線
客戶端可以傳送如下請求:
1.獲得所有已連線客戶端的列表
2.客戶端可以ping,當它ping時,服務端返回ping ok或者pingclient_list_chaned
為了更有趣一點,我們增加了一些難度:
1.每個客戶端登入6個使用者連線,比如Johon,James,Lucy,Tracy,Frank和Abby
在接下來的例子中:
1.客戶端使用一個使用者名稱(無密碼)登入到服務端
2.所有的連線由客戶端建立,當客戶端請求時服務端迴應
3.所有的請求和回覆都以換行符結尾(’\n’)
4.對於5秒鐘沒有ping操作的客戶端,服務端會自動斷開其連線
客戶端可以傳送如下請求:
1.獲得所有已連線客戶端的列表
2.客戶端可以ping,當它ping時,服務端返回ping ok或者pingclient_list_chaned
為了更有趣一點,我們增加了一些難度:
1.每個客戶端登入6個使用者連線,比如Johon,James,Lucy,Tracy,Frank和Abby
2.每個客戶端連線隨機地ping服務端(隨機7秒;這樣的話,服務端會時不時關閉一個連線)
基於TCP的同步客戶端
1.流程圖
2.實現
#ifdef WIN32 #define _WIN32_WINNT 0x0501 #include <stdio.h> #endif #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/asio.hpp> #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> using namespace boost::asio; io_service service; /** simple connection to server: - logs in just with username (no password) - all connections are initiated by the client: client asks, server answers - server disconnects any client that hasn't pinged for 5 seconds Possible requests: - gets a list of all connected clients - ping: the server answers either with "ping ok" or "ping client_list_changed" */ struct talk_to_svr { talk_to_svr(const std::string & username) : sock_(service), started_(true), username_(username) {} void connect(ip::tcp::endpoint ep) { sock_.connect(ep); } void loop() { // read answer to our login write("login " + username_ + "\n"); read_answer(); while (started_) { // 迴圈傳送ping請求 write_request(); read_answer(); int millis = rand() % 7000; std::cout << username_ << " postpone ping " << millis << " ms" << std::endl; boost::this_thread::sleep(boost::posix_time::millisec(millis)); } } std::string username() const { return username_; } private: void write_request() { write("ping\n"); } void read_answer() { already_read_ = 0; read(sock_, buffer(buff_), boost::bind(&talk_to_svr::read_complete, this, _1, _2)); process_msg(); } void process_msg() { std::string msg(buff_, already_read_); if (msg.find("login ") == 0) on_login(); else if (msg.find("ping") == 0) on_ping(msg); else if (msg.find("clients ") == 0) on_clients(msg); else std::cerr << "invalid msg " << msg << std::endl; } void on_login() { std::cout << username_ << " logged in" << std::endl; do_ask_clients(); } void on_ping(const std::string & msg) { std::istringstream in(msg); std::string answer; in >> answer >> answer; if (answer == "client_list_changed") do_ask_clients(); } void on_clients(const std::string & msg) { std::string clients = msg.substr(8); std::cout << username_ << ", new client list:" << clients; } // 獲得所有已連線客戶端的列表 void do_ask_clients() { write("ask_clients\n"); read_answer(); } void write(const std::string & msg) { sock_.write_some(buffer(msg)); } size_t read_complete(const boost::system::error_code & err, size_t bytes) { if (err) return 0; already_read_ = bytes; bool found = std::find(buff_, buff_ + bytes, '\n') < buff_ + bytes; return found ? 0 : 1; } private: ip::tcp::socket sock_; enum { max_msg = 1024 }; int already_read_; char buff_[max_msg]; bool started_; std::string username_; }; ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 8001); void run_client(const std::string & client_name) { talk_to_svr client(client_name); try { client.connect(ep); client.loop(); } catch (boost::system::system_error & err) { // 捕獲socket斷開原因 std::cout << "client terminated " << client.username() << "——" << err.what() << std::endl; } } int main(int argc, char* argv[]) { boost::thread_group threads; char* names[] = { "John", "James", "Lucy", "Tracy", "Frank", "Abby", 0 }; for (char ** name = names; *name; ++name) { threads.create_thread(boost::bind(run_client, *name)); boost::this_thread::sleep(boost::posix_time::millisec(100)); } threads.join_all(); system("pause"); }