Boost asio

asio(asynchronous input and output) 表示异步输入输出,是基于操作系统提供的异步机制,异步数据处理表示触发后不需要等待完成,期间可以执行其他任务,而且不要求使用多线程和锁定,有效避免了条件竞争,死锁等。asio主要用于网络通信方面,支持TCP,UDP,ICMP等网络通信协议。

asio

asio主要分为同步和异步两种方式,同步表示同步等待,比如网络连接时,客户端发出请求后,它会把控制权交给操作系统,会一直等待连接成功或者出错才返回。而异步方式不需要阻塞程序一直等待应答,它会立即返回,接着执行其他任务,当系统完成操作时,会调用回调函数通知它。

 //***********************************************************
 //FUNCTION::synchronization 
 void synchronization()
 {
    boost::asio::io_service IOService;
    boost::asio::deadline_timer t(IOService, boost::posix_time::seconds(3));

    std::cout << t.expires_at() << std::endl;//查看终止绝对时间
    t.wait();//调用wait同步等待
    std::cout << "Hello asio" << std::endl;
 }

IO 服务和对象

asio的和核心类是io_service,必须使用它来支持所有IO功能,而且必须调用run()方法开启IO服务事件循环,它会把控制权交给操作系统,负责分发异步回调事件,只有所有异步回调事件所有完成后才返回,不执行run(),异步函数也不会调用。

 //***********************************************************
 //FUNCTION::
 void printAsio(const boost::system::error_code &ec) 
 { 
    std::cout << "hello asio" << std::endl; 
 } 

 //***********************************************************
 //FUNCTION::asynchronization
 void asynchronization()
 {
    boost::asio::io_service IOService;
    boost::asio::deadline_timer t(IOService, boost::posix_time::seconds(5));

    t.async_wait(printAsio);//异步等待,传入回调函数,立即返回
    std::cout << "It show before t expired." << std::endl;
    IOService.run();//异步IO必须
 }

首先定义IO服务 IOService, 用来初始化IO对象, 所有IO对象通常需要一个IO服务作为构造函数的第一个参数,boost::asio::dealline_timer 的第二个参数,用于表示某个时间点或者某段时间后闹钟停止async_wait() 的好处是,该函数调用会立即返回,而不是等待五秒钟。 一旦闹钟时间到,作为参数所提供的函数就会被相应调用。 因此,应用程序可以在调用了 async_wait() 之后执行其它操作,而不是阻塞在这里。
注意到回调函数的形参是void printAsio(const boost::system::error_code &ec) ,其asio使用system库的error_code和system_error来表示运行时错误。io_service将错误的操作结果翻译为boost::system::error_code类型。error_code可与特定值进行比较,或作为boolean值检测(false表示无错误)。结果再传递给IO对象。
1. 一种形式是有error_code的输出参数,调用后必须检查这个参数验证是否发生了错误。
2. 另一种没有error_code的参数,如果发生了错误会抛出system_error异常,调用代码必须使用try_catch块捕获错误。

ASIO通过buffer函数生成一个内部使用的缓冲区类,它能把数组、指针(同时指定大 小)、std::vector、std::string、boost::array包装成缓冲区类。自动管理缓冲区大小以防止缓冲区溢出

boost::asio::buffer(Temp); //Construct: array, pointer, string, vector, boost::array 

const char* pChar = boost::asio::buffer_cast<const char*> (boost::asio::buffer(Temp));//Cast 

std::size_t Size = boost::asio::buffer_size(boost::asio::buffer(Temp)); //Size

网络通信

ip::tcp类是asio网络通信(tcp)主要类,本身没有太多的功能,而是定义了大量的typedef类型,用来协作完成网络通信。

  1. 端点类 endpoint
  2. 套接字类 socket
  3. 流类 iostream
  4. 接受器 acceptor
  5. 解析器 resolver

Example:通过编程实现服务器端和客户端的简单通信,要求服务器端输出客户IP,并向客户端发出hello文字
服务端:通常服务端是一个死循环,监听客户端的连接请求。

#include <boost/asio.hpp>
#include <iostream>

int main()
{
    try 
    {
        std::cout << "Server start." << std::endl;
        boost::asio::io_service IOService;

        boost::asio::ip::tcp::acceptor Acceptor(IOService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 6688));
        std::cout << Acceptor.local_endpoint().address() << std::endl;

        while (true)
        {
            boost::asio::ip::tcp::socket Socket(IOService);
            Acceptor.accept(Socket);        //阻塞等待socket连接
            std::cout << "Client: ";
            std::cout << Socket.remote_endpoint().address() << std::endl;
            Socket.write_some(boost::asio::buffer("Hello asio"));
        }
    }
    catch(std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }
}

