boost------asio库的使用2(Boost程序库完全开发指南)读书笔记

本文详细介绍了使用Boost.Asio库进行TCP通信的编程方法,包括IP地址和端点的使用、同步与异步socket处理,以及如何通过resolver类查询网络地址。

网络通信

 

asio库支持TCP、UDP、ICMP通信协议,它在名字空间boost::asio::ip里提供了大量的网络通信方面的函数和类,很好地封装了原始的Berkeley Socket Api,展现给asio用户一个方便易用且健壮的网络通信库。

 

ip::tcp类是asio网络通信(TCP)部分主要的类,但它本身并没有太多的功能,而是定义了数个用于TCP通信的typedef类型,用来协作完成网络通信。这些typedef包括端点类endpoint、套接字类socket、流类iostream,以及接收器acceptor、解析器resolver等等。从某种程度上来看,ip::tcp类更像是一个名字空间。

 

1、IP地址和端点

IP地址独立于TCP、UDP等通信协议,asio库使用类ip::address来表示IP地址,可以同时支持ipv4和ipv6两种地址。

[cpp] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. #include "stdafx.h"  
  2. #include "boost/asio.hpp"  
  3. #include "boost/date_time/posix_time/posix_time.hpp"  
  4. #include "boost/bind.hpp"  
  5. #include "boost/function.hpp"  
  6. #include "iostream"  
  7. using namespace std;  
  8.   
  9.   
  10. int _tmain(int argc, _TCHAR* argv[])  
  11. {  
  12.     boost::asio::ip::address addr;          // 声明一个ip地址对象  
  13.     addr = addr.from_string("127.0.0.1");   // 从字符串产生IP地址  
  14.     assert(addr.is_v4());                   // ipv4的地址  
  15.     cout << addr.to_string() << endl;  
  16.   
  17.     addr = addr.from_string("2000:0000:0000:0000:0001:2345:6789:abcd");  
  18.     assert(addr.is_v6());  
  19.     cout << addr.to_string() << endl;  
  20.   
  21.     return 0;  
  22. }  

 

 

有了IP地址,再加上通信用的端口号就构成了一个socket端点,在asio库中用ip::tcp::endpoint类来表示。它的主要用法就是通过构造函数创建一个可用于socket通信的端点对象,端点的地址和端口号可以用address()和port()获得:

 

 

[cpp] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. #include "stdafx.h"  
  2. #include "boost/asio.hpp"  
  3. #include "boost/date_time/posix_time/posix_time.hpp"  
  4. #include "boost/bind.hpp"  
  5. #include "boost/function.hpp"  
  6. #include "iostream"  
  7. using namespace std;  
  8.   
  9.   
  10. int _tmain(int argc, _TCHAR* argv[])  
  11. {  
  12.     boost::asio::ip::address addr;          // 声明一个ip地址对象  
  13.     addr = addr.from_string("127.0.0.1");   // 从字符串产生IP地址  
  14.   
  15.     boost::asio::ip::tcp::endpoint ep(addr, 6688);  
  16.   
  17.     assert(ep.address() == addr);  
  18.     assert(ep.port() == 6688);  
  19.   
  20.     return 0;  
  21. }  


 

 

 

2、同步socket处理

 

ip::tcp的内部类型socket、acceptor和resolver是asio库TCP通信中最核心的一组类,它们封装了socket的连接、断开和数据收发功能,使用它们可以很容易地编写出socket程序。

 

socket类是TCP通信的基本类,调用成员函数connect()可以连接到一个指定的通信端点,连接成功后用local_endpoint()和remote_endpoint()获得连接两端的端点信息,用read_some()和write_some()阻塞读写数据,当操作完成后使用close()函数关闭socket。如果不关闭socket,那么在socket对象析构时也会自动调用close()关闭。

 

acceptor类对应socketAPI的accept()函数功能,它用于服务器端,在指定的端口号接受连接,必须配合socket类才能完成通信。

 

resolver类对应socketAPI的getaddrinfo()系列函数,用于客户端解析网址获得可用的IP地址,解析得到的IP地址可以使用socket对象连接。

 

下面是一个使用socket类和acceptor类来实现一对同步通信的服务器和客户端程序:

服务器端(它使用一个acceptor对象在6688端口接受连接,当有连接时使用一个socket对象发送一个字符串):

 

server.cpp:

[cpp] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. // server.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "boost/asio.hpp"  
  6. #include "boost/date_time/posix_time/posix_time.hpp"  
  7. #include "boost/bind.hpp"  
  8. #include "boost/function.hpp"  
  9. #include "iostream"  
  10. using namespace std;  
  11.   
  12.   
  13. int _tmain(int argc, _TCHAR* argv[])  
  14. {  
  15.     try  
  16.     {  
  17.         cout << "server start" << endl;  
  18.         boost::asio::io_service ios;  
  19.   
  20.         boost::asio::ip::tcp::acceptor acceptor(ios,   
  21.             boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 6688));  
  22.   
  23.         cout << acceptor.local_endpoint().address() << endl;  
  24.   
  25.         while (true)  
  26.         {  
  27.             boost::asio::ip::tcp::socket sock(ios);  
  28.             acceptor.accept(sock);  
  29.   
  30.             cout << "client : ";  
  31.             cout << sock.remote_endpoint().address() << endl;  
  32.   
  33.             sock.write_some(boost::asio::buffer("hello asio"));  
  34.         }  
  35.     }  
  36.   
  37.     catch (std::exception& e)  
  38.     {  
  39.         cout << e.what() << endl;  
  40.     }  
  41.   
  42.     return 0;  
  43. }  

服务器端程序里要注意的是自由函数buffer(),他可以包装很多种类的容器成为asio组件可用的缓冲区类型。通常不能直接把数组、vercor等容器用作asio的读写参数,必须使用buffer()函数包装

 

 

client:

[cpp] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. // client.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "boost/asio.hpp"  
  6. #include "boost/date_time/posix_time/posix_time.hpp"  
  7. #include "boost/bind.hpp"  
  8. #include "boost/function.hpp"  
  9. #include "iostream"  
  10. using namespace std;  
  11. #include "vector"  
  12.   
  13.   
  14. class AsynTimer  
  15. {  
  16. public:  
  17.     template<typename F>                              // 模板类型,可以接受任意可调用物  
  18.     AsynTimer(boost::asio::io_service& ios, int x, F func)  
  19.         :f(func), count_max(x), count(0),               // 初始化回调函数和计数器  
  20.         t(ios, boost::posix_time::millisec(500))        // 启动计时器  
  21.     {  
  22.         t.async_wait(boost::bind(&AsynTimer::CallBack,  // 异步等待计时器  
  23.             this, boost::asio::placeholders::error));   // 注册回调函数  
  24.     }  
  25.   
  26.     void CallBack(const boost::system::error_code& error)  
  27.     {  
  28.         if (count >= count_max)   // 如果计数器达到上限则返回  
  29.         {  
  30.             return;  
  31.         }  
  32.         ++count;  
  33.         f();                     // 调用function对象  
  34.   
  35.         // 设置定时器的终止时间为0.5秒之后  
  36.         t.expires_at(t.expires_at() + boost::posix_time::microsec(500));  
  37.         // 再次启动定时器,异步等待  
  38.         t.async_wait(boost::bind(&AsynTimer::CallBack, this, boost::asio::placeholders::error));  
  39.     }  
  40.   
  41. private:  
  42.     int count;  
  43.     int count_max;  
  44.     boost::function<void()> f;        // function对象,持有无参无返回值的可调用物  
  45.     boost::asio::deadline_timer t;  // asio定时器对象  
  46. };  
  47.   
  48.   
  49. void client(boost::asio::io_service& ios)  
  50. {  
  51.     try  
  52.     {  
  53.         cout << "client start." << endl;  
  54.   
  55.         boost::asio::ip::tcp::socket sock(ios);  
  56.         boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 6688);  
  57.   
  58.         sock.connect(ep);  
  59.   
  60.         vector<char> str(100, 0);  
  61.         sock.read_some(boost::asio::buffer(str));  
  62.   
  63.         cout << "recive from" << sock.remote_endpoint().address();  
  64.         cout << &str[0] << endl;  
  65.   
  66.     }  
  67.     catch (std::exception& e)  
  68.     {  
  69.         cout << e.what() << endl;  
  70.     }  
  71. }  
  72.   
  73.   
  74. int _tmain(int argc, _TCHAR* argv[])  
  75. {  
  76.     boost::asio::io_service ios;  
  77.     AsynTimer at(ios, 50000, boost::bind(client, boost::ref(ios)));  
  78.     ios.run();  
  79.   
  80.     return 0;  
  81. }  

 

3、异步socket处理

我们把刚才的同步socket程序改为异步调用方式。异步程序的处理流程与同步程序基本相同,只需要把原有的同步调用函数都换成前缀是async_的异步调用函数,并增加回调函数,在回调函数中再启动一个异步调用

 

服务器端:

