首页 > 代码库 > 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秒;这样的话,服务端会时不时关闭一个连接)
同步客户端/服务端
首先,我们会实现同步应用。你会发现它的代码很直接而且易读的。然而,网络部分不需要它自己的线程,因为所有的网络调用都是阻塞的。
同步客户端
同步客户端以你所期望的穿行方式运行;连接到服务端,登录服务器,然后执行连接循环,比如休眠一下,发起一个请求,读取服务端返回,然后再休眠一会,然后一直循环下去……
因为我们是同步的,所以我们要保持简单。首先,连接到服务器,然后再循环,如下:
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,我们重新请求客户端列表。
Boost.Asio c++ 网络编程翻译(17)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。