【boost】boost::asio(3)——socket编程

来源:互联网 发布:安卓网络聊天室破解版 编辑:程序博客网 时间:2024/06/14 07:25

asio的主要用途还是用于socket编程,本文就以一个tcp的daytimer服务为例简单的演示一下如何实现同步和异步的tcp socket编程。


一、同步服务器版本


客户端

客户端的代码如下:

[cpp] view plain copy
  1. //code of client  
  2. #include <iostream>  
  3. #include <boost/array.hpp>  
  4. #include <boost/asio.hpp>  
  5. using namespace std;  
  6. using boost::asio::ip::tcp;  
  7.   
  8. int main(int argc, char* argv[])  
  9. {  
  10.     try  
  11.     {  
  12.         //(1)通过tcp::socket类定义一个tcp client对象socket  
  13.         boost::asio::io_service io;  
  14.         tcp::socket socket(io);  
  15.           
  16.         //(2)通过connect函数连接服务器,打开socket连接。  
  17.         tcp::endpoint end_point(boost::asio::ip::address::from_string("127.0.0.1"), 3200);  
  18.         socket.connect(end_point);  
  19.   
  20.         for (;;)  
  21.         {  
  22.             boost::array<char, 128> buf;  
  23.             boost::system::error_code error;  
  24.   
  25.             //(3)通过read_some函数来读数据  
  26.             size_t len = socket.read_some(boost::asio::buffer(buf), error);  
  27.   
  28.             if (error == boost::asio::error::eof)  
  29.             {  
  30.                 break;  //connection closed cleadly by peer  
  31.             }  
  32.             else if (error)  
  33.             {  
  34.                 throw boost::system::system_error(error);   //some other error  
  35.             }  
  36.   
  37.             cout.write(buf.data(), len);  
  38.         }  
  39.     }  
  40.     catch (std::exception& e)  
  41.     {  
  42.         cout<<e.what()<<endl;  
  43.     }  
  44. }  

主要流程如下:

(1)通过tcp::socket类定义一个tcp client对象socket
(2)通过connect函数连接服务器,打开socket连接。
(3)通过read_some函数来读数据
另外,还可以通过write_some来写数据,通过close来关闭socket连接(这里是通过释放socket对象隐式释放连接)。


服务器

服务器代码如下:

[cpp] view plain copy
  1. //code of server  
  2. #include <ctime>  
  3. #include <iostream>  
  4. #include <string>  
  5. #include <boost/asio.hpp>  
  6.   
  7. using namespace std;  
  8. using namespace boost;  
  9. using boost::asio::ip::tcp;  
  10.   
  11. int main(int argc, char*argv[])  
  12. {  
  13.     try  
  14.     {  
  15.         //(1)通过tcp::acceptor类创建一个tcp server对象,并绑定端口(也可以不在构造器中自动绑定,而通过bind函数手动绑定)  
  16.         asio::io_service io;  
  17.         tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), 3200));  
  18.   
  19.         for (;;)  
  20.         {  
  21.             //(2)通过accept函数获取远端连接  
  22.             tcp::socket socket(io);  
  23.             acceptor.accept(socket);  
  24.   
  25.             time_t now = time(0);  
  26.             string message = ctime(&now);  
  27.   
  28.             //(3)通过远端连接的write_some函数将数据发往客户端  
  29.             system::error_code ignored_error;  
  30.             socket.write_some(asio::buffer(message), ignored_error);  
  31.         }  
  32.     }  
  33.     catch (std::exception &e)  
  34.     {  
  35.         cout<<e.what()<<endl;  
  36.     }  
  37. }  

主要流程如下:

(1)通过tcp::acceptor类创建一个tcp server对象,并绑定端口(也可以不在构造器中自动绑定,而通过bind函数手动绑定)
(2)通过accept函数获取远端连接
(3)通过远端连接的write_some函数将数据发往客户端



二、异步服务器

前面的服务器是同步版本,在大并发的场景下一般需要用到异步socket。服务器的异步版本如下:

[cpp] view plain copy
  1. //code of asyc server  
  2. #include <ctime>  
  3. #include <iostream>  
  4. #include <string>  
  5. #include <memory>  
  6. #include <functional>  
  7. #include <boost/asio.hpp>  
  8.   
  9. using boost::asio::ip::tcp;  
  10. using namespace std;  
  11.   
  12.   
  13. void process_socket(shared_ptr<tcp::socket> socket)  
  14. {  
  15.     time_t now = time(0);  
  16.     shared_ptr<string> message(new string(ctime(&now)));  
  17.   
  18.     auto callback = [=](const boost::system::error_code& err, size_t size)  //匿名函数:用到的任何外部变量都隐式按值捕获  
  19.     {  
  20.         if ((int)size == message->length())  
  21.         {  
  22.             cout<<"Write completed"<<endl;  
  23.         }  
  24.     };  
  25.   
  26.     //(3)通过远端连接的write_some函数将数据发往客户端  
  27.     socket->async_send(boost::asio::buffer((*message)), callback);  
  28. }  
  29.   
  30. typedef function<void (const boost::system::error_code&)> accept_callback;  
  31. void start_accept(tcp::acceptor& acceptor)  
  32. {  
  33.     shared_ptr<tcp::socket> socket(new tcp::socket(acceptor.get_io_service()));  
  34.     accept_callback callback = [&acceptor, socket](const boost::system::error_code& error)//匿名函数:socket按值捕获, acceptor按引用捕获  
  35.     {  
  36.         if (!error)  
  37.         {  
  38.             process_socket(socket);  
  39.         }  
  40.   
  41.         //循环:相当于同步服务器中的for循环  
  42.         start_accept(acceptor);  
  43.     };  
  44.   
  45.     //(2)通过accept函数获取远端连接  
  46.     acceptor.async_accept(*socket, callback);  
  47. }  
  48.   
  49.   
  50.   
  51. int main()  
  52. {  
  53.     try  
  54.     {  
  55.         //(1)通过tcp::acceptor类创建一个tcp server对象,并绑定端口(也可以不在构造器中自动绑定,而通过bind函数手动绑定)  
  56.         boost::asio::io_service io;  
  57.         tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), 3200));  
  58.   
  59.         start_accept(acceptor);  
  60.   
  61.         //异步必须要主动调用io_service::run  
  62.         io.run();  
  63.     }  
  64.     catch (std::exception& e)  
  65.     {  
  66.         cout<<e.what()<<endl;  
  67.     }  
  68. }  

这个异步版本的逻辑倒不是很复杂,基本上和.net中传统的异步socket相似,不过需要注意的是,由于c++中内存需要自己管理,而asio框架也没有提供任何管理机制,因此需要注意async_accept、async_send等函数的参数生命周期,切记不能在里面传入栈变量的引用。如果是堆变量,需要确保释放,本例中我是通过share_ptr来实现的自动释放。

更多的示例请参看asio官方文档。