boost asio异步读写网络编程实例详解

时间:2022-09-08 22:42:54

boost官方文档中聊天程序实例讲解

平台:ubuntu 14.04  g++

安装boost的一些库:

sudo apt-get install libboost-system-dev libboost-thread-dev

编译:chat_client.cpp chat_server.cpp

g++ chat_server.cpp -lboost_system -o chat_server
g++ chat_server.cpp -lboost_system -lboost_thread -lpthread -o chat_client

运行:

1. 首先运行chat_server服务器端,命令:

 $ ./chat_server 8080

2. 打开另一个终端,查看本机的ip地址,比如:192.168.1.101,运行chat_client客户端,命令:

$ ./chat_client 192.168.1.101 8080

3. 在chat_client的终端中继续输入想发送的信息,终端会自动再次显示一遍的你输入到的信息:

./chat_client 192.168.1.101 8080你还你还你好你好

数据包格式chat_message.hpp

  1. <pre name="code" class="cpp"><h3>数据包chat_message.hpp</h3>  
  2. // chat_message.hpp  
  3. // ~~~~~~~~~~~~~~~~  
  4. //  
  5. // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)  
  6. //  
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying  
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)  
  9. //实例源于http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/examples/cpp03_examples.html 文档中没有注释,自己在///开始看的时候也走了点弯路,本文对该源码写了详细的注释,希望能让后来者们少走些弯路。  
  10. //首先看看数据包的结构:  
  11. //数据包分为两个部分,首先是报头占4bytes,每一byte表示一个数字,也就是最多能表示的出9999,不足在前填0;  
  12. // 数据包头的数字表示数据部分的长度大小。   
  13.   
  14. #ifndef CHAT_MESSAGE_HPP  
  15. #define CHAT_MESSAGE_HPP  
  16.   
  17. #include <cstdio>  
  18. #include <cstdlib>  
  19. #include <cstring>  
  20.   
  21. class chat_message  
  22. {  
  23. public:  
  24.   enum { header_length = 4 };  
  25.   enum { max_body_length = 512 };  
  26.   
  27.   chat_message()  
  28.     : body_length_(0)  
  29.   {  
  30.   }  
  31.   
  32.   const char* data() const  
  33.   {  
  34.     return data_;  
  35.   }  
  36.   
  37.   char* data()  
  38.   {  
  39.     return data_;  
  40.   }  
  41.   
  42.   size_t length() const  
  43.   {  
  44.     return header_length + body_length_;  
  45.   }  
  46.   
  47.   const char* body() const  
  48.   {  
  49.     return data_ + header_length;  
  50.   }  
  51.   
  52.   char* body()  
  53.   {  
  54.     return data_ + header_length;  
  55.   }  
  56.   
  57.   size_t body_length() const  
  58.   {  
  59.     return body_length_;  
  60.   }  
  61.   
  62.   void body_length(size_t new_length)  
  63.   {  
  64.     body_length_ = new_length;  
  65.     if (body_length_ > max_body_length)  
  66.       body_length_ = max_body_length;  
  67.   }  
  68.   
  69.   bool decode_header()//将报头的4字节字符串转换成数字  
  70.   {  
  71.     using namespace std; // For strncat and atoi.  
  72.     char header[header_length + 1] = "";  
  73.     strncat(header, data_, header_length);  
  74.     body_length_ = atoi(header);  
  75.     if (body_length_ > max_body_length)  
  76.     {  
  77.       body_length_ = 0;  
  78.       return false;  
  79.     }  
  80.     return true;  
  81.   }  
  82.   
  83.   void encode_header()//把数据部分大小编码成字符串  
  84.   {  
  85.     using namespace std; // For sprintf and memcpy.  
  86.     char header[header_length + 1] = "";  
  87.     sprintf(header, "%4d", body_length_);  
  88.     memcpy(data_, header, header_length);  
  89.   }  
  90.   
  91. private:  
  92.   char data_[header_length + max_body_length];  
  93.   size_t body_length_;  
  94. };  
  95.   
  96. #endif // CHAT_MESSAGE_HPP  
  1.   



聊天客户端chat_client.cpp