[cpp] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. // server.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "boost/asio.hpp"  
  6. #include "boost/date_time/posix_time/posix_time.hpp"  
  7. #include "boost/bind.hpp"  
  8. #include "boost/function.hpp"  
  9. #include "iostream"  
  10. using namespace std;  
  11.   
  12.   
  13. class Server  
  14. {  
  15. private:  
  16.     boost::asio::io_service& ios;  
  17.     boost::asio::ip::tcp::acceptor acceptor;  
  18.     typedef boost::shared_ptr<boost::asio::ip::tcp::socket> sock_pt;  
  19.   
  20. public:  
  21.     Server(boost::asio::io_service& io) : ios(io),  
  22.         acceptor(ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 6688))  
  23.     {  
  24.         Start();  
  25.     }  
  26.     ~Server()  
  27.     {  
  28.   
  29.     }  
  30.   
  31.     void Start()  
  32.     {  
  33.         sock_pt sock(new boost::asio::ip::tcp::socket(ios));    // 智能指针  
  34.   
  35.         // 异步侦听服务  
  36.         acceptor.async_accept(*sock, boost::bind(&Server::acceptor_handle,   
  37.                                                  this, boost::asio::placeholders::error, sock));  
  38.     }  
  39.   
  40.     void acceptor_handle(const boost::system::error_code& error, sock_pt sock)  
  41.     {  
  42.         if (error)  
  43.         {  
  44.             return;  
  45.         }  
  46.         cout << "client : ";  
  47.   
  48.         // 输出连接的客户端信息  
  49.         cout << sock->remote_endpoint().address() << endl;  
  50.   
  51.         //   
  52.         sock->async_write_some( boost::asio::buffer("hello asio"),   
  53.                                 boost::bind(&Server::write_handle,  
  54.                                 this, boost::asio::placeholders::error));  
  55.   
  56.         Start(); // 再次启动异步接受连接  
  57.     }  
  58.   
  59.     void write_handle(const boost::system::error_code& error)  
  60.     {  
  61.         cout << "send message is complate" << endl;  
  62.     }  
  63.   
  64. };  
  65.   
  66.   
  67. int _tmain(int argc, _TCHAR* argv[])  
  68. {  
  69.     try  
  70.     {  
  71.         cout << "server start." << endl;  
  72.   
  73.         boost::asio::io_service ios;  
  74.   
  75.         Server serv(ios);  
  76.   
  77.         ios.run();  
  78.     }  
  79.     catch (std::exception& e)  
  80.     {  
  81.         cout << e.what() << endl;  
  82.     }  
  83.   
  84.     return 0;  
  85. }  

 

首先检查asio传递的error_code,保证没有错误发生。然后调用socket对象的async_write_some()异步发送数据。同样,我们必须再为这个异步调用编写回调函数write_handler()。当发送完数据后不要忘记调用Start()再次启动服务器接受链接,否则当完成数据发送后io_service将因为没有时间处理而结束运行。

 

发送数据的回调函数write_handler()很简单,因为不需要做更多的工作,可以直接实现一个空函数,在这里简单地输出一条信息,表示异步发送数据完成

 

 

客户端:

[cpp] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. // client.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "boost/asio.hpp"  
  6. #include "boost/date_time/posix_time/posix_time.hpp"  
  7. #include "boost/bind.hpp"  
  8. #include "boost/function.hpp"  
  9. #include "iostream"  
  10. using namespace std;  
  11. #include "vector"  
  12.   
  13.   
  14. class Client  
  15. {  
  16. private:  
  17.     boost::asio::io_service& ios;  
  18.     boost::asio::ip::tcp::endpoint ep;  // tcp端点  
  19.     typedef boost::shared_ptr<boost::asio::ip::tcp::socket> sock_pt;  
  20.   
  21. public:  
  22.     Client(boost::asio::io_service& io) : ios(io),  
  23.         ep(boost::asio::ip::address::from_string("127.0.0.1"), 6688)  
  24.     {  
  25.         Start(); // 启动异步连接  
  26.     }  
  27.   
  28.     ~Client()  
  29.     {  
  30.   
  31.     }  
  32.   
  33.     void Start()  
  34.     {  
  35.         sock_pt sock(new boost::asio::ip::tcp::socket(ios));  
  36.         sock->async_connect(ep, boost::bind(&Client::conn_handle, this,  
  37.                                             boost::asio::placeholders::error, sock));  
  38.     }  
  39.   
  40.     void conn_handle(const boost::system::error_code& error, sock_pt sock)  
  41.     {  
  42.         if (error)  
  43.         {  
  44.             return;  
  45.         }  
  46.         cout << "recive from : " << sock->remote_endpoint().address();  
  47.   
  48.         // 建立接收数据的缓冲区  
  49.         boost::shared_ptr<vector<char> > str(new vector<char>(100, 0));  
  50.   
  51.         // 异步读取数据  
  52.         sock->async_read_some(boost::asio::buffer(*str), boost::bind(&Client::read_handle,  
  53.                                                                     this,   
  54.                                                                     boost::asio::placeholders::error,  
  55.                                                                     str));  
  56.         Start(); // 再次启动异步连接  
  57.     }  
  58.   
  59.     void read_handle(const boost::system::error_code& error,   
  60.                      boost::shared_ptr<vector<char> > str)  
  61.     {  
  62.         if (error)  
  63.         {  
  64.             return;  
  65.         }  
  66.         cout << &(*str)[0] << endl;  
  67.     }  
  68. };  
  69.   
  70.   
  71. int _tmain(int argc, _TCHAR* argv[])  
  72. {  
  73.     try  
  74.     {  
  75.         cout << "client start." << endl;  
  76.         boost::asio::io_service ios;  
  77.   
  78.         Client client(ios);  
  79.   
  80.         ios.run();  
  81.     }  
  82.     catch (std::exception& e)  
  83.     {  
  84.         cout << e.what() << endl;  
  85.     }  
  86.   
  87.     return 0;  
  88. }  

 

 