Endpoint:服务器和客户端双方都以端点表示。包括IP地址,和端口。服务器端同意IPv4协议,端口为6688。
Acceptor:建立接收器对象来监听连接,初始化为tcp端点,只在指定的6688端口上等待连接。
Socket:通信桥梁。服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。读写操作。
Accept:一直监听等待,直到有客户端连接过来
有连接后通过socket调用远端端点的地址函数,输出客户端IP地址。
用write方法向客户端输入信息。
While里面发送一次数据后析构,若想双方一直通信,可将socket和accept定义在循环体外。

客服端:

#include <boost/asio.hpp>
#include <iostream>

int main()
{
    std::cout << "Client start." << std::endl;


    try
    {
        boost::asio::io_service IOService;
        boost::asio::ip::tcp::socket Socket(IOService);
        boost::asio::ip::tcp::endpoint EndPoint(boost::asio::ip::address::from_string("127.0.0.1"), 6688);
        Socket.connect(EndPoint);//发出连接请求
        std::vector<char> Str(100, 0);
        Socket.read_some(boost::asio::buffer(Str));//阻塞等待服务端的响应信息
        std::cout << "Receive from: " << Socket.remote_endpoint().address() << std::endl;
        std::cout << &Str[0] << std::endl;
    }
    catch(std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }
}

由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

这里写图片描述

异步服务端
异步程序处理与同步基本相同,同步函数改成异步调用函数,并增加回调函数,并在回调函数中再启动一个异步调用。

#pragma once
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>

class CServer
{

private:
    boost::asio::io_service& m_IOService;
    boost::asio::ip::tcp::acceptor m_Acceptor;
    typedef boost::shared_ptr<boost::asio::ip::tcp::socket> SocketPt;

public:
    CServer(boost::asio::io_service& vIOService) : m_IOService(vIOService), m_Acceptor(m_IOService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 6688))
    {
        start();
    }
    ~CServer(){}

    void start()
    {
        SocketPt Socket(new boost::asio::ip::tcp::socket(m_IOService));
        m_Acceptor.async_accept(*Socket, boost::bind(&CServer::acceptHandler, this, boost::asio::placeholders::error, Socket));
    }

    void acceptHandler(const boost::system::error_code& vErrorCode, SocketPt vSocket)
    {
        if (vErrorCode) return;//检查错误码

        std::cout << "Client: ";
        std::cout << vSocket->remote_endpoint().address() << std::endl;//输出客户端信息
        vSocket->async_write_some(boost::asio::buffer("Hello asio"), boost::bind(&CServer::writeHandler, this, boost::asio::placeholders::error));

        start();//再次启动异步接受连接
    }

    void writeHandler(const boost::system::error_code& vErrorCode)
    {
        std::cout << "Send message complete." << std::endl;
    }
};

tcp通信的必备要素是io_service,和acceptor对象。随后的智能指针指向socket对象,用来在回调函数中传递。start()函数启动异步服务,而智能指针表示socket对象能够被异步调用后还能使用,可以存在整个程序运行周期,直到没人使用为止。当tcp连接发生时,accept_handler()函数被调用,使用socket对象发送数据。首先必须检查asio传递的error_code,保证没有错误发生。异步发送数据async_write_some,而且需要为异步调用编写回调函数write_handler()。当发送完数据后,再次调用start()启动服务器接受连接,不然io_service没有事件处理而结束运行。

异步客户端

#pragma once
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class CClient
{
private:
    boost::asio::io_service& m_IOService;
    boost::asio::ip::tcp::endpoint m_EndPoint;
    typedef boost::shared_ptr<boost::asio::ip::tcp::socket> SocketPt;

public:
    CClient(boost::asio::io_service& vIOService) : m_IOService(vIOService), m_EndPoint(boost::asio::ip::address::from_string("127.0.0.1"), 6688)
    {
        start();
    }
    ~CClient(){}

    void start()
    {
        SocketPt Socket(new boost::asio::ip::tcp::socket(m_IOService));
        Socket->async_connect(m_EndPoint, boost::bind(&CClient::connectHandler, this, boost::asio::placeholders::error, Socket));
    }

    void connectHandler(const boost::system::error_code& vErrorCode, SocketPt vSocket)
    {
        if (vErrorCode) return;

        std::cout << "Receive from: ";
        std::cout << vSocket->remote_endpoint().address() << std::endl;

        boost::shared_ptr<std::vector<char> > Str(new std::vector<char>(100, 0));//建立数据接收缓冲区
        vSocket->async_read_some(boost::asio::buffer(*Str), boost::bind(&CClient::readHandler, this, boost::asio::placeholders::error, Str));//异步读取数据

        start();    
    }

    void readHandler(const boost::system::error_code& vErrorCode, boost::shared_ptr<std::vector<char> > vStr)
    {
        if (vErrorCode) return;
        std::cout << &(*vStr)[0] << std::endl;
    }
};

查询网络地址

