[譯]客戶端和服務(wù)端

2018-06-19 15:33 更新

客戶端和服務(wù)端

在這一章節(jié),我們會(huì)深入學(xué)習(xí)怎樣使用Boost.Asio建立非凡的客戶端和服務(wù)端應(yīng)用。你可以運(yùn)行并測(cè)試它們,而且在理解之后,你可以把它們做為框架來(lái)構(gòu)造自己的應(yīng)用。

在接下來(lái)的例子中:

  • 客戶端使用一個(gè)用戶名(無(wú)密碼)登錄到服務(wù)端
  • 所有的連接由客戶端建立,當(dāng)客戶端請(qǐng)求時(shí)服務(wù)端回應(yīng)
  • 所有的請(qǐng)求和回復(fù)都以換行符結(jié)尾(’\n’)
  • 對(duì)于5秒鐘沒(méi)有ping操作的客戶端,服務(wù)端會(huì)自動(dòng)斷開其連接

客戶端可以發(fā)送如下請(qǐng)求:

  • 獲得所有已連接客戶端的列表
  • 客戶端可以ping,當(dāng)它ping時(shí),服務(wù)端返回ping ok或者ping client_list_chaned(在接下來(lái)的例子中,客戶端重新請(qǐng)求已連接的客戶端列表)

為了更有趣一點(diǎn),我們?cè)黾恿艘恍╇y度:

  • 每個(gè)客戶端登錄6個(gè)用戶連接,比如Johon,James,Lucy,Tracy,Frank和Abby
  • 每個(gè)客戶端連接隨機(jī)地ping服務(wù)端(隨機(jī)7秒;這樣的話,服務(wù)端會(huì)時(shí)不時(shí)關(guān)閉一個(gè)連接)

同步客戶端/服務(wù)端

首先,我們會(huì)實(shí)現(xiàn)同步應(yīng)用。你會(huì)發(fā)現(xiàn)它的代碼很直接而且易讀的。而且因?yàn)樗械木W(wǎng)絡(luò)調(diào)用都是阻塞的,所以它不需要獨(dú)立的線程。

同步客戶端

同步客戶端會(huì)以你所期望的串行方式運(yùn)行;連接到服務(wù)端,登錄服務(wù)器,然后執(zhí)行連接循環(huán),比如休眠一下,發(fā)起一個(gè)請(qǐng)求,讀取服務(wù)端返回,然后再休眠一會(huì),然后一直循環(huán)下去……

這里寫圖片描述

因?yàn)槲覀兪峭降?,所以我們讓事情變得?jiǎn)單一點(diǎn)。首先,連接到服務(wù)器,然后再循環(huán),如下:

  1. ip::tcp::endpoint ep( ip::address::from_string("127.0.0.1"), 8001);
  2. void run_client(const std::string & client_name) {
  3. talk_to_svr client(client_name);
  4. try {
  5. client.connect(ep);
  6. client.loop();
  7. } catch(boost::system::system_error & err) {
  8. std::cout << "client terminated " << std::endl;
  9. }
  10. }

下面的代碼片段展示了talk_to_svr類:

  1. struct talk_to_svr {
  2. talk_to_svr(const std::string & username) : sock_(service), started_(true), username_(username) {}
  3. void connect(ip::tcp::endpoint ep) {
  4. sock_.connect(ep);
  5. }
  6. void loop() {
  7. write("login " + username_ + "\n");
  8. read_answer();
  9. while ( started_) {
  10. write_request();
  11. read_answer();
  12. boost::this_thread::sleep(millisec(rand() % 7000));
  13. }
  14. }
  15. std::string username() const { return username_; }
  16. ...
  17. private:
  18. ip::tcp::socket sock_;
  19. enum { max_msg = 1024 };
  20. int already_read_;
  21. char buff_[max_msg];
  22. bool started_;
  23. std::string username_;
  24. };

