您的位置:首页 > 理论基础 > 计算机网络

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

2014-10-20 08:55 441 查看
客户端和服务端
在这一章节,我们会深入学习使用Boost.Asio建立不平凡的客户端和服务端应用。你可以运行和测试它们,当你理解时,你可以用它们做框架来构造你自己的应用。
在接下来的例子中:

客户端使用一个用户名(无密码)登录到服务端

所有的连接由客户端建立,当客户端请求时服务端回应

所有的请求和回复都以换行符结尾(’\n’)

对于5秒钟没有ping操作的客户端,服务端自动断开连接

客户端可以发送如下请求:

获得所有连接客户端的列表

客户端可以ping,当它ping时,服务端返回ping ok或者ping client_list_chaned(在接下来的例子中,客户端重新请连接的客户端列表)

为了更有趣一点,我们增加了一些难度:

每个客户端登录6个用户连接,比如Johon,James,Lucy,Tracy,Frank和Abby

每个客户端连接随机地ping服务端(随机7秒;这样的话,服务端会时不时关闭一个连接)

同步客户端/服务端

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

同步客户端
同步客户端以你所期望的穿行方式运行;连接到服务端,登录服务器,然后执行连接循环,比如休眠一下,发起一个请求,读取服务端返回,然后再休眠一会,然后一直循环下去……



因为我们是同步的,所以我们要保持简单。首先,连接到服务器,然后再循环,如下:

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_;




};

在循环中,我们仅仅填充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,我们重新请求客户端列表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: