1. 程式人生 > >boost--asio

boost--asio

.get time wait lin pso 函數 需要 inf 建立連接

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