在這個(gè)循環(huán)中,我們僅僅填充1個(gè)比特,做一個(gè)ping操作之后就進(jìn)入睡眠狀態(tài),之后再讀取服務(wù)端的返回。我們的睡眠是隨機(jī)的(有時(shí)候超過(guò)5秒),這樣服務(wù)端就有可能在某個(gè)時(shí)間點(diǎn)斷開我們的連接:

  1. void write_request() {
  2. write("ping\n");
  3. }
  4. void read_answer() {
  5. already_read_ = 0;
  6. read(sock_, buffer(buff_), boost::bind(&talk_to_svr::read_complete, this, _1, _2));
  7. process_msg();
  8. }
  9. void process_msg() {
  10. std::string msg(buff_, already_read_);
  11. if ( msg.find("login ") == 0) on_login();
  12. else if ( msg.find("ping") == 0) on_ping(msg);
  13. else if ( msg.find("clients ") == 0) on_clients(msg);
  14. else std::cerr << "invalid msg " << msg << std::endl;
  15. }

對(duì)于讀取結(jié)果,我們使用在之前章節(jié)就有說(shuō)到的read_complete來(lái)保證我們能讀到換行符(’\n’)。這段邏輯在process_msg()中,在這里我們讀取服務(wù)端的返回,然后分發(fā)到正確的方法去處理:

  1. void on_login() { do_ask_clients(); }
  2. void on_ping(const std::string & msg) {
  3. std::istringstream in(msg);
  4. std::string answer;
  5. in >> answer >> answer;
  6. if ( answer == "client_list_changed")
  7. do_ask_clients();
  8. }
  9. void on_clients(const std::string & msg) {
  10. std::string clients = msg.substr(8);
  11. std::cout << username_ << ", new client list:" << clients;
  12. }
  13. void do_ask_clients() {
  14. write("ask_clients\n");
  15. read_answer();
  16. }
  17. void write(const std::string & msg) { sock_.write_some(buffer(msg)); }
  18. size_t read_complete(const boost::system::error_code & err, size_t bytes) {
  19. // ... 和之前一樣
  20. }

在讀取服務(wù)端對(duì)我們ping操作的返回時(shí),如果得到的消息是client_list_changed,我們就需要重新請(qǐng)求客戶端列表。

同步服務(wù)端

同步服務(wù)端也是相當(dāng)簡(jiǎn)單的。它只需要兩個(gè)線程,一個(gè)負(fù)責(zé)接收新的客戶端連接,另外一個(gè)負(fù)責(zé)處理已經(jīng)存在的客戶端請(qǐng)求。它不能使用單線程,因?yàn)榈却碌目蛻舳诉B接是一個(gè)阻塞操作,所以我們需要另外一個(gè)線程來(lái)處理已經(jīng)存在的客戶端請(qǐng)求。

這里寫圖片描述

正常來(lái)說(shuō)服務(wù)端都比客戶端要難實(shí)現(xiàn)。一方面,它要管理所有已經(jīng)連接的客戶端。因?yàn)槲覀兪峭降?,所以我們需要至少兩個(gè)線程,一個(gè)負(fù)責(zé)接受新的客戶端連接(因?yàn)閍ccept()是阻塞的)而另一個(gè)負(fù)責(zé)回復(fù)已經(jīng)存在的客戶端。

  1. void accept_thread() {
  2. ip::tcp::acceptor acceptor(service,ip::tcp::endpoint(ip::tcp::v4(), 8001));
  3. while ( true) {
  4. client_ptr new_( new talk_to_client);
  5. acceptor.accept(new_->sock());
  6. boost::recursive_mutex::scoped_lock lk(cs);
  7. clients.push_back(new_);
  8. }
  9. }
  10. void handle_clients_thread() {
  11. while ( true) {
  12. boost::this_thread::sleep( millisec(1));
  13. boost::recursive_mutex::scoped_lock lk(cs);
  14. for(array::iterator b = clients.begin(), e = clients.end(); b!= e; ++b)
  15. (*b)->answer_to_client();
  16. // 刪除已經(jīng)超時(shí)的客戶端
  17. clients.erase(std::remove_if(clients.begin(), clients.end(), boost::bind(&talk_to_client::timed_out,_1)), clients.end());
  18. }
  19. }
  20. int main(int argc, char* argv[]) {
  21. boost::thread_group threads;
  22. threads.create_thread(accept_thread);
  23. threads.create_thread(handle_clients_thread);
  24. threads.join_all();
  25. }

