boost--asio

Posted 一点一滴成长

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了boost--asio相关的知识,希望对你有一定的参考价值。

1、asio综述

  asio的核心类是io_service,它相当于前摄器模式的Proactor角色,在异步模式下发起的I/O操作,需要定义一个用于回调的完成处理函数,当I/O完成时io_service会调用注册的完成处理函数。通过调用io_service的成员函数run()来启动前摄器的事件处理循环,阻塞等待所有的操作完成并分派事件。在异步模式下,如果不调用run()就没有了等待异步操作完成的机制,回调函数将得不到执行。

  asio不直接使用线程,而是定义了一个自己的线程概念:strand,它保证在多线程环境中不使用互斥量代码可以正确执行,io_service::strand::wrap()可以封装一个函数在strand中执行。

  asio中的两个类mutable_buffer和const_buffer用来封装缓冲区,可以被安全的用在异步读写中。asio通常不能直接使用数组、string、vector等来作为收发数据的缓冲区,函数buffer()能够包装常用的数据类型(数组、string、vector等)用于asio的数据收发。

  asio的函数有两种重载形式:一种是有一个error_code(boost::system库中)的输出参数,调用函数后可以检查这个参数以判断是否发生了错误;一种是没有error_code参数,但发生了错误会抛出system_error(boost::system库中)异常,调用函数的时候应该使用try-catch块来捕获错误。

  使用asio需要包含头文件"boost\asio.hpp",在VC下还要添加以下定义避免编译警告:

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"

2、定时器

  deadline_timer是asio中的定时器类,它有两种形式的构造函数,第一个参数都是一个io_service对象,第二个参数可以是posix_time的绝对时间或者是自当前时间开始的一个时间段。创建定时器并指定终止时间后它就会立即开始计时,如果创建定时器时不指定终止时间,那么定时器不会开始工作,可以使用成员函数expires_at()或expires_from_now()设置终止时间,调用wait()同步等待或async_wai()设置异步等待,并注册完成时的回调handler。

  deadline_timer的成员函数expires_at()可以获得计时器终止的绝对时间,成员函数cancel()用来取消异步操作。

  deadline_timer的一些功能需要包含头文件"boost\date_time\posix_time\posix_time.hpp"。

  下面为一个同步定时器的使用:

技术分享
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
#include "boost\date_time\posix_time\posix_time.hpp"

int main()
{
    boost::asio::io_service ios;
    boost::asio::deadline_timer dt(ios, boost::posix_time::seconds(3));
    cout << dt.expires_at() << endl; //输出终止时间
    dt.wait(); //一直等待定时器终止
    
    cout << "here" << endl;
    return 0;
}
View Code

  以下是一个异步定时器的使用:

技术分享
#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
#include "boost\date_time\posix_time\posix_time.hpp"

void Printf(const boost::system::error_code& e, int n)
{
    cout << "Printf func, thread id: " << n << endl;
    cout << boost::this_thread::get_id() << endl;
}

int main()
{
    cout << "main thread id: " << boost::this_thread::get_id() << endl;
    boost::asio::io_service ios;
    boost::asio::deadline_timer dt(ios, boost::posix_time::seconds(3));
    int num = 999;
    dt.async_wait(bind(Printf, _1, num)); //通知io_service异步的执行I/O操作,并注册回调函数后立即返回
    ios.run(); //启动Proactor的事件处理循环

    cout << "here" << endl;
    return 0;
}
View Code

3、同步socket通信

  asio主要支持TCP、UDP、ICMP协议,在名字空间boost::asio::ip里提供了大量的网络通信的功能类和函数。ip::address是IP地址类,在ip::tcp类里包含了网络通信主要的类,如:端点类endpoint、套接字类socket、流类iostream、接受器acceptor、解析器resolver等。

  同步socket处理客户端:

技术分享
#include <iostream>
#include <vector>

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"

using namespace std;

void main()
try
{
    cout << "client start." << endl;

    boost::asio::io_service ios; //asio程序必需的io_service对象
    boost::asio::ip::tcp::socket sock(ios); //创建socket对象
    boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 6688); //创建连接点

    sock.connect(endpoint); //连接到端点

    vector<char> str(100, 0);
    sock.read_some(boost::asio::buffer(str)); //接收数据,使用buffer()包装缓冲区
    cout << "recive from " << sock.remote_endpoint().address().to_string() << ": " << &str[0] << endl; //输出对方地址及发送内容

    //sock.close();  //sock析构的时候会自动调用close()

    getchar();
}
catch (exception& e)
{
    cout << e.what() << endl;
}
View Code

  同步socket处理服务端:

技术分享
#include <iostream>

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"

using namespace std;
typedef boost::asio::io_service io_service_boost;
typedef boost::asio::ip::tcp::acceptor acceptor_boost;
typedef boost::asio::ip::tcp::endpoint endpoint_boost;
typedef boost::asio::ip::tcp::socket socket_boost;