///////////////////////////////////////////////////////////////////////////////////////////////////////
  1. <h3>  
  2. </h3><h3>  
  3.     <span style="white-space:pre-wrap">聊天客户端chat_client.cpp</span>  
  4. </h3>  
  5. <pre>  
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////////  
  1. //  
  2. // chat_client.cpp  
  3. // ~~~~~~~~~~~~~~~  
  4. //  
  5. // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)  
  6. //  
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying  
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)  
  9. //  
  10. //boost开发文档中实时聊天程序的客户端  
  11. //实现的大致思路是:在创建的客户端实例中初始化socket、连接服务器端,并且不断的进行着异步读操作(从服务器端读数据)  
  12. //在主线程中,从console中不断读取要被发送的消息,并且把这些消息post至io_service,然后进行异步写操作  
  13. //读写socket都是用异步操作  
  14. //这种方法不同于分别开一个读线程,一个写线程, 它的优势是线程不会一直等待读写数据,在并发数大的情况下通过异步读写能提高资源利用率  
  15.   
  16.   
  17. #include <cstdlib>  
  18. #include <deque>  
  19. #include <iostream>  
  20. #include <boost/bind.hpp>  
  21. #include <boost/asio.hpp>  
  22. #include <boost/thread/thread.hpp>  
  23. #include "chat_message.hpp"  
  24.   
  25. using boost::asio::ip::tcp;  
  26.   
  27. typedef std::deque<chat_message> chat_message_queue;  
  28.   
  29. class chat_client  
  30. {  
  31. public:  
  32.   chat_client(boost::asio::io_service& io_service,  
  33.       tcp::resolver::iterator endpoint_iterator)  
  34.     : io_service_(io_service),  
  35.       socket_(io_service) //使得成员函数能直接使用这些变量  
  36.   {  
  37.     boost::asio::async_connect(socket_, endpoint_iterator,  
  38.         boost::bind(&chat_client::handle_connect, this,  
  39.           boost::asio::placeholders::error)); //所有的操作都采用异步的方式  
  40.   }  
  41.   
  42.   void write(const chat_message& msg)  
  43.   {  
  44.     io_service_.post(boost::bind(&chat_client::do_write, this, msg)); //将消息主动投递给io_service  
  45.   }  
  46.   
  47.   void close()  
  48.   {  
  49.     io_service_.post(boost::bind(&chat_client::do_close, this)); //这个close函数是客户端要主动终止时调用  do_close函数是从服务器端  
  50.                                                                 //读数据失败时调用  
  51.   }  
  52.   
  53. private:  
  54.   
  55.   void handle_connect(const boost::system::error_code& error)  
  56.   {  
  57.     if (!error)  
  58.     {  
  59.       boost::asio::async_read(socket_,  
  60.           boost::asio::buffer(read_msg_.data(), chat_message::header_length), //读取数据报头  
  61.           boost::bind(&chat_client::handle_read_header, this,  
  62.             boost::asio::placeholders::error));  
  63.     }  
  64.   }  
  65.   
  66.   void handle_read_header(const boost::system::error_code& error)  
  67.   {  
  68.     if (!error && read_msg_.decode_header()) //分别处理数据报头和数据部分  
  69.     {  
  70.       boost::asio::async_read(socket_,  
  71.           boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),//读取数据包数据部分  
  72.           boost::bind(&chat_client::handle_read_body, this,  
  73.             boost::asio::placeholders::error));  
  74.     }  
  75.     else  
  76.     {  
  77.       do_close();  
  78.     }  
  79.   }  
  80.   
  81.   void handle_read_body(const boost::system::error_code& error)  
  82.   {  
  83.     if (!error)  
  84.     {  
  85.       std::cout.write(read_msg_.body(), read_msg_.body_length()); //输出消息  
  86.       std::cout << "\n";  
  87.       boost::asio::async_read(socket_,   
  88.           boost::asio::buffer(read_msg_.data(), chat_message::header_length), //在这里读取下一个数据包头  
  89.           boost::bind(&chat_client::handle_read_header, this,    
  90.             boost::asio::placeholders::error)); //完成一次读操作(处理完一个数据包)  进行下一次读操作  
  91.     }  
  92.     else  
  93.     {  
  94.       do_close();  
  95.     }  
  96.   }  
  97.   
  98.   void do_write(chat_message msg)  
  99.   {  
  100.     bool write_in_progress = !write_msgs_.empty(); //空的话变量为false  
  101.     write_msgs_.push_back(msg); //把要写的数据push至写队列  
  102.     if (!write_in_progress)//队列初始为空 push一个msg后就有一个元素了  
  103.     {  
  104.       boost::asio::async_write(socket_,  
  105.           boost::asio::buffer(write_msgs_.front().data(),  
  106.             write_msgs_.front().length()),  
  107.           boost::bind(&chat_client::handle_write, this,   
  108.             boost::asio::placeholders::error));  
  109.     }  
  110.   }  
  111.   
  112.   void handle_write(const boost::system::error_code& error)//第一个消息单独处理,剩下的才更好操作  
  113.   {  
  114.     if (!error)  
  115.     {  
  116.       write_msgs_.pop_front();//刚才处理完一个数据 所以要pop一个  
  117.       if (!write_msgs_.empty())    
  118.       {  
  119.         boost::asio::async_write(socket_,  
  120.             boost::asio::buffer(write_msgs_.front().data(),  
  121.               write_msgs_.front().length()),  
  122.             boost::bind(&chat_client::handle_write, this,  
  123.               boost::asio::placeholders::error)); //循环处理剩余的消息  
  124.       }  
  125.     }  
  126.     else  
  127.     {  
  128.       do_close();  
  129.     }  
  130.   }  
  131.   
  132.   void do_close()  
  133.   {  
  134.     socket_.close();  
  135.   }  
  136.   
  137. private:  
  138.   boost::asio::io_service& io_service_;  
  139.   tcp::socket socket_;  
  140.   chat_message read_msg_;  
  141.   chat_message_queue write_msgs_;  
  142. };  
  143.   
  144. int main(int argc, char* argv[])  
  145. {  
  146.   try  
  147.   {  
  148.     if (argc != 3)  
  149.     {  
  150.       std::cerr << "Usage: chat_client <host> <port>\n";  
  151.       return 1;  
  152.     }  
  153.   
  154.     boost::asio::io_service io_service;  
  155.   
  156.     tcp::resolver resolver(io_service);  
  157.     tcp::resolver::query query(argv[1], argv[2]); //输入ip(或域名)和端口号  
  158.     tcp::resolver::iterator iterator = resolver.resolve(query);  
  159.   
  160.     chat_client c(io_service, iterator);  
  161.   
  162.     boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));  
  163.   
  164.     char line[chat_message::max_body_length + 1];  
  165.     while (std::cin.getline(line, chat_message::max_body_length + 1))  
  166.     {  
  167.       using namespace std; // For strlen and memcpy.  
  168.       chat_message msg;  
  169.       msg.body_length(strlen(line));  
  170.       memcpy(msg.body(), line, msg.body_length());  
  171.       msg.encode_header();  
  172.       c.write(msg);  
  173.     }  
  174.   
  175.     c.close();  
  176.     t.join();  
  177.   }  
  178.   catch (std::exception& e)  
  179.   {  
  180.     std::cerr << "Exception: " << e.what() << "\n";  
  181.   }  
  182.   
  183.   return 0;  
  184. }  


聊天服务器端chat_server.cpp

  1. //  
  2. // chat_server.cpp  
  3. // ~~~~~~~~~~~~~~~  
  4. //  
  5. // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)  
  6. //  
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying  
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)  
  9. //  
  10. //服务器端程序,首先开启一个chat_server,用来初始话io_service和chat_room;  服务器每次只开启一个chat_room,chat_room的作用是 1//.保存用户信息  2.实现用户的join和leave  3.保存从每个客户端接收到的信息  4.将接收到的消息挂到每一个客户端write_msgs队尾  
  11. //每连接上一个客户端(socket)就开启一个新的chat_session 并加入唯一的chat_room   chat_session中实现分别对每一个客户端的异步读写//操作  
  12.   
  13. #include <algorithm>  
  14. #include <cstdlib>  
  15. #include <deque>  
  16. #include <iostream>  
  17. #include <list>  
  18. #include <set>  
  19. #include <boost/bind.hpp>  
  20. #include <boost/shared_ptr.hpp>  
  21. #include <boost/enable_shared_from_this.hpp>  
  22. #include <boost/asio.hpp>  
  23. #include "chat_message.hpp"  
  24.   
  25. using boost::asio::ip::tcp;  
  26.   
  27. //----------------------------------------------------------------------  
  28.   
  29. typedef std::deque<chat_message> chat_message_queue;  
  30.   
  31. //----------------------------------------------------------------------  
  32.   
  33. class chat_participant  
  34. {  
  35. public:  
  36.   virtual ~chat_participant() {}  
  37.   virtual void deliver(const chat_message& msg) = 0; //后面需要重载  
  38. };  
  39.   
  40. typedef boost::shared_ptr<chat_participant> chat_participant_ptr;  
  41.   
  42. //----------------------------------------------------------------------  
  43.   
  44. class chat_room  
  45. {  
  46. public:  
  47.   void join(chat_participant_ptr participant)  
  48.   {  
  49.     participants_.insert(participant);  
  50.     std::for_each(recent_msgs_.begin(), recent_msgs_.end(),  
  51.         boost::bind(&chat_participant::deliver, participant, _1));  
  52.   }  
  53.   
  54.   void leave(chat_participant_ptr participant)  
  55.   {  
  56.     participants_.erase(participant);  
  57.   }  
  58.   
  59.   
  60. //将从某个客户端收到的消息挂到 每一个客户端的write_msgs队尾 具体见chat_participant::deliver  
  61.   
  62.   void deliver(const chat_message& msg)  
  63.   
  64.   {  
  65.     recent_msgs_.push_back(msg);  
  66.     while (recent_msgs_.size() > max_recent_msgs)  
  67.       recent_msgs_.pop_front(); //room中保存的消息数有限  
  68.   
  69.     std::for_each(participants_.begin(), participants_.end(),  
  70.         boost::bind(&chat_participant::deliver, _1, boost::ref(msg)));  
  71.   }  
  72.   
  73. private:  
  74.   std::set<chat_participant_ptr> participants_;//用set来保存用户信息  
  75.   enum { max_recent_msgs = 100 };  
  76.   chat_message_queue recent_msgs_;//用来保存从某个客户端接收到的信息  
  77. };  
  78.   
  79. //----------------------------------------------------------------------  
  80.   
  81. class chat_session  
  82.   : public chat_participant,  
  83.     public boost::enable_shared_from_this<chat_session>  
  84. {  
  85. public:  
  86.   chat_session(boost::asio::io_service& io_service, chat_room& room)  
  87.     : socket_(io_service),  
  88.       room_(room)  
  89.   {  
  90.   }  
  91.   
  92.   tcp::socket& socket()  
  93.   {  
  94.     return socket_;  
  95.   }  
  96.   
  97.   void start()//每生成一个新的chat_session都会调用  
  98.   {  
  99.     room_.join(shared_from_this());  
  100.     boost::asio::async_read(socket_,  
  101.         boost::asio::buffer(read_msg_.data(), chat_message::header_length),  
  102.         boost::bind(  
  103.           &chat_session::handle_read_header, shared_from_this(),  
  104.           boost::asio::placeholders::error)); //异步读客户端发来的消息  
  105.   }  
  106.   
  107.   void deliver(const chat_message& msg)  
  108.   {  
  109.     bool write_in_progress = !write_msgs_.empty();  
  110.     write_msgs_.push_back(msg); //把room中保存的消息挂到write_msgs队尾  
  111.     if (!write_in_progress)  
  112.     {  
  113.       boost::asio::async_write(socket_,  
  114.           boost::asio::buffer(write_msgs_.front().data(),  
  115.             write_msgs_.front().length()),   
  116.           boost::bind(&chat_session::handle_write, shared_from_this(),  
  117.             boost::asio::placeholders::error));  
  118.     }  
  119.   }  
  120.   
  121.   void handle_read_header(const boost::system::error_code& error)  
  122.   {  
  123.     if (!error && read_msg_.decode_header())  
  124.     {  
  125.       boost::asio::async_read(socket_,  
  126.           boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),  
  127.           boost::bind(&chat_session::handle_read_body, shared_from_this(),  
  128.             boost::asio::placeholders::error));  
  129.     }  
  130.     else  
  131.     {  
  132.       room_.leave(shared_from_this());  
  133.     }  
  134.   }  
  135.   
  136.   void handle_read_body(const boost::system::error_code& error)  
  137.   {  
  138.     if (!error)  
  139.     {  
  140.       room_.deliver(read_msg_);  
  141.       boost::asio::async_read(socket_,  
  142.           boost::asio::buffer(read_msg_.data(), chat_message::header_length),  
  143.           boost::bind(&chat_session::handle_read_header, shared_from_this(),  
  144.             boost::asio::placeholders::error));  
  145.     }  
  146.     else  
  147.     {  
  148.       room_.leave(shared_from_this());  
  149.     }  
  150.   }  
  151.   
  152.   void handle_write(const boost::system::error_code& error)  
  153.   {  
  154.     if (!error)  
  155.     {  
  156.       write_msgs_.pop_front();  
  157.       if (!write_msgs_.empty())  
  158.       {  
  159.         boost::asio::async_write(socket_,  
  160.             boost::asio::buffer(write_msgs_.front().data(),  
  161.               write_msgs_.front().length()),  
  162.             boost::bind(&chat_session::handle_write, shared_from_this(),  
  163.               boost::asio::placeholders::error)); //服务器端将收到的消息送给所有的客户端(广播的方式)  
  164.       }  
  165.     }  
  166.     else  
  167.     {  
  168.       room_.leave(shared_from_this());  
  169.     }  
  170.   }  
  171.   
  172. private:  
  173.   tcp::socket socket_;  
  174.   chat_room& room_;  
  175.   chat_message read_msg_;  
  176.   chat_message_queue write_msgs_;  
  177. };  
  178.   
  179. typedef boost::shared_ptr<chat_session> chat_session_ptr;  
  180.   
  181. //----------------------------------------------------------------------  
  182.   
  183. class chat_server  
  184. {  
  185. public:  
  186.   chat_server(boost::asio::io_service& io_service,  
  187.       const tcp::endpoint& endpoint)  
  188.     : io_service_(io_service),  
  189.       acceptor_(io_service, endpoint) //全局只有一个io_service和一个acceptor  
  190.   {  
  191.     start_accept();  
  192.   }  
  193.   
  194.   void start_accept()  
  195.   {  
  196.     chat_session_ptr new_session(new chat_session(io_service_, room_));  
  197.     acceptor_.async_accept(new_session->socket(),  
  198.         boost::bind(&chat_server::handle_accept, this, new_session,  
  199.           boost::asio::placeholders::error));  
  200.   }  
  201.   
  202.   void handle_accept(chat_session_ptr session,  
  203.       const boost::system::error_code& error)  
  204.   {  
  205.     if (!error)  
  206.     {  
  207.       session->start();  
  208.     }  
  209.   
  210.     start_accept(); //每连接上一个socket都会调用  
  211.   }  
  212.   
  213. private:  
  214.   boost::asio::io_service& io_service_;  
  215.   tcp::acceptor acceptor_;  
  216.   chat_room room_; //chat_room中没有重载构造函数 所以会直接调用默认构造函数  
  217. };  
  218.   
  219. typedef boost::shared_ptr<chat_server> chat_server_ptr;  
  220. typedef std::list<chat_server_ptr> chat_server_list;  
  221.   
  222. //----------------------------------------------------------------------  
  223.   
  224. int main(int argc, char* argv[])  
  225. {  
  226.   try  
  227.   {  
  228.     if (argc < 2)  
  229.     {  
  230.       std::cerr << "Usage: chat_server <port> [<port> ...]\n";  
  231.       return 1;  
  232.     }  
  233.   
  234.     boost::asio::io_service io_service;  
  235.   
  236.     chat_server_list servers;  
  237.     for (int i = 1; i < argc; ++i)  
  238.     {  
  239.       using namespace std; // For atoi.  
  240.       tcp::endpoint endpoint(tcp::v4(), atoi(argv[i]));  
  241.       chat_server_ptr server(new chat_server(io_service, endpoint));  
  242.       servers.push_back(server);  
  243.     }  
  244.   
  245.     io_service.run();  
  246.   }  
  247.   catch (std::exception& e)  
  248.   {  
  249.     std::cerr << "Exception: " << e.what() << "\n";  
  250.   }  
  251.   
  252.   return 0;  
  253. }

  254. 转载自:blog.csdn.net/cyg0810/article/details/36179195