為了分辨客戶端發(fā)送過(guò)來(lái)的請(qǐng)求我們需要保存一個(gè)客戶端的列表。

每個(gè)talk_to_client實(shí)例都擁有一個(gè)socket,socket類是不支持拷貝構(gòu)造的,所以如果你想要把它們保存在一個(gè)std::vector對(duì)象中,你需要一個(gè)指向它的智能指針。這里有兩種實(shí)現(xiàn)的方式:在talk_to_client內(nèi)部保存一個(gè)指向socket的智能指針然后創(chuàng)建一個(gè)talk_to_client實(shí)例的數(shù)組,或者讓talk_to_client實(shí)例用變量的方式保存socket,然后創(chuàng)建一個(gè)指向talk_to_client智能指針的數(shù)組。我選擇后者,但是你也可以選前面的方式:

  1. typedef boost::shared_ptr<talk_to_client> client_ptr;
  2. typedef std::vector<client_ptr> array;
  3. array clients;
  4. boost::recursive_mutex cs; // 用線程安全的方式訪問(wèn)客戶端數(shù)組

talk_to_client的主要代碼如下:

  1. struct talk_to_client : boost::enable_shared_from_this<talk_to_client>
  2. {
  3. talk_to_client() { ... }
  4. std::string username() const { return username_; }
  5. void answer_to_client() {
  6. try {
  7. read_request();
  8. process_request();
  9. } catch ( boost::system::system_error&) { stop(); }
  10. if ( timed_out())
  11. stop();
  12. }
  13. void set_clients_changed() { clients_changed_ = true; }
  14. ip::tcp::socket & sock() { return sock_; }
  15. bool timed_out() const {
  16. ptime now = microsec_clock::local_time();
  17. long long ms = (now - last_ping).total_milliseconds();
  18. return ms > 5000 ;
  19. }
  20. void stop() {
  21. boost::system::error_code err; sock_.close(err);
  22. }
  23. void read_request() {
  24. if ( sock_.available())
  25. already_read_ += sock_.read_some(buffer(buff_ + already_read_, max_msg - already_read_));
  26. }
  27. ...
  28. private:
  29. // ... 和同步客戶端中的一樣
  30. bool clients_changed_;
  31. ptime last_ping;
  32. };

上述代碼擁有非常好的自釋能力。其中最重要的方法是read_request()。它只在存在有效數(shù)據(jù)的情況才讀取,這樣的話,服務(wù)端永遠(yuǎn)都不會(huì)阻塞:

  1. void process_request() {
  2. bool found_enter = std::find(buff_, buff_ + already_read_, '\n') < buff_ + already_read_;
  3. if ( !found_enter)
  4. return; // 消息不完整
  5. // 處理消息
  6. last_ping = microsec_clock::local_time();
  7. size_t pos = std::find(buff_, buff_ + already_read_, '\n') - buff_;
  8. std::string msg(buff_, pos);
  9. std::copy(buff_ + already_read_, buff_ + max_msg, buff_);
  10. already_read_ -= pos + 1;
  11. if ( msg.find("login ") == 0) on_login(msg);
  12. else if ( msg.find("ping") == 0) on_ping();
  13. else if ( msg.find("ask_clients") == 0) on_clients();
  14. else std::cerr << "invalid msg " << msg << std::endl;
  15. }
  16. void on_login(const std::string & msg) {
  17. std::istringstream in(msg);
  18. in >> username_ >> username_;
  19. write("login ok\n");
  20. update_clients_changed();
  21. }
  22. void on_ping() {
  23. write(clients_changed_ ? "ping client_list_changed\n" : "ping ok\n");
  24. clients_changed_ = false;
  25. }
  26. void on_clients() {
  27. std::string msg;
  28. { boost::recursive_mutex::scoped_lock lk(cs);
  29. for( array::const_iterator b = clients.begin(), e = clients.end() ; b != e; ++b)
  30. msg += (*b)->username() + " ";
  31. }
  32. write("clients " + msg + "\n");
  33. }
  34. void write(const std::string & msg){sock_.write_some(buffer(msg)); }

