首页 > 代码库 > Boost.Asio c++ 网络编程翻译(17)

Boost.Asio c++ 网络编程翻译(17)

客户端和服务端
在这一章节,我们会深入学习使用Boost.Asio建立不平凡的客户端和服务端应用。你可以运行和测试它们,当你理解时,你可以用它们做框架来构造你自己的应用。
在接下来的例子中:
  • 客户端使用一个用户名(无密码)登录到服务端
  • 所有的连接由客户端建立,当客户端请求时服务端回应
  • 所有的请求和回复都以换行符结尾(’\n’)
  • 对于5秒钟没有ping操作的客户端,服务端自动断开连接
客户端可以发送如下请求:
  • 获得所有连接客户端的列表
  • 客户端可以ping,当它ping时,服务端返回ping ok或者ping client_list_chaned(在接下来的例子中,客户端重新请连接的客户端列表)
为了更有趣一点,我们增加了一些难度:
  • 每个客户端登录6个用户连接,比如Johon,James,Lucy,Tracy,Frank和Abby
  • 每个客户端连接随机地ping服务端(随机7秒;这样的话,服务端会时不时关闭一个连接)

同步客户端/服务端
首先,我们会实现同步应用。你会发现它的代码很直接而且易读的。然而,网络部分不需要它自己的线程,因为所有的网络调用都是阻塞的。

同步客户端
同步客户端以你所期望的穿行方式运行;连接到服务端,登录服务器,然后执行连接循环,比如休眠一下,发起一个请求,读取服务端返回,然后再休眠一会,然后一直循环下去……
page81image6024
因为我们是同步的,所以我们要保持简单。首先,连接到服务器,然后再循环,如下:
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) {
           std::cout << "client terminated " << std::endl;
       }

}

下面的代码片段展示了talk_to_svr类:
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() {
           write("login " + username_ + "\n");
           read_answer();
           while ( started_) {
               write_request();
               read_answer();
               boost::this_thread::sleep(millisec(rand() % 7000));

} }

       std::string username() const { return username_; }

... private:

       ip::tcp::socket sock_;
       enum { max_msg = 1024 };
       int already_read_;
       char buff_[max_msg];
       bool started_;
       std::string username_;
page82image11296

}; 

在循环中,我们仅仅填充1个比特,一个ping操作然后睡眠,然后读取服务端的返回。我们随机睡眠(有时候超过5秒),这样服务端就会在某个时间点断开我们的连接:
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;

对于读取结果,我们使用在之前章节就有说到的read_complete,来保证我们能都到换行符(’\n’)。逻辑在process_msg()中,在这里我们读取服务端的返回,然后分发到正确的方法:
void on_login() { 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) {
       // ... 和之前一样
   }
当读取服务端对我们ping操作的返回时,如果我们获得client_list_changed,我们重新请求客户端列表。






Boost.Asio c++ 网络编程翻译(17)