4、查询网络地址

之前关于tcp通信的所有论述都是使用直接的ip地址,但在实际生活中大多数时候,都不大可能知道socket链接另一端的地址,而只有一个域名,这时候我们就需要使用resolver类来通过域名获得可用的ip,它可以实现与ip版本无关的网址解析

 

resolver使用内部类query和iterator共同完成查询ip地址的工作:首先使用网址和服务名创建query对象,然后由resolve()函数生成iterator对象,它代表了查询到的ip端点。之后就可以使用socket对象尝试连接,知道找到一个可用的为止。

[cpp] view plain copy

 print?在CODE上查看代码片派生到我的代码片

  1. #include "stdafx.h"  
  2. #include "boost/asio.hpp"  
  3. #include "boost/date_time/posix_time/posix_time.hpp"  
  4. #include "boost/bind.hpp"  
  5. #include "boost/function.hpp"  
  6. #include "boost/lexical_cast.hpp"  
  7. #include "boost/asio/error.hpp"  
  8. #include "iostream"  
  9. using namespace std;  
  10.   
  11.   
  12. void resolv_connect(boost::asio::ip::tcp::socket& sock, const char* name, int port)  
  13. {  
  14.     boost::asio::ip::tcp::resolver rlv(sock.get_io_service());  
  15.     boost::asio::ip::tcp::resolver::query qry(name, boost::lexical_cast<string>(port));  
  16.   
  17.     boost::asio::ip::tcp::resolver::iterator iter = rlv.resolve(qry);  
  18.     boost::asio::ip::tcp::resolver::iterator end;  
  19.   
  20.     boost::system::error_code ec = boost::asio::error::host_not_found;  
  21.     for (; ec && iter != end; ++iter)  
  22.     {  
  23.         sock.close();  
  24.         sock.connect(*iter, ec);  
  25.     }  
  26.   
  27.     if (ec)  
  28.     {  
  29.         cout << "can't connect." << endl;  
  30.         throw boost::system::error_code(ec);  
  31.     }  
  32.   
  33.     cout << "connet suceessd." << endl;  
  34. }  
  35.   
  36.   
  37. int _tmain(int argc, _TCHAR* argv[])  
  38. {  
  39.     try  
  40.     {  
  41.         boost::asio::io_service ios;  
  42.         boost::asio::ip::tcp::socket sock(ios);  
  43.   
  44.         resolv_connect(sock, "www.boost.org", 80);  
  45.   
  46.         ios.run();  
  47.     }  
  48.     catch (std::exception& e)  
  49.     {  
  50.         cout << e.what() << endl;  
  51.     }  
  52.       
  53.     return 0;  
  54. }  

 

resolv_connect()函数中使用lexical_cast,这是因为query对象只接受字符串参数,所以我们需要把端口号由整数转换为字符串。

 

当开始resolver的迭代时,需要使用error_code和逾尾迭代器两个条件来控制循环,因为有可能迭代完所有解析到的端点都无法连接,只有当error_code为0才表示连接成功。

 

有了resolv_connect()函数,就可以不受具体ip地址值的限制,以更直观更灵活的域名来连接服务器。

转载于:https://my.oschina.net/yagami1983/blog/810690

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值