全部的異步操做:異步網絡讀寫,異步時鐘,都在io_service.run()
時進行輪詢。有趣的是,io_service
在線程利用方面下了很大的功夫,你能夠在主線程創建它的實例,可是在多個線程裏面run
,io_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),這樣就能夠在等待中醒來,進行調度了。網絡
庫中提供了mutable_buffer
和const_buffer
兩種單個緩衝,以及 mutable_buffers_1
和const_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
是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)
函數管理的,這個函數在overflow
和prepare
處會調用,用於保證輸出緩衝區裏有至少n字節的空間。
緩衝區的大小是沒有限制的,max_size
通常是size_t
能表示的最大數字,至關於內存的極限。
std::istream(sb).ignore(n)
,sb.gbump(n)
,sb.consume(n)
有相同的效果,可是ignore
和consume
有防下溢機制。
值得注意的是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);
基本上都是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 }); });