通常情况下,我们是不知道服务器的地址,而是知道它的域名。这时候需要使用resolver来通过域名获得可用的ip。resolver使用内部类query和iterator共同完成查询ip的工作:首先使用网址和服务名创建query对象,然后用resolve()函数生产iterator对象,它代表了查询到的ip端点。

 //***********************************************************
 //FUNCTION::socket,网址,端口号
 void resolveConnect(boost::asio::ip::tcp::socket& vSocket, const char* vName, int vPort)
 {
    boost::asio::ip::tcp::resolver RLV(vSocket.get_io_service());
    boost::asio::ip::tcp::resolver::query Query(vName, boost::lexical_cast<std::string>(vPort));

    //使用resolve()开始迭代端点
    boost::asio::ip::tcp::resolver::iterator Iter = RLV.resolve(Query);
    boost::asio::ip::tcp::resolver::iterator End;
    boost::system::error_code ErrorCode = boost::asio::error::host_not_found;

    for (; ErrorCode && Iter!=End; ++Iter)
    {
        vSocket.close();
        vSocket.connect(*Iter, ErrorCode);
    }
    if (ErrorCode)
    {
        std::cout << "Can't connect." << std::endl;
        throw boost::system::system_error(ErrorCode);
    }
    std::cout << "Connect success." << std::endl;
 }

使用error_code和迭代器控制循环,因为可能迭代器完所有解析到的端点都无法连接,只有当error_code为0才表示连接成功。

 //***********************************************************
 //FUNCTION::
 void queryWebSite()
 {   
     boost::asio::io_service IOService;
     boost::asio::ip::tcp::socket Socket(IOService);
     resolveConnect(Socket, "www.boost.org", 80);

     IOService.run();
 }

流操作

对于有连接的tcp协议,asio有ip::tcp::iostream类简化socket通信,其是std::basic_iostream子类。内部集成了resolver的域名解析和acceptor的接受连接功能,能够非常简单完成tcp通信。
客户端:

 int main()
 {
     for (int i=0; i<5; i++)
     {
         boost::asio::ip::tcp::iostream tcp_stream("127.0.0.1", "6688");
         std::string Str;
         getline(tcp_stream, Str);//tcp流中读取一行数据
         std::cout << Str << std::endl;
     }

     return 0;
 }

服务端:

#include <boost/asio.hpp>

int main()
{
    boost::asio::io_service IOService;
    boost::asio::ip::tcp::endpoint EndPoint(boost::asio::ip::tcp::v4(), 6688);
    boost::asio::ip::tcp::acceptor Acceptor(IOService, EndPoint);

    for (; ;)
    {
        boost::asio::ip::tcp::iostream tcp_iostream;
        Acceptor.accept(*tcp_iostream.rdbuf());
        tcp_iostream << "hello tcp_stream" << std::endl;
    }

    return 0;
}

UDP协议通信

我们都知道TCP是3次握手可靠连接,而UDP通信不需要建立连接的过程。使用send_to()和receive_from()就可以直接通过端点发送数据。

#include <boost/asio.hpp>
#include <iostream>

int main()
{
    std::cout << "UDP server start." << std::endl;
    boost::asio::io_service IOService;
    boost::asio::ip::udp::socket Socket(IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 6699));

    for (; ;)
    {
        char Buf[1];//临时缓冲区
        boost::asio::ip::udp::endpoint EndPoint;//要接受连接的远程端点
        boost::system::error_code ErrorCode;
        Socket.receive_from(boost::asio::buffer(Buf), EndPoint, 0 , ErrorCode);//阻塞等待远程连接,连接的端点信息保存在EndPointif (ErrorCode && ErrorCode != boost::asio::error::message_size)
        {
            throw boost::system::system_error(ErrorCode);
        }

        std::cout << "Send to " << EndPoint.address() << std::endl;
        Socket.send_to(boost::asio::buffer("Hello asio udp"), EndPoint);//发送数据
    }

    return 0;
}
#include <boost/asio.hpp>
#include <iostream>

int main()
{
    std::cout << "Client start" << std::endl;
    boost::asio::io_service IOService;

    boost::asio::ip::udp::endpoint SendEndPoint(boost::asio::ip::address::from_string("127.0.0.1"), 6699);
    boost::asio::ip::udp::socket Socket(IOService);
    Socket.open(boost::asio::ip::udp::v4());//ipv4打开socket
    char Buf[1];
    Socket.send_to(boost::asio::buffer(Buf), SendEndPoint);//向连接端点发送连接数据
    std::vector<char> V(100, 0);
    boost::asio::ip::udp::endpoint RecvEndPoint;
    Socket.receive_from(boost::asio::buffer(V), RecvEndPoint);//接受数据

    std::cout << "Receive from: " << RecvEndPoint.address() << std::endl;
    std::cout << &V[0] << std::endl;

    return 0;
}

这里写图片描述

【参考资料】
【1】Boost 程序库完全开发指南
【2】http://zh.highscore.de/cpp/boost/asio.html
【3】https://www.gitbook.com/book/mmoaay/boost-asio-cpp-network-programming-chinese/details

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值