觀察process_request()。當(dāng)我們讀取到足夠多有效的數(shù)據(jù)時(shí),我們需要知道我們是否已經(jīng)讀取到整個(gè)消息(如果found_enter為真)。這樣做的話,我們可以使我們避免一次讀多個(gè)消息的可能(’\n’之后的消息也被保存到緩沖區(qū)中),然后我們解析讀取到的整個(gè)消息。剩下的代碼都是很容易讀懂的。

異步客戶端/服務(wù)端

現(xiàn)在,是比較有趣(也比較難)的異步實(shí)現(xiàn)! 當(dāng)查看示意圖時(shí),你需要知道Boost.Asio代表由Boost.Asio執(zhí)行的一個(gè)異步調(diào)用。例如do_read(),Boost.Asio和on_read()代表了從do_read()on_read()的邏輯流程,但是你永遠(yuǎn)不知道什么時(shí)候輪到on_read()被調(diào)用,你只是知道你最終會(huì)調(diào)用它。

異步客戶端

到這里事情會(huì)變得有點(diǎn)復(fù)雜,但是仍然是可控的。當(dāng)然你也會(huì)擁有一個(gè)不會(huì)阻塞的應(yīng)用。

這里寫圖片描述

下面的代碼你應(yīng)該已經(jīng)很熟悉:

  1. #define MEM_FN(x) boost::bind(&self_type::x, shared_from_this())
  2. #define MEM_FN1(x,y) boost::bind(&self_type::x, shared_from_
  3. this(),y)
  4. #define MEM_FN2(x,y,z) boost::bind(&self_type::x, shared_from_
  5. this(),y,z)
  6. class talk_to_svr : public boost::enable_shared_from_this<talk_to_svr>, boost::noncopyable {
  7. typedef talk_to_svr self_type;
  8. talk_to_svr(const std::string & username) : sock_(service), started_(true), username_(username), timer_
  9. (service) {}
  10. void start(ip::tcp::endpoint ep) {
  11. sock_.async_connect(ep, MEM_FN1(on_connect,_1));
  12. }
  13. public:
  14. typedef boost::system::error_code error_code;
  15. typedef boost::shared_ptr<talk_to_svr> ptr;
  16. static ptr start(ip::tcp::endpoint ep, const std::string & username) {
  17. ptr new_(new talk_to_svr(username));
  18. new_->start(ep);
  19. return new_;
  20. }
  21. void stop() {
  22. if ( !started_) return;
  23. started_ = false;
  24. sock_.close();
  25. }
  26. bool started() { return started_; }
  27. ...
  28. private:
  29. size_t read_complete(const boost::system::error_code &err, size_t bytes) {
  30. if ( err) return 0;
  31. bool found = std::find(read_buffer_, read_buffer_ + bytes, '\n') < read_buffer_ + bytes;
  32. return found ? 0 : 1;
  33. }
  34. private:
  35. ip::tcp::socket sock_;
  36. enum { max_msg = 1024 };
  37. char read_buffer_[max_msg];
  38. char write_buffer_[max_msg];
  39. bool started_;
  40. std::string username_;
  41. deadline_timer timer_;
  42. };

