Boost::Asio庫詳解

io_service

全部的異步操做:異步網絡讀寫,異步時鐘,都在io_service.run()時進行輪詢。有趣的是,io_service在線程利用方面下了很大的功夫,你能夠在主線程創建它的實例,可是在多個線程裏面runio_service很擅長於將須要執行的回調函數分配到空閒線程當中。ios

io_service爲了跨平臺,不得不在Linux下也實現Proactor模型。再說一遍,Asio的實現是Proactor模型。在Linux廣泛是Reactor的狀況下模擬出Proactor,做者也是很辛苦啊。run裏面同時須要調度來自用戶的Handler和來自操做系統的IO請求。安全

這裏有一個Service的概念。Service是從一類IO操做中抽象出來的一些共同的地方。在你調用 socket.async_read之類的操做以後,事實上它將真正跟操做系統打交道的操做託管給了 basic_stream_socket_service服務器

io_service的調度有多種實現,在 epoll 實現裏,做者很機智地把 "有空閒進程" 事件轉化爲一個水平觸發(Level Trigger),這樣就能夠在等待中醒來,進行調度了。網絡

buffer

庫中提供了mutable_bufferconst_buffer兩種單個緩衝,以及 mutable_buffers_1const_buffers_1兩種緩衝序列,asio提供了一些操做:異步

  • buffer_cast<char*>(mb)單個緩衝轉成指針socket

  • buffer_size(buf):取得緩衝區大小async

  • buffer_copy(bufs, bufd):緩衝區(包括單個和序列)之間複製儘量多的元素,它是安全的,不會出現溢出的狀況tcp

//`buffer` can wrap nearly everything
vector<char> underlying_buffer(50);
auto buf1 = buffer(underlying_buffer);

char underlying_string[] = "Hello";
auto buf2 = buffer(underlying_string);

buffer_copy(buf2, buf1); // returns 6

std::string s(buffer_begin(buf1), buffer_end(buf1));

streambuf

streambuf是Asio能靈活地異步調控數據的關鍵。它能自動增加和回收consumed space。在使用的時候有這些要點:函數

  • streambuf分爲input sequence和output sequence兩部分,這都是繼承自std::streambuf的理念。this

  • data()來獲取輸入序列(常緩衝),prepare(n)來獲取輸出序列(變緩衝)。

  • commit(n)來將從輸出序列提交到輸入緩衝,用 consume(n) 來將輸入緩衝中的數據丟棄,表明已經處理完畢。

  • read數據的過程:先prepare好固定大小的緩衝區,而後buffer_copy進去一些數據,拷貝了多少數據就commit多少數據。而後再從prepare開始,拷貝到手頭上的數據沒有了爲止。

  • streambuf不可拷貝,因此乖乖傳引用或者指針吧。

這裏有一些文檔沒有說明可是須要了解的細節:

  • 自動增加的功能是經過reserve(n)函數管理的,這個函數在overflowprepare處會調用,用於保證輸出緩衝區裏有至少n字節的空間。

  • 緩衝區的大小是沒有限制的,max_size通常是size_t能表示的最大數字,至關於內存的極限。

  • std::istream(sb).ignore(n)sb.gbump(n)sb.consume(n)有相同的效果,可是ignoreconsume有防下溢機制。

async_read_xxxx

值得注意的是async_read_until(socket, streambuf, condition, handler)函數,給咱們處理數據分段帶來了極大的方便。它內建了4種條件決定read到何時中止 :

  • 出現了某個字符

  • 出現了某條字串

  • 符合了某條 Regular Expression

  • 符合了某條謂詞的條件

注意:這裏的"出現"指的是streambuf的input sequence中出現,也就是若是本來streambuf中的內容已經符合條件,則async_read_until將當即呼叫回調。
推論:某些庫的until是不包含結束符的,好比readLine沒有換行符。可是asio是包含的。

using boost::system::error_code;

// this piece of code shows how to read Chunked Tranfer-Encoding
streambuf sbuf;
std::string content;

void chunked_handler(const error_code& ec, std::size_t sz)
{
    std::istream is(sbuf);
    std::size_t chunk_size;

    is >> std::hex >> chunk_size;
    std::assert(is.get() == '\r' && is.get() == '\n');

    async_read(socket, sbuf,
        [chunk_size] (const error_code& ec, std::size_t) {
            return chunk_size + 2 - sbuf.size();}, 
        [chunk_size] (const error_code& ec, std::size_t) {
            std::istream is(sbuf);
            boost::scoped_array<char> cbuf(new char[chunk_size]);

            is.read(cbuf, chunk_size);
            content += std::string(cbuf, chunk_size);

            std::assert(is.get() == '\r' && is.get() == '\n');

            async_read_until(socket, sbuf,
                std::string("\r\n"), chunked_handler); });
}

async_read_until(socket, sbuf,
    std::string("\r\n"), chunked_handler);

Networking

基本上都是ip::tcp裏的東西了。acceptor至關於Java裏的 ServerSocket,沒有任何數據傳輸的功能,只有做爲服務器監聽端口接收鏈接的功能。socket就是通常意義上的socket了,沒有什麼特別的。iostream 是一個很聰明的設計,你能夠用 operator >>operator << 來進行數據傳輸了,不過是同步阻塞的。這是使用上的一些要點:

  • socket::read_some是非阻塞的,socket::async_read_some會當即回調。read some的意義是,有多少讀多少,沒有就不讀直接返回。write_some同理,若是網絡不順暢致使內核緩衝區滿的話,返回0都是有可能的。

  • async_***的話不少時候要靈活運用 std::bind了。

  • query 中的服務參數service能夠是端口或者服務名,定義在/etc/services中。

  • socket::connect 僅對一個endpoint進行鏈接,connect可對迭代器所指示的一系列endpoint進行鏈接,直到有其中一個成功鏈接爲止。

using boost::system::error_code;

// server: accept
tcp::ip::socket sock(service);
tcp::ip::acceptor acc(service);
tcp::ip::endpoint ep(tcp::ip::v4(), 8080);

acc.async_accpet(sock, eq, [sock] (const error_code&) {
    // new connection handling
});

//client: resolve + connect
tcp::ip::resolver resolver(service);
tcp::ip::resolver::query qurey("www.example.com", "http" /*"80"*/);

resolver.async_resolve(query, [] (
        const error_code& ec,
        tcp::ip::resolver::iterator i) {
    tcp::ip::socket sock(service);

    tcp::ip::async_connect(i, [sock] (
            const error_code& ec,
            tcp::ip::resolver::iterator i) {
        // new connection handling
    });
});
相關文章
相關標籤/搜索