void main()
try
{
    cout << "server start, bind ";

    io_service_boost ios; //asio程序必需的io_service对象
    acceptor_boost acceptor(ios, endpoint_boost(boost::asio::ip::tcp::v4(), 6688)); //创建acceptor对象,绑定本机IP及6688端口
    cout << acceptor.local_endpoint().address() << ": " << acceptor.local_endpoint().port() << endl; //输出绑定的地址信息

    while (true)
    {
        socket_boost sock(ios); //创建socket对象
        acceptor.accept(sock); //等待客户连接
        cout << sock.remote_endpoint().address() << ": " << sock.remote_endpoint().port() << " now connect." << endl; //输出对方信息

        sock.write_some(boost::asio::buffer("hello!")); //发送数据,使用buffer()包装缓冲区

        //sock.close();  //sock析构的时候会自动调用close()
    }
}
catch(exception& e)
{
    cout << e.what() << endl;
}
View Code

4、异步socket通信

  异步socket通信的函数名称比同步的函数多了async_,且应该设置异步操作完成后的回调函数,以下为使用异步socket通信的服务端示例:

技术分享
#include <iostream>

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\smart_ptr.hpp"
#include "boost/bind.hpp"
#include "boost\asio.hpp"

using namespace std;
typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::tcp::acceptor acceptor_t;
typedef boost::asio::ip::tcp::endpoint endpoint_t;
typedef boost::asio::ip::tcp::socket socket_t;
typedef boost::asio::ip::tcp tcp_t;
typedef boost::shared_ptr<socket_t> spSock_t;

class CServer
{
public:
    CServer(io_service_t& io)
    : ios(io) //使用传入的io_service
    , acceptor(ios, endpoint_t(tcp_t::v4(), 6688)) //绑定本机IP及6688端口
    {
        accept_async();
    }
    virtual ~CServer(){}
public:
    void accept_async() //发起一个异步接受连接
    {
        spSock_t spSock(new socket_t(ios));
        acceptor.async_accept(*spSock, boost::bind(&CServer::accept_handler, this, _1, spSock));
    }
    void accept_handler(const boost::system::error_code& ec, spSock_t spSock) //accept完成后的回调
    {
        if (ec) //检测错误码
            return;

        cout << "a client connect, form ";
        cout << spSock->remote_endpoint().address() << ": " << spSock->remote_endpoint().port() << endl; //输出客户地址和端口号

        spSock->async_write_some(boost::asio::buffer("hello"), boost::bind(&CServer::send_handler, this, _1, spSock)); //发送数据

        accept_async(); //再次发起一个异步接受连接
    }
    void send_handler(const boost::system::error_code& ec, spSock_t spSock) //发送数据完成的回调
    {
        cout << "send msg complete" << endl;
    }
private:
    io_service_t& ios;
    acceptor_t acceptor;
};

void main()
{
    cout << "server start." << endl;

    io_service_t ios;
    CServer srv(ios);
    ios.run(); //开启事件处理循环
}
View Code

5、解析器resolver

  resolver的主要功能是通过主机名获得主机地址等信息,相当于getaddrinfo()。以下封装了一个使用域名来连接服务器的函数resolv_connect():

技术分享
#include <iostream>
using namespace std;

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
#include "boost\lexical_cast.hpp"

typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::tcp::socket socket_t;
typedef boost::asio::ip::tcp::resolver resolver_t;
typedef boost::system::error_code error_code_t;

void resolv_connect(socket_t& sock, const char*name, int port)
{
    resolver_t rlv(sock.get_io_service());
    resolver_t::query qry(name, boost::lexical_cast<string>(port));

    resolver_t::iterator iter = rlv.resolve(qry);
    resolver_t::iterator end; //逾尾迭代器
    error_code_t ec = boost::asio::error::host_not_found;
    for (; ec && iter != end; ++iter) //开始迭代端点
    {
        sock.close();
        sock.connect(*iter, ec);
    }
    if (ec)
    {
        cout << "can not connect." << endl;
        throw boost::system::system_error(ec);
    }
    else
    {
        cout << "connect sucess." << endl;
    }
}

int main()
{
    io_service_t ios;
    socket_t sock(ios);
    resolv_connect(sock, "www.baidu.com", 80);
    sock.close();

    return 0;
}
View Code

  resolver不仅能够解析域名,也支持使用IP地址和服务名,如:

boost::asio::ip::tcp::resolver::query qry("127.0.0.1", "http");

6、超时处理

  如果要实现超时处理,应该在异步调用后声明一个deadline_timer对象,然后设置deadline_timer的等待时间和回调函数就可以了。如下代码示例了异步连接服务器,如果超过5秒还没有完成则关闭socket:

技术分享
#include <iostream>
using namespace std;

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
#include "boost\lexical_cast.hpp"
#include "boost\bind.hpp"

typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::tcp::socket socket_t;
typedef boost::asio::ip::tcp::resolver resolver_t;
typedef boost::system::error_code error_code_t;
typedef boost::asio::ip::tcp::endpoint endpoint_t;
typedef boost::asio::ip::address address_t;

void connect_timeout(const error_code_t&, socket_t* sock)
{
    sock->close();
}

int main()
{
    io_service_t ios;
    socket_t sock(ios);
    endpoint_t ep(address_t::from_string("127.0.0.1"), 6688);

    sock.async_connect(ep, connect_handler);
    boost::asio::deadline_timer t(ios, boost::posix_time::seconds(5)); //5秒等待时间
    t.async_wait(boost::bind(connect_timeout, _1, &sock)); //超时回调

    ... //其它操作

    ios.run();

    return 0;
}
View Code

7、流操作

  对于TCP连接,可以使用asio::ip::tcp::iostream来代替socket,它也是std::basic_iostream的子类,可以像标准流一样来操作socket,它内部集成了connect和resolver域名解析功能,使用示例:

技术分享
    #include <iostream>
    using namespace std;

    #ifdef _MSC_VER
    #define _WIN32_WINNT 0X0501
    #endif
    #include "boost\asio.hpp"

    typedef boost::asio::io_service io_service_t;
    typedef boost::asio::ip::tcp::socket socket_t;
    typedef boost::asio::ip::tcp::resolver resolver_t;
    typedef boost::system::error_code error_code_t;
    typedef boost::asio::ip::tcp::endpoint endpoint_t;
    typedef boost::asio::ip::address address_t;
    typedef boost::asio::ip::tcp::acceptor acceptor_t;
    typedef boost::asio::ip::tcp tcp_t;

    //客户端
    {
        ...

        boost::asio::ip::tcp::iostream tcp_stream("127.0.0.1", 6688); //连接到本机6688端口
        string str;
        getline(tcp_stream, str); //从数据流中读取一行数据
        cout << str << endl;
        ...
    }

    //服务端
    {
        ...

        io_service_t ios;
        endpoint_t ep(tcp_t::v4(), 6688);
        acceptor_t acceptor(ios, ep);

        boost::asio::ip::tcp::iostream tcp_stream;
        acceptor.accept(*tcp_stream.rdbuf());
        tcp_stream << "hello\r\n" << endl;

        ...
    }

    return 0;
View Code

8、UDP通信

  UDP是无连接的,所以通信前不需要建立连接,使用send_to()和receive_from()来收发数据。

  UDP服务端示例:

技术分享
#include <iostream>
using namespace std;

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"

typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::udp::socket socket_t;
typedef boost::asio::ip::udp::endpoint endpoint_t;
typedef boost::asio::ip::udp udp_t;
typedef boost::system::error_code error_code_t;
typedef boost::asio::ip::address address_t;

int main()
{
    cout << "udp server start." << endl;
    
    io_service_t ios;
    socket_t sock(ios, endpoint_t(udp_t::v4(), 6699));

    for (;;)
    {
        char buf[2] = {0};
        endpoint_t ep;
        error_code_t ec;
        sock.receive_from(boost::asio::buffer(buf), ep, 0, ec);
        if (ec && ec != boost::asio::error::message_size)
        {
            throw boost::system::system_error(ec);
        }
        cout << "recv from " << ep.address() << ": " << buf << endl;
    }

    return 0;
}
View Code

  UDP客户端示例:

技术分享
#include <iostream>
using namespace std;

#ifdef _MSC_VER
#define _WIN32_WINNT 0X0501
#endif
#include "boost\asio.hpp"
#include "boost\lexical_cast.hpp"
#include "boost\bind.hpp"

typedef boost::asio::io_service io_service_t;
typedef boost::asio::ip::udp::socket socket_t;
typedef boost::asio::ip::udp::endpoint endpoint_t;
typedef boost::asio::ip::udp udp_t;
typedef boost::system::error_code error_code_t;
typedef boost::asio::ip::address address_t;

int main()
{
    cout << "udp client start." << endl;

    io_service_t ios;
    socket_t sock(ios);
    sock.open(udp_t::v4());

    char buf[1];
    buf[0] = a;
    endpoint_t send_ep(address_t::from_string("127.0.0.1"), 6699);
    sock.send_to(boost::asio::buffer(buf), send_ep);
    sock.close();

    getchar();
    return 0;
}
View Code

9、串口通信

  aiso也支持串口通信,通过使用serial_port类。

 

以上是关于boost--asio的主要内容,如果未能解决你的问题,请参考以下文章

websocketpp 和 boost.asio 有啥区别?

boost::asio::read 函数挂起

boost::asio::system_timer 不会忽略信号

Boost::asio::async_read 不会在条件下停止

boost :: asio :: steady_timer在boost :: dll中不起作用

boost/asio/ssl 抛出“未定义的引用”错误