你會(huì)看到額外還有一個(gè)叫deadlinetimer timer的方法用來(lái)ping服務(wù)端;而且ping操作同樣是隨機(jī)的。

下面是類的邏輯:

  1. void on_connect(const error_code & err) {
  2. if ( !err) do_write("login " + username_ + "\n");
  3. else stop();
  4. }
  5. void on_read(const error_code & err, size_t bytes) {
  6. if ( err) stop();
  7. if ( !started() ) return;
  8. // 處理消息
  9. std::string msg(read_buffer_, bytes);
  10. if ( msg.find("login ") == 0) on_login();
  11. else if ( msg.find("ping") == 0) on_ping(msg);
  12. else if ( msg.find("clients ") == 0) on_clients(msg);
  13. }
  14. void on_login() {
  15. do_ask_clients();
  16. }
  17. void on_ping(const std::string & msg) {
  18. std::istringstream in(msg);
  19. std::string answer;
  20. in >> answer >> answer;
  21. if ( answer == "client_list_changed") do_ask_clients();
  22. else postpone_ping();
  23. }
  24. void on_clients(const std::string & msg) {
  25. std::string clients = msg.substr(8);
  26. std::cout << username_ << ", new client list:" << clients ;
  27. postpone_ping();
  28. }

on_read()中,首先的兩行代碼是亮點(diǎn)。在第一行,如果出現(xiàn)錯(cuò)誤,我們就停止。而第二行,如果我們已經(jīng)停止了(之前就停止了或者剛好停止),我們就返回。反之如果所有都是OK,我們就對(duì)收到的消息進(jìn)行處理。

最后是*do_**方法,實(shí)現(xiàn)如下:

  1. void do_ping() { do_write("ping\n"); }
  2. void postpone_ping() {
  3. timer_.expires_from_now(boost::posix_time::millisec(rand() % 7000));
  4. timer_.async_wait( MEM_FN(do_ping));
  5. }
  6. void do_ask_clients() { do_write("ask_clients\n"); }
  7. void on_write(const error_code & err, size_t bytes) { do_read(); }
  8. void do_read() {
  9. async_read(sock_, buffer(read_buffer_), MEM_FN2(read_complete,_1,_2), MEM_FN2(on_read,_1,_2));
  10. }
  11. void do_write(const std::string & msg) {
  12. if ( !started() ) return;
  13. std::copy(msg.begin(), msg.end(), write_buffer_);
  14. sock_.async_write_some( buffer(write_buffer_, msg.size()), MEM_FN2(on_write,_1,_2));

注意每一個(gè)read操作都會(huì)觸發(fā)一個(gè)ping操作

  • 當(dāng)read操作結(jié)束時(shí),on_read()被調(diào)用
  • on_read()調(diào)用on_login(),on_ping()或者on_clients()
  • 每一個(gè)方法要么發(fā)出一個(gè)ping,要么請(qǐng)求客戶端列表
  • 如果我們請(qǐng)求客戶端列表,當(dāng)read操作接收到它們時(shí),它會(huì)發(fā)出一個(gè)ping操作。

異步服務(wù)端

這個(gè)示意圖是相當(dāng)復(fù)雜的;從Boost.Asio出來(lái)你可以看到4個(gè)箭頭指向on_accept,on_read,on_writeon_check_ping。這也就意味著你永遠(yuǎn)不知道哪個(gè)異步調(diào)用是下一個(gè)完成的調(diào)用,但是你可以確定的是它是這4個(gè)操作中的一個(gè)。

這里寫圖片描述

現(xiàn)在,我們是異步的了;我們可以繼續(xù)保持單線程。接受客戶端連接是最簡(jiǎn)單的部分,如下所示:

  1. ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(), 8001));
  2. void handle_accept(talk_to_client::ptr client, const error_code & err)
  3. {
  4. client->start();
  5. talk_to_client::ptr new_client = talk_to_client::new_();
  6. acceptor.async_accept(new_client->sock(), boost::bind(handle_accept,new_client,_1));
  7. }
  8. int main(int argc, char* argv[]) {
  9. talk_to_client::ptr client = talk_to_client::new_();
  10. acceptor.async_accept(client->sock(),boost::bind(handle_accept,client,_1));
  11. service.run();
  12. }

