Boost.Asio C++ Chapter_4 tcp_sync
2017-03-30 16:21
323 查看
/*同步客户端*/ //同步客户端会以你所期望的串行方式运行;连接到服务端,登录服务器,然后执行连接循环,比如休眠一下, //发起一个请求,读取服务端返回,然后再休眠一会,然后一直循环下去…… //因为我们是同步的,所以我们让事情变得简单一点。首先,连接到服务器,然后再循环,如下: io_service service; 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; } 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) { // ... 和之前一样 } 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(); }
/*同步服务端*/ //同步服务端也是相当简单的。它只需要两个线程,一个负责接收新的客户端连接,另外一个负责处理已经存在的客户端请求。 //它不能使用单线程,因为等待新的客户端连接是一个阻塞操作,所以我们需要另外一个线程来处理已经存在的客户端请求。 //正常来说服务端都比客户端要难实现。一方面,它要管理所有已经连接的客户端。因为我们是同步的,所以我们需要至少两个线程, //一个负责接受新的客户端连接(因为accept()是阻塞的)而另一个负责回复已经存在的客户端。 io_service service; void accept_thread() { ip::tcp::acceptor acceptor(service,ip::tcp::endpoint(ip::tcp::v4(), 8001)); while ( true) { client_ptr new_( new talk_to_client); acceptor.accept(new_->sock()); boost::recursive_mutex::scoped_lock lk(cs); clients.push_back(new_); } } void handle_clients_thread() { while ( true) { boost::this_thread::sleep( millisec(1)); boost::recursive_mutex::scoped_lock lk(cs); for(array::iterator b = clients.begin(), e = clients.end(); b!= e; ++b) (*b)->answer_to_client(); // 删除已经超时的客户端 clients.erase(std::remove_if(clients.begin(), clients.end(), boost::bind(&talk_to_client::timed_out,_1)), clients.end()); } } int main(int argc, char* argv[]) { boost::thread_group threads; threads.create_thread(accept_thread); threads.create_thread(handle_clients_thread); threads.join_all(); } //为了分辨客户端发送过来的请求我们需要保存一个客户端的列表。 //每个talk_to_client实例都拥有一个socket,socket类是不支持拷贝构造的,所以如果你想要把它们保存在一个std::vector对象中,你需要一个指向它的智能指针。 //这里有两种实现的方式:在talk_to_client内部保存一个指向socket的智能指针然后创建一个talk_to_client实例的数组, //或者让talk_to_client实例用变量的方式保存socket,然后创建一个指向talk_to_client智能指针的数组。我选择后者,但是你也可以选前面的方式: typedef boost::shared_ptr<talk_to_client> client_ptr; typedef std::vector<client_ptr> array; array clients; boost::recursive_mutex cs; //talk_to_client的主要代码如下: struct talk_to_client : boost::enable_shared_from_this<talk_to_client> { talk_to_client() { ... } std::string username() const { return username_; } void answer_to_client() { try { read_request(); process_request(); } catch ( boost::system::system_error&) { stop(); } if ( timed_out()) stop(); } void set_clients_changed() { clients_changed_ = true; } ip::tcp::socket & sock() { return sock_; } bool timed_out() const { ptime now = microsec_clock::local_time(); long long ms = (now - last_ping).total_milliseconds(); return ms > 5000 ; } void stop() { boost::system::error_code err; sock_.close(err); } void read_request() { if ( sock_.available()) already_read_ += sock_.read_some(buffer(buff_ + already_read_, max_msg - already_read_)); } ... private: // ... 和同步客户端中的一样 bool clients_changed_; ptime last_ping; }; //上述代码拥有非常好的自释能力。其中最重要的方法是read_request()。它只在存在有效数据的情况才读取,这样的话,服务端永远都不会阻塞: void process_request() { bool found_enter = std::find(buff_, buff_ + already_read_, '\n') < buff_ + already_read_; if ( !found_enter) return; // 消息不完整 // 处理消息 last_ping = microsec_clock::local_time(); size_t pos = std::find(buff_, buff_ + already_read_, '\n') - buff_; std::string msg(buff_, pos); std::copy(buff_ + already_read_, buff_ + max_msg, buff_); already_read_ -= pos + 1; if ( msg.find("login ") == 0) on_login(msg); else if ( msg.find("ping") == 0) on_ping(); else if ( msg.find("ask_clients") == 0) on_clients(); else std::cerr << "invalid msg " << msg << std::endl; } void on_login(const std::string & msg) { std::istringstream in(msg); in >> username_ >> username_; write("login ok\n"); update_clients_changed(); } void on_ping() { write(clients_changed_ ? "ping client_list_changed\n" : "ping ok\n"); clients_changed_ = false; } void on_clients() { std::string msg; { boost::recursive_mutex::scoped_lock lk(cs); for( array::const_iterator b = clients.begin(), e = clients.end() ; b != e; ++b) msg += (*b)->username() + " "; } write("clients " + msg + "\n"); } void write(const std::string & msg){ sock_.write_some(buffer(msg)); } //观察process_request()。当我们读取到足够多有效的数据时,我们需要知道我们是否已经读取到整个消息(如果found_enter为真)。 //这样做的话,我们可以使我们避免一次读多个消息的可能(’\n’之后的消息也被保存到缓冲区中), //然后我们解析读取到的整个消息。剩下的代码都是很容易读懂的。原文链接地址:https://mmoaay.gitbooks.io/boost-asio-cpp-network-programming-chinese/content/Chapter4.html
相关文章推荐
- Boost.Asio C++ Chapter_3 udp_sync
- Boost.Asio C++ Chapter_4 tcp_async
- Boost.Asio C++ Chapter_3 tcp_async
- C++ boost::asio编程-同步TCP详解及实例代码
- Boost.Asio C++ 网络编程之五:TCP回显客户端/服务端
- Boost.Asio C++ Chapter_2
- Boost.Asio C++ 网络编程之八:基于TCP的同步服务端
- Boost.Asio C++ Network Programming(Chapter 2)
- Boost.Asio C++ 网络编程之十:基于TCP的异步服务端
- Boost.Asio C++ 网络编程之七:基于TCP的同步客户端
- C++ boost::asio编程-异步TCP详解及实例代码
- Boost.Asio C++ Network Programming(Chapter 1)
- Boost.Asio C++ 网络编程之九:基于TCP的异步客户端
- boost::asio::ip::tcp::socket is connected?(如何知道socket已经连接?)
- 使用Boost.Asio写的多线程TCP转发代理服务器
- LinuxC/C++编程基础(21) 使用boost::asio搭建服务器简单实例(续)
- boost::asio::ip::tcp实现网络通信的小例子
- 使用Boost.Asio写的多线程TCP转发代理服务器
- ASIO—下一代C++标准可能接纳的网络库(2)TCP网络应用
- boost::asio::ip::tcp实现网络通信的小例子