上述代碼會(huì)一直異步地等待一個(gè)新的客戶端連接(每個(gè)新的客戶端連接會(huì)觸發(fā)另外一個(gè)異步等待操作)。

我們需要監(jiān)控client list changed事件(一個(gè)新客戶端連接或者一個(gè)客戶端斷開連接),然后當(dāng)事件發(fā)生時(shí)通知所有的客戶端。因此,我們需要保存一個(gè)客戶端連接的數(shù)組,否則除非你不需要在某一時(shí)刻知道所有連接的客戶端,你才不需要這樣一個(gè)數(shù)組。

  1. class talk_to_client;
  2. typedef boost::shared_ptr<talk_to_client>client_ptr;
  3. typedef std::vector<client_ptr> array;
  4. array clients;

connection類的框架如下:

  1. class talk_to_client : public boost::enable_shared_from_this<talk_to_client> , boost::noncopyable {
  2. talk_to_client() { ... }
  3. public:
  4. typedef boost::system::error_code error_code;
  5. typedef boost::shared_ptr<talk_to_client> ptr;
  6. void start() {
  7. started_ = true;
  8. clients.push_back( shared_from_this());
  9. last_ping = boost::posix_time::microsec_clock::local_time();
  10. do_read(); //首先,我們等待客戶端連接
  11. }
  12. static ptr new_() { ptr new_(new talk_to_client); return new_; }
  13. void stop() {
  14. if ( !started_) return;
  15. started_ = false;
  16. sock_.close();
  17. ptr self = shared_from_this();
  18. array::iterator it = std::find(clients.begin(), clients.end(), self);
  19. clients.erase(it);
  20. update_clients_changed();
  21. }
  22. bool started() const { return started_; }
  23. ip::tcp::socket & sock() { return sock_;}
  24. std::string username() const { return username_; }
  25. void set_clients_changed() { clients_changed_ = true; }
  26. private:
  27. ip::tcp::socket sock_;
  28. enum { max_msg = 1024 };
  29. char read_buffer_[max_msg];
  30. char write_buffer_[max_msg];
  31. bool started_;
  32. std::string username_;
  33. deadline_timer timer_;
  34. boost::posix_time::ptime last_ping;
  35. bool clients_changed_;
  36. };

我會(huì)用talk_to_client或者talk_to_server來(lái)調(diào)用connection類,從而讓你更明白我所說(shuō)的內(nèi)容。

現(xiàn)在你需要用到之前的代碼了;它和我們?cè)诳蛻舳藨?yīng)用中所用到的是一樣的。我們還有另外一個(gè)stop()方法,這個(gè)方法用來(lái)從客戶端數(shù)組中移除一個(gè)客戶端連接。

服務(wù)端持續(xù)不斷地等待異步的read操作:

  1. void on_read(const error_code & err, size_t bytes) {
  2. if ( err) stop();
  3. if ( !started() ) return;
  4. std::string msg(read_buffer_, bytes);
  5. if ( msg.find("login ") == 0) on_login(msg);
  6. else if ( msg.find("ping") == 0) on_ping();
  7. else if ( msg.find("ask_clients") == 0) on_clients();
  8. }
  9. void on_login(const std::string & msg) {
  10. std::istringstream in(msg);
  11. in >> username_ >> username_;
  12. do_write("login ok\n");
  13. update_clients_changed();
  14. }
  15. void on_ping() {
  16. do_write(clients_changed_ ? "ping client_list_changed\n" : "ping ok\n");
  17. clients_changed_ = false;
  18. }
  19. void on_clients() {
  20. std::string msg;
  21. for(array::const_iterator b =clients.begin(),e =clients.end(); b != e; ++b)
  22. msg += (*b)->username() + " ";
  23. do_write("clients " + msg + "\n");
  24. }

這段代碼是簡(jiǎn)單易懂的;需要注意的一點(diǎn)是:當(dāng)一個(gè)新客戶端登錄,我們調(diào)用update_clients_changed(),這個(gè)方法為所有客戶端將clientschanged標(biāo)志為true。

服務(wù)端每收到一個(gè)請(qǐng)求就用相應(yīng)的方式進(jìn)行回復(fù),如下所示:

  1. void do_ping() { do_write("ping\n"); }
  2. void do_ask_clients() { do_write("ask_clients\n"); }
  3. void on_write(const error_code & err, size_t bytes) { do_read(); }
  4. void do_read() {
  5. async_read(sock_, buffer(read_buffer_), MEM_FN2(read_complete,_1,_2), MEM_FN2(on_read,_1,_2));
  6. post_check_ping();
  7. }
  8. void do_write(const std::string & msg) {
  9. if ( !started() ) return;
  10. std::copy(msg.begin(), msg.end(), write_buffer_);
  11. sock_.async_write_some( buffer(write_buffer_, msg.size()), MEM_FN2(on_write,_1,_2));
  12. }
  13. size_t read_complete(const boost::system::error_code & err, size_t bytes) {
  14. // ... 就像之前
  15. }

在每個(gè)write操作的末尾,on_write()方法被調(diào)用,這個(gè)方法會(huì)觸發(fā)另外一個(gè)異步讀操作,這樣的話“等待請(qǐng)求-回復(fù)請(qǐng)求”這個(gè)循環(huán)就會(huì)一直執(zhí)行,直到客戶端斷開連接或者超時(shí)。

在每次讀操作開始之前,我們異步等待5秒鐘來(lái)觀察客戶端是否超時(shí)。如果超時(shí),我們關(guān)閉它的連接:

  1. void on_check_ping() {
  2. ptime now = microsec_clock::local_time();
  3. if ( (now - last_ping).total_milliseconds() > 5000)
  4. stop();
  5. last_ping = boost::posix_time::microsec_clock::local_time();
  6. }
  7. void post_check_ping() {
  8. timer_.expires_from_now(boost::posix_time::millisec(5000));
  9. timer_.async_wait( MEM_FN(on_check_ping));
  10. }

這就是整個(gè)服務(wù)端的實(shí)現(xiàn)。你可以運(yùn)行并讓它工作起來(lái)!

在代碼中,我向你們展示了這一章我們學(xué)到的東西,為了更容易理解,我把代碼稍微精簡(jiǎn)了下;比如,大部分的控制臺(tái)輸出我都沒(méi)有展示,盡管在這本書附贈(zèng)的代碼中它們是存在的。我建議你自己運(yùn)行這些例子,因?yàn)閺念^到尾讀一次代碼能加強(qiáng)你對(duì)本章展示應(yīng)用的理解。

總結(jié)

我們已經(jīng)學(xué)到了怎么寫一些基礎(chǔ)的客戶端/服務(wù)端應(yīng)用。我們已經(jīng)避免了一些諸如內(nèi)存泄漏和死鎖的低級(jí)錯(cuò)誤。所有的編碼都是框架式的,這樣你就可以根據(jù)你自己的需求對(duì)它們進(jìn)行擴(kuò)展。

在接下來(lái)的章節(jié)中,我們會(huì)更加深入地了解使用Boost.Asio進(jìn)行同步編程和異步編程的不同點(diǎn),同時(shí)你也會(huì)學(xué)會(huì)如何嵌入你自己的異步操作。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)