Boost.Asio技術文檔

Christopher Kohlhoffhtml

Copyright © 2003-2012 Christopher M. Kohlhoff前端

以Boost1.0的軟件受權進行發佈(見附帶的LICENSE_1_0.txt文件或從http://www.boost.org/LICENSE_1_0.txt)react

Boost.Asio是用於網絡和低層IO編程的跨平臺C++庫,爲開發者提供了C++環境下穩定的異步模型.ios

綜述

基本原理

應用程序與外界交互的方式有不少,可經過文件,網絡,串口或控制檯.例如在網絡通訊中,完成獨立的IO操做須要很長時間.對應用程序開發者提出了一個挑戰.c++

Boost.Asio 提供了管理需長時間運行操做的工具,但沒必要涉及到線程的併發模式和顯式鎖定.程序員

Boost.Asio 庫使用C++來實現,提供如網絡編程等經常使用的操做系統接口. Boost.Asio實現了以下目標:編程

· 可移植性Portability.庫支持一系列的經常使用系統操做,具備穩定的跨平臺特性.windows

· 可擴展性Scalability.庫能夠幫助開發者構建數千併發鏈接的網絡應用程序.asio庫實現的每一個系統操做都使用了最具擴展性的機制.後端

· 效率Efficiency.庫支持發散聚合IO(scatter-gather I/O)等技術,使應用程序儘可能少的拷貝數據.設計模式

· 能夠像BSD Sockets同樣經過API函數創建模型概念. BSD Socket API應用普遍,有不少相關的程序庫.其餘編程語言一般也使用其簡單的網絡API接口.出於這些緣由, Boost.Asio一樣使用這種已存在的技術進行構建.

· 易於使用Ease of use.庫提供了工具箱而不是框架來下降新手入門門檻.使用最少時間投入來學習基本的規則和方針.然後,庫的使用者只須要理解用到的特定函數便可.

· 基於更多的抽象.Basis for further abstraction.asio庫提供了高層次的抽象容許其餘開發者對庫進行擴展.例如,實現經常使用的HTTP協議.

雖然Boost.Asio一開始定位於網絡通訊,但其異步IO的概念已經擴展到了如串口通訊,文件描述符等其餘操做系統的IO操做方面.

核心概念和功能

解析Boost.Asio

Boost.Asio 可用於如socket等IO對象的同步或異步操做.在使用Boost.Asio前首先了解一下Boost.Asio概念圖, 以及與應用程序的相互集成方式.

第一個範例,看看處理socket鏈接的狀況.首先從同步操做開始.

應用程序必須有一個io_service對象. io_service對象負責鏈接應用程序與操做系統的IO服務.

boost::asio::io_service io_service;

要執行IO操做應用程序須要一個像TCP Socket的IO對象:

boost::asio::ip::tcp::socket socket(io_service);

然後執行同步鏈接操做,發送以下事件:

1. 應用程序調用IO對象的初始化鏈接操做:

socket.connect(server_endpoint);

2. IO對象向io_service 提出請求.

3. io_service 調用操做系統的功能執行鏈接操做.

4. 操做系統向io_service 返回執行結果.

5. io_service將錯誤的操做結果翻譯爲boost::system::error_code類型. error_code可與特定值進行比較,或做爲boolean值檢測(false表示無錯誤).結果再傳遞給IO對象.

6. 若是操做失敗,IO對象拋出boost::system::system_error類型的異常.開始操做的代碼以下所示:

boost::system::error_code ec;

socket.connect(server_endpoint, ec);

然後error_code類型的變量ec被賦予操做的結果值,但不會拋出異常.

對於異步操做,事件順序有所不一樣.

1. 應用程序調用IO對象進行鏈接操做:

socket.async_connect(server_endpoint, your_completion_handler);

your_completion_handler函數的簽名爲:

void your_completion_handler(const boost::system::error_code& ec);

執行的異步操做須要嚴格的函數簽名.每種操做的合法形式可見參考文檔.

2. IO對象請求io_service的服務.

3. io_service 通知操做系統其須要開始一個異步鏈接.

時序過程:(在同步狀況下等待包括鏈接操做時間.)

4. 操做系統指示鏈接操做完成, io_service從隊列中獲取操做結果.

5. 應用程序必須調用io_service::run()(io_service類似的成員函數)以便於接收結果.調用io_service::run()會阻塞應用程序等待未完成的異步操做,所以可在啓動第一個異步操做後調用這個函數.

6. 調用io_service::run()後,io_service返回一個操做結果,並將其翻譯爲error_code,傳遞到事件處理器中.

這是Boost.Asio的簡單圖形.更多特性可從文檔中獲取,如使用Boost.Asio執行其餘類型的異步操做.

Proactor設計模式:無線程併發

Boost.Asio庫同時支持同步和異步操做.異步支持基於Proactor設計模式.下面討論這種方式與同步操做及Reactor方式相比的優缺點.

Proactor和Boost.Asio

如今討論Proactor不依賴於平臺細節的特性.

Proactor 設計模式

— 異步操做:定義一個異步執行操做,如Socket異步讀寫.

— 異步操做處理器:執行異步操做,並在操做完成後執行完成事件隊列中的隊列事件.從更高層次上說,服務端的stream_socket_service 就是一個異步操做處理器.

— 完成事件隊列:存放完成事件,這些事件會被異步事件信號分離器移出隊列.

— 完成句柄:異步操做完成後執行的函數.這是一個函數對象,一般使用boost::bind建立.

— 異步事件信號分離器:在完成事件隊列中阻塞等待事件,受信後向調用者返回完成事件.

— Proactor :調用異步事件信號分離器將相應的處理事件移出隊列,併爲這個事件分配一個完成句柄(如調用函數對象).這個功能封裝在io_service類中.

— 初始化器: 執行特定程序代碼啓動異步操做.初始化器經過如basic_stream_socket等高層次接口與異步操做處理器交互,並返回stream_socket_service等類型的服務端代理.

使用Reactor的實現

在不少平臺上Boost.Asio按照Reactor機制實現Proactor設計模式,如select,epoll或kqueue等.相對於Proactor,其實現方式以下:

— 異步操做處理器:Reactor使用select,epoll或kqueue機制實現.若是reactor指示運行操做的資源已經就位,處理器執行異步操做並在完成事件隊列中插入相應的完成句柄.

— 完成事件隊列:存放完成句柄的鏈表(如函數對象).

— 異步事件信號分離器:在事件或條件變量上等待,直到完成事件隊列中的完成句柄可用.

實現Windows的重疊IO

在Windows NT,2000,和XP系統中, Boost.Asio使用重疊IO高效實現了Proactor設計模式:

— 異步操做處理器:由操做系統實現.調用如AcceptEx等重疊函數進行初始化.

— 完成事件隊列:由操做系統實現,與IO的完成端口相關聯.每一個io_service實例對應一個IO完成端口.

— 異步事件信號分離器:由Boost.Asio從隊列中移除事件及其相關聯的完成句柄.

優勢

— 可移植性Portability:不少操做系統都提供原生的異步操做IO API(如Windows中的重疊IO),是開發者開發高性能網絡應用程序的最佳選擇.ASIO庫儘量使用原生的異步IO.若是原生支持不可用,ASIO庫可以使用同步事件信號分離器實現典型的Reactor模式,如POSIX的select().

— 去除多線程併發的耦合

應用程序使用異步方式調用長時間運行的操做.但應用程序沒必要爲增長的併發而顯式的建立大量線程.

每一個鏈接一個線程的實現策略(僅須要同步的方式)--太多的線程會產生大量的CPU上下文切換,同步和數據移動,致使系統效率下降.異步操做建立最小數量的操做系統線程,避免線程上下文切換的代價--CPU是典型的有限資源,這時僅由激活的邏輯線程進行事件處理.

— 簡單的應用程序同步.

異步操做完成句柄能夠在已存在的單線程環境中進行設置,此時開發出來的應用程序邏輯清晰,沒必要涉及同步問題.

— 功能組合.

功能組合是指實現高層次的操做功能,如按特定格式發送消息.每一個功能的實現都是依賴於屢次調用底層的讀或寫操做.

例如, 一個包含固定長度包頭和變長包體的協議,包體長度在包頭中指定.假設read_message操做由兩個底層的讀來實現,第一次接收包頭,獲得長度,第二次接收包體.

若是用異步方式來組合這些功能,可將這些異步調用鏈接到一塊兒.前一個操做的完成句柄初始化下一個操做.使用時只須要調用封裝鏈中的第一個操做,調用者不會意識到這個高層次的操做是按異步操做鏈的方式實現的.

按此方式能夠很簡單的向網路庫中添加新的操做,開發出更高層次的抽象,如實現特定協議的功能和支持.

缺點

— 抽象複雜.

因爲操做初始化和完成在時間和空間上是分離的,增長了使用異步機制開發程序的難度.並且控制流顛倒使應用程序很難調試.

— 內存佔用.

在讀和寫過程當中必須獨佔緩衝區空間,然後緩衝區又變爲不肯定的,每一個併發操做都須要一個獨立的緩衝區.換句話說,在Reactor模式中,直到socket準備讀或寫時才須要緩衝區空間.

線程和Boost.Asio

線程安全

一般在併發操做中使用相互獨立的局部對象是安全的,但併發操做中使用同一個全局對象就不安全了.然而如io_service 等類型能夠保證併發操做中使用同一個對象也是安全的.

線程池

在多個線程中調用io_service::run()函數,就能夠建立一個包含這些線程的線程池,其中的線程在異步操做完成後調用完成句柄.也可經過調用io_service::post()實現橫跨整個線程池的任意計算任務來達到相同的目的.

注意全部加入io_service池的線程都是平等的,io_service能夠按任意的方式向其分配工做.

內部線程

這個庫的特定平臺實現會建立幾個內部線程來模擬異步操做.同時,這些線程對庫用戶是不可見的.這些線程特性:

  • 不會直接調用用戶的代碼
  • 阻塞全部信號

使用以下擔保來實現:

  • 異步完成句柄只會由調用io_service::run()的線程來調用.

所以,由庫的使用者來創建和管理投遞通知的線程.

緣由:

  • 在單線程中調用io_service::run(),用戶代碼避免了複雜的線程同步控制.例如,ASIO庫用戶可使用單線程實現伸縮性良好的服務端(特定用戶觀點).
  • 線程啓動後在其餘應用程序代碼執行前,ASIO庫用戶須要在線程中執行一些簡短的初始化操做.例如在線程中調用微軟的COM操做以前必須調用CoInitializeEx.
  • 將ASIO庫接口與線程的建立和管理進行了解耦,確保可在不支持線程的平臺中進行調用.

Strands:無顯式鎖定的線程

Strand被定義爲嚴格按順序調用(如無併發調用)事件句柄的機制.使用Strand能夠在多線程程序中同步執行代碼而無需顯式地加鎖(如互斥量等).

Strand能夠按以下兩種方式來隱式或顯式的應用:

· 僅在一個線程中調用io_service::run()意味着全部的事件句柄都執行在一個隱式的Strand下,由於io_service保證全部的句柄都在run()中被調用.

· 連接中只有一個相關的異步操做鏈(如半雙工的HTTP),句柄是不可能併發執行的.也是隱式Strand的狀況.

· 顯式Strand須要建立一個io_service::strand實例.全部的事件句柄函數對象都須要使用io_service::strand::wrap()進行包裝,或使用io_service::strand進行投遞或分發.

在組合的異步操做中,如async_read() 或 async_read_until(),若是完成句柄使用一個Strand管理,則其餘全部中間句柄都要由同一個Strand來管理.這能夠確保線程安全的訪問全部在調用者和組合的操做中共享的對象(例如socket中的async_read(),調用者能夠調用close()來取消操做).這是經過在全部中間句柄中添加一個鉤子函數實現的,在執行最終句柄前調用自定義的鉤子函數:

struct my_handler

{

  void operator()() { ... }

};

 

template<class F>

void asio_handler_invoke(F f, my_handler*)

{

  // Do custom invocation here.

  // Default implementation calls f();

}

io_service::strand::wrap()函數生成一個新的定義了asio_handler_invoke的完成句柄,以便於Strand管理函數對象的運行.

緩衝區

一般IO在連續的內存區域(緩衝區)上傳輸數據.這個緩衝區能夠簡單的認爲是包含了一個指針地址和一些字節的東西.然而,爲了高效的開發網絡應用程序, Boost.Asio支持分散聚合操做,須要一個或多個緩衝區:

  • 一個分散讀(scatter-read)接收數據並存入多緩衝區.
  • .一個聚合寫(gather-write)傳輸多緩衝區中的數據.

所以須要一個抽象概念表明緩衝區集合.爲此Boost.Asio定義了一個類(其實是兩個類)來表明單個緩衝區.可存儲在向分散集合操做傳遞的容器中.

此外可將緩衝區看作是一個具備地址和大小的字節數組, Boost.Asio區別對待可修改內存(叫作mutable)和不可修改內存(後者在帶有const聲明的存儲區域上建立).這兩種類型能夠定義爲:

typedef std::pair<void*, std::size_t> mutable_buffer;
typedef std::pair<const void*, std::size_t> const_buffer;

這裏mutable_buffer 能夠轉換爲const_buffer ,但反之不成立.

然而,Boost.Asio沒有使用上述的定義,而是定義了兩個類: mutable_buffer 和 const_buffer.目的是提供了不透明的連續內存概念:

  • 類型轉換上同std::pair.即mutable_buffer能夠轉換爲const_buffer ,但反之不成立.
  • 可防止緩衝區溢出.對於一個緩衝區實例,用戶只能建立另一個緩衝區來表明一樣的內存序列或子序列.爲了更加安全,ASIOI庫也提供了從數組(如boost::array 或 std::vector,或std::string)中自動計算緩衝區大小的機制.
  • 必須明確的調用buffer_cast函數進行類型轉換.應用程序中一般沒必要如此,但在ASIO庫的實現中將原始內存數據傳遞給底層的操做系統函數時必須如此.

最後將多個緩衝區存入一個容器中就能夠傳遞給分散聚合操做了.(如read()write()).爲了使用std::vector, std::list, std::vector 或 boost::array等容器而定義了MutableBufferSequence和ConstBufferSequence概念.

Streambuf與IoStream整合

類boost::asio::basic_streambuf從std::basic_streambuf繼承,將輸入輸出流與一個或多個字符數組類型的對象相關聯,其中的每一個元素能夠存儲任意值.這些字符數組對象是內部的streambuf對象,但經過直接存取數組中的元素使其可用於IO操做,如在socket中發送或接收:

  • streambuf 的輸入序列能夠經過data()成員函數獲取.函數的返回值知足ConstBufferSequence的要求.
  • streambuf 的輸出序列能夠經過prepare()成員函數獲取.函數的返回值知足MutableBufferSequence的要求.
  • 調用commit()成員函數將數據從前端的輸出序列傳遞到後端的輸入序列.
  • 調用consume()成員函數從輸入序列中移除數據.

streambuf 構造函數接收一個size_t的參數指定輸入序列和輸出序列大小的總和.對於任何操做,若是成功,增長內部數據量,超過這個大小限制會拋出std::length_error異常.

遍歷緩衝區序列的字節

buffers_iterator<>類模板可用於像遍歷連續字節序列同樣遍歷緩衝區序列(如MutableBufferSequence 或 ConstBufferSequence).並提供了buffers_begin() 和 buffers_end()幫助函數, 會自動推斷buffers_iterator<>的模板參數.

例如,從socket中讀取一行數據,存入std::string中:

boost::asio::streambuf sb;
...
std::size_t n = boost::asio::read_until(sock, sb, '\n');
boost::asio::streambuf::const_buffers_type bufs = sb.data();
std::string line(
    boost::asio::buffers_begin(bufs),
    boost::asio::buffers_begin(bufs) + n);

緩衝區調試

有些標準庫實現,如VC++8.0或其後版本,提供了叫作迭代器調試的特性.這意味着運行期會驗證迭代器是否合法.若是應用程序試圖使用非法的迭代器,會拋出異常.例如:

std::vector<int> v(1)
std::vector<int>::iterator i = v.begin();
v.clear(); // invalidates iterators
*i = 0; // assertion!

Boost.Asio 利用這個特性實現緩衝區調試.對於下面的代碼:

void dont_do_this()
{
std::string msg = "Hello, world!";
boost::asio::async_write(sock, boost::asio::buffer(msg), my_handler);
}

當調用異步讀或寫時須要確保此操做的緩衝區在調用完成句柄時可用.上例中,緩衝區是std::string變量msg.變量在棧中,異步完成前已通過期了.若是幸運的話程序崩潰,但更可能會出現隨機錯誤.

當緩衝區調試啓動後,Boost.Asio會存儲一個字符串的迭代器,一直保存到異步操做完成,而後解引用來檢查有效性.上例中在Boost.Asio調用完成句柄前就會看到一個斷言錯誤.

當定義_GLIBCXX_DEBUG 選項時,會自動在VS8.0及其之後版本和GCC中啓動這個特性.檢查須要性能代價,所以緩衝區調試只在debug生成時啓用.其餘編譯器可經過定義BOOST_ASIO_ENABLE_BUFFER_DEBUGGING選項來啓動.也可顯式的經過BOOST_ASIO_DISABLE_BUFFER_DEBUGGING選項中止.

流,短讀短寫

不少Boost.Asio中的IO對象都是基於流的.意味着:

  • 無消息邊界.被傳輸的數據就是一個連續的字節序列.
  • 讀寫操做可能會傳遞少許不須要的字節.這被稱爲短讀或短寫.

提供基於流IO操做模型的對象須要模擬以下幾個操做:

  • SyncReadStream, 調用成員函數read_some()執行同步讀操做.
  • AsyncReadStream, 調用成員函數async_read_some()執行異步讀操做.
  • SyncWriteStream, 調用成員函數write_some()執行同步寫操做.
  • AsyncWriteStream, 調用成員函數async_write_some()執行異步寫操做.

基於流的IO對象包括ip::tcp::socket, ssl::stream<>, posix::stream_descriptor, windows::stream_handle等等.

一般程序須要傳遞指定數量的字節數據.啓動操做後就會發生短讀或短寫,直到全部數據傳輸完畢.Boost.Asio提供了通用函數來自動完成這些操做: read(), async_read(), write() 和 async_write().

爲何EOF是一個錯誤

  • 流終止會致使read, async_read, read_until or async_read_until 函數違反約定.如要讀取N個字節,但因爲遇到EOF而提早結束.
  • EOF錯誤能夠區分流終止和成功讀取0個字節.

 

Reactor風格操做

有時應用程序必須整合第三方的庫來實現IO操做.爲此,Boost.Asio提供一個可用於讀和寫操做的null_buffers類型.null_buffers直到IO對象準備好執行操做後纔會返回.

例如,以下代碼執行無阻塞讀操做:

ip::tcp::socket socket(my_io_service);
...
socket.non_blocking(true);
...
socket.async_read_some(null_buffers(), read_handler);
...
void read_handler(boost::system::error_code ec)
{
  if (!ec)
  {
    std::vector<char> buf(socket.available());
    socket.read_some(buffer(buf));
  }
}

Socket在全部平臺上都支持這種操做,是POSIX基於流的描述符合類.

基於行的傳輸操做

不少經常使用的網絡協議都是基於行的,即這些協議元素被字符序列"\r\n"限定.例如HTTP,SMTP和FTP.爲了便於實現基於行的協議,以及其餘使用分隔符的協議,Boost.Asio包括了read_until() 和 async_read_until()函數.

以下代碼展現在HTTP服務端使用async_read_until()接收客戶端的第一行HTTP請求:

class http_connection
{
  ...
 
  void start()
  {
    boost::asio::async_read_until(socket_, data_, "\r\n",
        boost::bind(&http_connection::handle_request_line, this, _1));
  }
 
  void handle_request_line(boost::system::error_code ec)
  {
    if (!ec)
    {
      std::string method, uri, version;
      char sp1, sp2, cr, lf;
      std::istream is(&data_);
      is.unsetf(std::ios_base::skipws);
      is >> method >> sp1 >> uri >> sp2 >> version >> cr >> lf;
      ...
    }
  }
 
  ...
 
  boost::asio::ip::tcp::socket socket_;
  boost::asio::streambuf data_;
};

Streambuf數據成員用於存儲在查找到分隔符前接收的數據.記錄分隔符之後的數據也是很重要的.這些保留在streambuf中的冗餘數據會被保留下來用於隨後調用的read_until()或async_read_until()函數進行檢查.

分隔符能夠是單個字符,一個std::string或一個boost::regex. read_until() 和 async_read_until()函數也可接收一個用戶定義函數做爲參數,獲取匹配條件.例如,在streambuf中讀取數據,遇到一個空白字符後中止.:

typedef boost::asio::buffers_iterator<
    boost::asio::streambuf::const_buffers_type> iterator;
 
std::pair<iterator, bool>
match_whitespace(iterator begin, iterator end)
{
  iterator i = begin;
  while (i != end)
    if (std::isspace(*i++))
      return std::make_pair(i, true);
  return std::make_pair(i, false);
}
...
boost::asio::streambuf b;
boost::asio::read_until(s, b, match_whitespace);

從streambuf中讀取數據,直到遇到匹配的字符:

class match_char
{
public:
  explicit match_char(char c) : c_(c) {}
 
  template <typename Iterator>
  std::pair<Iterator, bool> operator()(
      Iterator begin, Iterator end) const
  {
    Iterator i = begin;
    while (i != end)
      if (c_ == *i++)
        return std::make_pair(i, true);
    return std::make_pair(i, false);
  }
 
private:
  char c_;
};
 
namespace boost { namespace asio {
  template <> struct is_match_condition<match_char>
    : public boost::true_type {};
} } // namespace boost::asio
...
boost::asio::streambuf b;
boost::asio::read_until(s, b, match_char('a'));

is_match_condition<>類型會自動的對函數,及帶有嵌套result_type類型定義的函數對象的返回值值進行評估是否爲true.如上例所示,其餘類型的特性必須顯式指定.

自定義內存分配

不少異步操做須要分配對象,用來存儲與操做有關的狀態數據.例如,Win32的實現中須要將重疊子對象傳遞給Win32 API函數.

幸虧程序包含一個易於識別的異步操做鏈.半雙工協議(如HTTP服務)爲每一個客戶端建立一個單操做鏈.全雙工協議實現有兩個並行的執行鏈.程序運用這個規則能夠在鏈上的全部異步操做中重用內存.

假設複製一個用戶定義的句柄對象h,若是句柄的實現須要分配內存,應該有以下代碼:

void* pointer = asio_handler_allocate(size, &h);

一樣要釋放內存:

asio_handler_deallocate(pointer, size, &h);

這個函數實現了參數依賴的定位查找.asio空間中有這個函數的默認實現:

void* asio_handler_allocate(size_t, ...);
void asio_handler_deallocate(void*, size_t, ...);

實現了::operator new() 和 ::operator delete()功能.

函數實現保證了相關句柄調用前會發生內存從新分配,這樣就能夠實現同一個句柄上的異步操做重用內存.

在任意調用庫函數的用戶線程中均可調用自定義內存分配函數.實現保證庫中的這些異步操做不會併發的對句柄進行內存分配函數調用.實現要加入適當的內存間隔,保證不一樣線程中調用的內存分配函數有正確的內存可見性.

句柄跟蹤

爲調試異步程序,Boost.Asio提供了句柄跟蹤支持.當激活BOOST_ASIO_ENABLE_HANDLER_TRACKING定義,Boost.Asio向標準錯誤流中寫入調試輸出信息.輸出信息記錄異步操做及其句柄間的關係.

這個特性對調試頗有幫助,須要知道異步操做是如何被連接在一塊兒的,或什麼異步操做被掛起了.以下是HTTP服務輸出的調試信息,處理單個請求,然後按Ctrl+C退出:

@asio|1298160085.070638|0*1|signal_set@0x7fff50528f40.async_wait
@asio|1298160085.070888|0*2|socket@0x7fff50528f60.async_accept
@asio|1298160085.070913|0|resolver@0x7fff50528e28.cancel
@asio|1298160118.075438|>2|ec=asio.system:0
@asio|1298160118.075472|2*3|socket@0xb39048.async_receive
@asio|1298160118.075507|2*4|socket@0x7fff50528f60.async_accept
@asio|1298160118.075527|<2|
@asio|1298160118.075540|>3|ec=asio.system:0,bytes_transferred=122
@asio|1298160118.075731|3*5|socket@0xb39048.async_send
@asio|1298160118.075778|<3|
@asio|1298160118.075793|>5|ec=asio.system:0,bytes_transferred=156
@asio|1298160118.075831|5|socket@0xb39048.close
@asio|1298160118.075855|<5|
@asio|1298160122.827317|>1|ec=asio.system:0,signal_number=2
@asio|1298160122.827333|1|socket@0x7fff50528f60.close
@asio|1298160122.827359|<1|
@asio|1298160122.827370|>4|ec=asio.system:125
@asio|1298160122.827378|<4|
@asio|1298160122.827394|0|signal_set@0x7fff50528f40.cancel

每行的格式爲:

<tag>|<timestamp>|<action>|<description>

<tag>一直是@asio,用於在程序輸出中識別和提取句柄跟蹤消息.

<timestamp>是從UTC時間1970年一月一日起至今的秒數.

<action>有以下幾種形式:

>n

程序進入了第n個句柄.<description>描述句柄參數.

<n

程序退出第n個句柄.

!n

程序因爲異常退出第n個句柄.

~n

第n個句柄沒有執行就被銷燬了.一般是因爲io_service銷燬時異步操做尚未完成.

n*m

第n個句柄建立了一個生成第m個完成句柄的新異步操做.<description>描述什麼異步操做被啓動.

n

第n個句柄執行其餘操做.<description>顯示調用了哪些函數.一般只記錄close()和cancel()操做,由於其影響了異步操做掛起狀態.

當<description>顯示同步或異步操做,格式爲<object-type>@<pointer>.<operation>. 並以逗號間隔來顯示執行句柄的參數和值.

如上所示,每一個句柄都由惟一的數字進行標識.句柄跟蹤輸出的句柄編號是0的,表示操做不是這些句柄執行的.

可視化

句柄跟蹤輸出可使用包含在handlerviz.pl中的工具進行處理,建立可視化的界面.

網絡

TCP,UDP和ICMP

Boost.Asio 對TCP,UDP和ICMP提供了完整的支持.

TCP客戶端

使用resolver執行主機名稱解析,查詢主機和服務名稱並轉換爲一個或多個端點:

ip::tcp::resolver resolver(my_io_service);
ip::tcp::resolver::query query("www.boost.org", "http");
ip::tcp::resolver::iterator iter = resolver.resolve(query);
ip::tcp::resolver::iterator end; // End marker.
while (iter != end)
{
  ip::tcp::endpoint endpoint = *iter++;
  std::cout << endpoint << std::endl;
}

上面列表中包含的端點能夠是IPv4和IPv6端點,所以程序須要對每種狀況進行嘗試直到獲得一個可用的端點.這使程序不依賴於特定的IP版本.

爲了開發獨立與協議的程序,TCP客戶端可使用connect()async_connect()函數創建鏈接.這個操做將嘗試列表中的每一個端點直到Socket鏈接成功爲止.例如,簡單的調用:

ip::tcp::socket socket(my_io_service);
boost::asio::connect(socket, resolver.resolve(query));

將同時嘗試全部端點直到找到鏈接成功.一樣異步操做的方式爲:

boost::asio::async_connect(socket_, iter,
    boost::bind(&client::handle_connect, this,
      boost::asio::placeholders::error));
 
// ...
 
void handle_connect(const error_code& error)
{
  if (!error)
  {
    // Start read or write operations.
  }
  else
  {
    // Handle error.
  }
}

當特定端點可用,socket建立並鏈接.方式爲:

ip::tcp::socket socket(my_io_service);
socket.connect(endpoint);

使用成員函數receive(),async_receive(),send() 或 async_send()可將數據讀於或寫到TCP鏈接的Socket上.然而爲了快速的讀寫操做,一般使用read(),async_read(),write()async_write()函數進行操做.

TCP服務

程序使用接收器來接收到達的TCP鏈接:

ip::tcp::acceptor acceptor(my_io_service, my_endpoint);
...
ip::tcp::socket socket(my_io_service);
acceptor.accept(socket);

當成功接收一個socket鏈接(向如上例中的TCP客戶端),就能夠在其上讀取或寫入數據了.

UDP

UDP也使用來resolver解析主機名稱:

ip::udp::resolver resolver(my_io_service);
ip::udp::resolver::query query("localhost", "daytime");
ip::udp::resolver::iterator iter = resolver.resolve(query);
...

UDP綁定到了本地端點.以下代碼建立一個IPv4的UDP Socket,綁定到任意地址的12345端口上:

ip::udp::endpoint endpoint(ip::udp::v4(), 12345);
ip::udp::socket socket(my_io_service, endpoint);

使用成員函數receive_from(),async_receive_from(),send_to() 或 async_send_to()可在無鏈接的UPD Socket上讀出或寫入數據.對於鏈接的UDP Socket,可以使用receive(),async_receive(),send() 或 async_send()成員函數.

ICMP

如同TCP和UPD同樣,ICMP也是用resolver解析主機名稱:

ip::icmp::resolver resolver(my_io_service);
ip::icmp::resolver::query query("localhost", "");
ip::icmp::resolver::iterator iter = resolver.resolve(query);
...

ICMP Socket可能綁定到本地端點.以下代碼建立IPv6版本的ICMP Socket,並綁定到任意的地址:

ip::icmp::endpoint endpoint(ip::icmp::v6(), 0);
ip::icmp::socket socket(my_io_service, endpoint);

ICMP沒必要指定端口號.

使用成員函數receive_from(),async_receive_from(),send_to() 或 async_send_to()在無鏈接的ICMP上讀出或寫入數據.

其餘協議

其餘Socket協議的支持(如藍牙或IRCOMM)可按協議要求進行實現.

Socket IO流

Boost.Asio 中包含一個在Socket上實現的iostream類.將端點解析,協議無關等複雜特性的實現隱藏起來.要建立鏈接只需簡單的寫幾行代碼:

ip::tcp::iostream stream("www.boost.org", "http");
if (!stream)
{
  // Can't connect.
}

iostream 類能夠與接收器一塊兒建立簡單的服務端.例如:

io_service ios;
 
ip::tcp::endpoint endpoint(tcp::v4(), 80);
ip::tcp::acceptor acceptor(ios, endpoint);
 
for (;;)
{
  ip::tcp::iostream stream;
  acceptor.accept(*stream.rdbuf());
  ...
}

可以使用expires_at() 或 expires_from_now()函數設置超時期限.超時的Socket操做會將iostream設置爲"bad"狀態.

例如,一個簡單的客戶端程序:

ip::tcp::iostream stream;
stream.expires_from_now(boost::posix_time::seconds(60));
stream.connect("www.boost.org", "http");
stream << "GET /LICENSE_1_0.txt HTTP/1.0\r\n";
stream << "Host: www.boost.org\r\n";
stream << "Accept: */*\r\n";
stream << "Connection: close\r\n\r\n";
stream.flush();
std::cout << stream.rdbuf();

若是全部的Socket操做時間加起來超過60秒則失敗.

若是發生錯誤,可以使用iostream的error()成員函數獲取最近系統操做的錯誤碼:

if (!stream)
{
  std::cout << "Error: " << stream.error().message() << "\n";
}

注意

這個iostream模板只支持char,不支持wchar_t,不要用於這樣的代碼版本.

BSD Socket API和Boost.Asio

Boost.Asio 包含了實現BSD Socket API的低層次Socket接口的庫.在其餘語言中後者也做爲最基本的網絡API,如Java.底層次接口可用來開發高效率和高可伸縮性的應用程序.例如,程序員能夠更好的控制系統調用次數,避免基礎數據拷貝,最小化如線程等資源的使用等.

BSD Socket API包含了不安全和有錯誤傾向的方面.例如,使用整型數字表明socket缺少安全性. Boost.Asio 中使用不一樣的Socket類型表明不用的協議,如TCP對應的是ip::tcp::socket,UDP對應的ip::udp::socket.

下表是BSD Socket API和Boost.Asio的對比:

BSD Socket API 元素

Boost.Asio中等價內容

Socket描述符-int (POSIX)或SOCKET (Windows)

TCP: ip::tcp::socketip::tcp::acceptor

UDP: ip::udp::socket

basic_socket,basic_stream_socket,basic_datagram_socket,basic_raw_socket

in_addr, in6_addr

ip::address,ip::address_v4,ip::address_v6

sockaddr_in, sockaddr_in6

TCP: ip::tcp::endpoint

UDP: ip::udp::endpoint

ip::basic_endpoint

accept()

TCP: ip::tcp::acceptor::accept()

basic_socket_acceptor::accept()

bind()

TCP: ip::tcp::acceptor::bind()ip::tcp::socket::bind()

UDP: ip::udp::socket::bind()

basic_socket::bind()

close()

TCP: ip::tcp::acceptor::close()ip::tcp::socket::close()

UDP: ip::udp::socket::close()

basic_socket::close()

connect()

TCP: ip::tcp::socket::connect()

UDP: ip::udp::socket::connect()

basic_socket::connect()

getaddrinfo(), gethostbyaddr(), gethostbyname(), getnameinfo(), getservbyname(), getservbyport()

TCP: ip::tcp::resolver::resolve()ip::tcp::resolver::async_resolve()

UDP: ip::udp::resolver::resolve()ip::udp::resolver::async_resolve()

ip::basic_resolver::resolve(),ip::basic_resolver::async_resolve()

gethostname()

ip::host_name()

getpeername()

TCP: ip::tcp::socket::remote_endpoint()

UDP: ip::udp::socket::remote_endpoint()

basic_socket::remote_endpoint()

getsockname()

TCP: ip::tcp::acceptor::local_endpoint()ip::tcp::socket::local_endpoint()

UDP: ip::udp::socket::local_endpoint()

basic_socket::local_endpoint()

getsockopt()

TCP: ip::tcp::acceptor::get_option()ip::tcp::socket::get_option()

UDP: ip::udp::socket::get_option()

basic_socket::get_option()

inet_addr(), inet_aton(), inet_pton()

ip::address::from_string(),ip::address_v4::from_string(),ip_address_v6::from_string()

inet_ntoa(), inet_ntop()

ip::address::to_string(),ip::address_v4::to_string(),ip_address_v6::to_string()

ioctl()

TCP: ip::tcp::socket::io_control()

UDP: ip::udp::socket::io_control()

basic_socket::io_control()

listen()

TCP: ip::tcp::acceptor::listen()

basic_socket_acceptor::listen()

poll(), select(), pselect()

io_service::run(),io_service::run_one(),io_service::poll(),io_service::poll_one()

注意:包括異步操做.

readv(), recv(), read()

CP: ip::tcp::socket::read_some()ip::tcp::socket::async_read_some()ip::tcp::socket::receive()ip::tcp::socket::async_receive()

UDP: ip::udp::socket::receive()ip::udp::socket::async_receive()

basic_stream_socket::read_some(),basic_stream_socket::async_read_some(),basic_stream_socket::receive(),basic_stream_socket::async_receive(),basic_datagram_socket::receive(),basic_datagram_socket::async_receive()

recvfrom()

UDP: ip::udp::socket::receive_from()ip::udp::socket::async_receive_from()

basic_datagram_socket::receive_from(),basic_datagram_socket::async_receive_from()

send(), write(), writev()

TCP: ip::tcp::socket::write_some()ip::tcp::socket::async_write_some()ip::tcp::socket::send()ip::tcp::socket::async_send()

UDP: ip::udp::socket::send()ip::udp::socket::async_send()

basic_stream_socket::write_some(),basic_stream_socket::async_write_some(),basic_stream_socket::send(),basic_stream_socket::async_send(),basic_datagram_socket::send(),basic_datagram_socket::async_send()

sendto()

UDP: ip::udp::socket::send_to()ip::udp::socket::async_send_to()

basic_datagram_socket::send_to(),basic_datagram_socket::async_send_to()

setsockopt()

TCP: ip::tcp::acceptor::set_option()ip::tcp::socket::set_option()

UDP: ip::udp::socket::set_option()

basic_socket::set_option()

shutdown()

TCP: ip::tcp::socket::shutdown()

UDP: ip::udp::socket::shutdown()

basic_socket::shutdown()

sockatmark()

TCP: ip::tcp::socket::at_mark()

basic_socket::at_mark()

socket()

TCP: ip::tcp::acceptor::open()ip::tcp::socket::open()

UDP: ip::udp::socket::open()

basic_socket::open()

socketpair()

local::connect_pair()

注意:僅適合POSIX操做系統.

定時器

長時間運行的操做一般都會設置一個最終的完成期限.這個最終期限能夠是使用絕對時間來表示,但一般使用相對時間.

一個簡單的範例,在相對時間內執行等待異步操做:

io_service i;
...
deadline_timer t(i);
t.expires_from_now(boost::posix_time::seconds(5));
t.wait();

一般,程序會基於計時器來執行異步等待操做:

void handler(boost::system::error_code ec) { ... }
...
io_service i;
...
deadline_timer t(i);
t.expires_from_now(boost::posix_time::milliseconds(400));
t.async_wait(handler);
...
i.run();

定時器相關聯的超時期限能夠是相對時間:

boost::posix_time::time_duration time_until_expiry
  = t.expires_from_now();

或使用絕對時間:

deadline_timer t2(i);
t2.expires_at(t.expires_at() + boost::posix_time::seconds(30));

串口

Boost.Asio包含用靈活的方式建立和操做串口的類.例如,打開串口的代碼:

serial_port port(my_io_service, name);

name是如Windows中的"COM1",及POSIX平臺下的"/dev/ttyS0".

打開後,串口就能夠向流同樣使用了.既這個對象能夠用於async_read(),write(),async_write(),read_until() 或async_read_until()函數.

串口實現中還包括配置串口波特率,流控制,奇偶校驗,中止位和字符數量等可選類.

注意

串口可用於全部POSIX平臺.Windows中串口通訊須要在編譯期將IO完成端口激活.程序能夠測試BOOST_ASIO_HAS_SERIAL_PORTS宏來檢查Windows系統是否支持串口操做.

信號處理

Boost.Asio經過signal_set類實現信號處理.程序能夠向集合中加入一個或多個信號,然後執行asyn_wait()操做.當其中一個信號發生時執行特定的事件處理函數.同一個信號可註冊在多個singal_set對象中,但這些信號只能用於Boost.Asio .

void handler(
    const boost::system::error_code& error,
    int signal_number)
{
  if (!error)
  {
    // A signal occurred.
  }
}
 
...
 
// Construct a signal set registered for process termination.
boost::asio::signal_set signals(io_service, SIGINT, SIGTERM);
 
// Start an asynchronous wait for one of the signals to occur.
signals.async_wait(handler);

信號處理也可在Windows中使用,與VC++運行時庫映射到控制檯的事件如Ctrl+C等價.

POSIX特有功能

UNIX領域的Socket

Boost.Asio 提供了UNIX領域的Socket基本支持(又叫作本地Socket).最簡單的使用狀況是有一對鏈接Socket.代碼以下:

local::stream_protocol::socket socket1(my_io_service);
local::stream_protocol::socket socket2(my_io_service);
local::connect_pair(socket1, socket2);

將建立一對基於流的Socket.要實現基於數據包的Socket,使用:

local::datagram_protocol::socket socket1(my_io_service);
local::datagram_protocol::socket socket2(my_io_service);
local::connect_pair(socket1, socket2);

UNIX領域的Socket服務能夠建立在一個綁定一個端點的接收器上,TCP服務也相似:

::unlink("/tmp/foobar"); // Remove previous binding.
local::stream_protocol::endpoint ep("/tmp/foobar");
local::stream_protocol::acceptor acceptor(my_io_service, ep);
local::stream_protocol::socket socket(my_io_service);
acceptor.accept(socket);

客戶端鏈接到服務端代碼:

local::stream_protocol::endpoint ep("/tmp/foobar");
local::stream_protocol::socket socket(my_io_service);
socket.connect(ep);

Boost.Asio不支持跨UNIX領域Socket傳輸文件描述符或證書,但可使用native()函數調用Socket的底層描述符來實現.

注意

UNIX領域的Socket僅在支持的平臺上編譯的時候激活.能夠測試BOOST_ASIO_HAS_LOCAL_SOCKETS宏檢查是否支持.

基於流的文件描述符

Boost.Asio 包含在POSIX文件描述符上同步或異步讀寫的類,如管道,標準輸入輸出,和各類設備(但不是常規的文件).

例如,在標準輸入輸出上執行讀寫,建立以下對象:

posix::stream_descriptor in(my_io_service, ::dup(STDIN_FILENO));
posix::stream_descriptor out(my_io_service, ::dup(STDOUT_FILENO));

然後進行同步或異步讀寫流.即對象可用於read(),async_read(),write(),async_write(),read_until() 或async_read_until()等函數.

注意

POSIX流描述符僅在支持平臺上編譯時激活.程序能夠檢查BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR宏查詢是否支持.

Fork

Boost.Asio 支持程序使用fork()系統調用.程序能夠在適當時間調用io_service.notify_fork(),Boost.Asio可從新建立一個內部的文件描述符(如使用"self-pipe trick"描述符技巧激活反應器(reactor)).通知用法以下:

io_service_.notify_fork(boost::asio::io_service::fork_prepare);
if (fork() == 0)
{
  io_service_.notify_fork(boost::asio::io_service::fork_child);
  ...
}
else
{
  io_service_.notify_fork(boost::asio::io_service::fork_parent);
  ...
}

用戶定義服務能夠重寫io_service::service::fork_service()虛函數有意製造fork操做.

注意全部Boost.Asio的公共API函數可訪問的文件描述符(如basic_socket<>, posix::stream_descriptor下的描述符)在fork期間不會更改.這須要程序按須要進行管理.

Windows特有功能

面向流的句柄

Boost.Asio 包含容許在Windows句柄上執行異步讀或寫操做的類,如命名管道.

例如,在命名管道上執行異步操做,建立以下對象:

HANDLE handle = ::CreateFile(...);
windows::stream_handle pipe(my_io_service, handle);

然後使用同步或異步方式讀寫流.即對象可用於read(),async_read(),write(),async_write(),read_until() 或async_read_until()函數.

句柄相對的內核對象必須支持IO完成端口(命名管道支持,但匿名管道和控制檯流不支持).

注意

Windows句柄流只能在編譯期激活,並且必須只用IO完成端口做爲後臺處理(默認).可以使用BOOST_ASIO_HAS_WINDOWS_STREAM_HANDLE宏檢測.

隨機存取句柄

Boost.Asio提供特定的類實如今規則文件相關句柄上執行異步讀寫操做.

例如,在文件上執行異步操做代碼:

HANDLE handle = ::CreateFile(...);
windows::random_access_handle file(my_io_service, handle);

能夠經過成員函數read_some_at(), async_read_some_at(), write_some_at() 或 async_write_some_at()來讀出或寫入數據.然而,和流上的等價函數(read_some()等)同樣,這些函數只須要在單個操做中傳遞一個或多個字節.所以建立了read_at(),async_read_at(),write_at() 和 async_write_at()函數,內部重複調用相應的*_some_at()函數直到全部數據傳遞完畢爲止.

注意

Windows隨機讀取句柄只能在編譯期激活,並且後臺使用IO完成端口進行處理纔可用.可以使用BOOST_ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE宏來檢測.

對象句柄

Boost.Asio提供Windows特有的類實如今內核對象句柄上進行異步等待的操做:

  • 修改通知
  • 控制檯輸入
  • 事件
  • 內存資源通知
  • 進程
  • 信號量
  • 線程
  • 等待定時器

例如,在事件上執行異步操做,建立以下對象:

HANDLE handle = ::CreateEvent(...);
windows::object_handle file(my_io_service, handle);

wait()和async_wait()成員函數用於等待內核對象受信.

注意

Windows對象句柄須要在編譯期激活.可以使用BOOST_ASIO_HAS_WINDOWS_OBJECT_HANDLE宏檢查.

SSL

Boost.Asio 包括對SSL支持的類和類模板.這些類能夠在通訊時將已存在的流(如TCP Socket)進行加密.

在建立加密流前,應用程序必須構造SSL上下文對象.這個對象中一般設置瞭如驗證模式,證書文件等SLL選項.例如,客戶端初始化代碼以下:

ssl::context ctx(ssl::context::sslv23);
ctx.set_verify_mode(ssl::verify_peer);
ctx.load_verify_file("ca.pem");

在TCP Socket下使用SSL:

ssl::stream<ip::tcp::socket> ssl_sock(my_io_service, ctx);

執行特定的Socket操做,如創建遠程鏈接或接收鏈接,底層的Socket必須使用ssl::stream模板的lowest_layer()成員函數來獲取:

ip::tcp::socket::lowest_layer_type& sock = ssl_sock.lowest_layer();
sock.connect(my_endpoint);

有時底層的流對象的生命期要比SSL流長,這時模板參數須要引用流類型:

ip::tcp::socket sock(my_io_service);
ssl::stream<ip::tcp::socket&> ssl_sock(sock, ctx);

SSL的加密鏈接握手須要在傳輸或接收數據前進行.可經過ssl::stream模板的handshake()或async_handshake()成員函數創建鏈接.

鏈接後,SSL流對象便可向同步或異步讀寫流同樣的方式使用了.即對象可用於read(),async_read(),write(),async_write(),read_until() 或 async_read_until()函數.

證書驗證

Boost.Asio 提供各類方法來配置SSL證書驗證:

簡單狀況下證書驗證規則爲RFC2818(HTTPS下證書驗證), Boost.Asio 提供一個可重用的證書驗證回調函數對象:

下例演示用HTTPS的方式驗證遠程主機的證書:

using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
typedef ssl::stream<tcp::socket> ssl_socket;
 
// Create a context that uses the default paths for
// finding CA certificates.
ssl::context ctx(ssl::context::sslv23);
ctx.set_default_verify_paths();
 
// Open a socket and connect it to the remote host.
boost::asio::io_service io_service;
ssl_socket sock(io_service, ctx);
tcp::resolver resolver(io_service);
tcp::resolver::query query("host.name", "https");
boost::asio::connect(sock.lowest_layer(), resolver.resolve(query));
sock.lowest_layer().set_option(tcp::no_delay(true));
 
// Perform SSL handshake and verify the remote host's
// certificate.
sock.set_verify_mode(ssl::verify_peer);
sock.set_verify_callback(ssl::rfc2818_verification("host.name"));
sock.handshake(ssl_socket::client);
 
// ... read and write as normal ...

SSL和線程

SSL流對象在無鎖定的狀況下執行.所以,異步執行SSL都須要隱式或顯式的應用strand.注意這意味着在單線程程序中不需同步控制(沒必要使用鎖).

注意

OpenSSL須要Boost.Asio的SSL支持.當應用程序使用未由Boost.Asio包裝的OpenSSL功能時,底層的OpenSSL類可經過調用ssl::context::native_handle() 或ssl::stream::native_handle()得到.

C++2011支持

可移動IO對象

C++支持移動對象後(經過右值引用),Boost.Asio能夠對socket,串口,POSIX描述符和Windows句柄進行移動構造和賦值.

經過移動對象可實現以下代碼:

tcp::socket make_socket(io_service& i)
{
  tcp::socket s(i);
  ...
  std::move(s);
}

或:

class connection : public enable_shared_from_this<connection>
{
private:
  tcp::socket socket_;
  ...
public:
  connection(tcp::socket&& s) : socket_(std::move(s)) {}
  ...
};
 
...
 
class server
{
private:
  tcp::acceptor acceptor_;
  tcp::socket socket_;
  ...
  void handle_accept(error_code ec)
  {
    if (!ec)
      std::make_shared<connection>(std::move(socket_))->go();
    acceptor_.async_accept(socket_, ...);
  }
  ...
};

同時:

std::vector<tcp::socket> sockets;
sockets.push_back(tcp::socket(...));

一字真言:當異步操做掛起時是能夠繼續移動對象的,但這不是一個好主意.特別是操做由如async_read()等函數觸發時其引用了流對象.在操做期間移動對象將會致使相應操做訪問已移動的對象.

移動支持在g++4.5及之後版本加入-std=c++0x 或 -std=gnu++0x編譯選項便可自動支持.可經過BOOST_ASIO_DISABLE_MOVE定義宏禁用或BOOST_ASIO_HAS_MOVE宏激活.注意這些宏還會影響可移動句柄.

可移動句柄

優化後,用戶定義的完成句柄可支持移動構造,Boost.Asio的實現會優先於拷貝構造函數使用移動構造函數.當前環境下,Boost.Asio也可能去除全部句柄的拷貝構造函數.然而,句柄類型仍是須要拷貝構造函數的.

當激活移動支持,異步代碼爲:

template <typename Handler>
void async_XYZ(..., Handler handler);

實際上聲明爲:

template <typename Handler>
void async_XYZ(..., Handler&& handler);

句柄參數在async_XYZ函數體內部完美的傳遞,併發生了移動構造.這可確保函數的全部其餘參數都會提早評估移動.尤爲是在async_XYZ()的其餘參數爲句柄的成員時更重要.例如:

struct my_operation
{
  shared_ptr<tcp::socket> socket;
  shared_ptr<vector<char>> buffer;
  ...
  void operator(error_code ec, size_t length)
  {
    ...
    socket->async_read_some(boost::asio::buffer(*buffer), std::move(*this));
    ...
  }
};

移動支持在g++4.5及之後版本加入-std=c++0x 或 -std=gnu++0x編譯選項便可自動支持.可經過BOOST_ASIO_DISABLE_MOVE定義宏禁用或BOOST_ASIO_HAS_MOVE宏激活.注意這些宏還會影響可移動IO對象.

可變參模板

若是編譯器支持,Boost.Asio可使用可變參模板實現basic_socket_streambuf::connect() 和basic_socket_iostream::connect()函數.

可變參模板支持在g++4.3及之後版本中的編譯期添加-std=c++0x 或 -std=gnu++0x編譯選項會自動激活.可以使用BOOST_ASIO_DISABLE_VARIADIC_TEMPLATES宏禁用,使用BOOST_ASIO_HAS_VARIADIC_TEMPLATES宏激活.

數組容器

因爲標準庫提供了std::array<>,Boost.Asio:

G++4.3及之後版本中加上-std=c++0x 或 -std=gnu++0x編譯選項便可自動激活std::array<>,同時VC++10也支持.使用BOOST_ASIO_DISABLE_STD_ARRAY宏禁用,或BOOST_ASIO_HAS_STD_ARRAY宏激活.

原語

Boost.Asio的實現相對於boost::detail::atomic_count優先使用std::atomic<>.

在g++4.5及之後版本中加入-std=c++0x 或 -std=gnu++0x編譯選項便可激活標準的原語整數模板.可以使用BOOST_ASIO_DISABLE_STD_ATOMIC宏禁用,或BOOST_ASIO_HAS_STD_ATOMIC宏啓用.

共享指針

Boost.Asio 優先使用std::shared_ptr<>和std::weak_ptr<>.

在g++4.3及之後版本中編譯期添加編譯選項則會自動激活智能指針,MC++10也一樣.可定義BOOST_ASIO_DISABLE_STD_SHARED_PTR宏禁用,用BOOST_ASIO_HAS_STD_SHARED_PTR宏激活.

Chrono

Boost.Asio基於std::chrono機制經過basic_waitable_timer類模板提供了基本的定時器功能.system_timer,steady_timer 和 high_resolution_timer分別使用system_clock, steady_clock 和 high_resolution_clock標準時鍾.

g++4.6級之後版本添加-std=c++0x 或 -std=gnu++0x編譯選項便可自動支持std::chrono.(注意,g++中使用monotonic_clock替代steady_clock.)使用BOOST_ASIO_DISABLE_STD_CHRONO宏禁用, BOOST_ASIO_HAS_STD_CHRONO宏激活.

若是chrono不可用,可以使用Boost.Chrono庫. basic_waitable_timer類仍可用.

使用Boost.Asio

支持平臺

在以下平臺和編譯器下測試經過:

  • Win32 and Win64 using Visual C++ 7.1 and Visual C++ 8.0.
  • Win32 using MinGW.
  • Win32 using Cygwin. (__USE_W32_SOCKETS must be defined.)
  • Linux (2.4 or 2.6 kernels) using g++ 3.3 or later.
  • Solaris using g++ 3.3 or later.
  • Mac OS X 10.4 using g++ 3.3 or later.

也可用於以下平臺:

  • AIX 5.3 using XL C/C++ v9.
  • HP-UX 11i v3 using patched aC++ A.06.14.
  • QNX Neutrino 6.3 using g++ 3.3 or later.
  • Solaris using Sun Studio 11 or later.
  • Tru64 v5.1 using Compaq C++ v7.1.
  • Win32 using Borland C++ 5.9.2

依賴

爲使程序可鏈接到Boost.Asio以下庫必須可用:

  • 提供boost::system::error_code 和 boost::system::system_error 的Boost.System庫.
  • 若是使用帶boost::regex參數重載的read_until()或async_read_until()函數,須要Boost.Regex庫.
  • 若是使用了Boost.Asio的SSL支持須要OpenSSL庫.

並且有些範例須要Boost.Thread, Boost.Date_Time 或 Boost.Serialization庫的支持.

 

 

注意Note

在MSVC或Borland C++中能夠在項目設置加入-DBOOST_DATE_TIME_NO_LIB 和 -DBOOST_REGEX_NO_LIB來禁止分別對庫Boost.Date_Time 和 Boost.Regex的自動鏈接.不然須要生成這些庫並進行鏈接.

 

生成Boost庫

能夠按須要生成Boost的子庫,下例在Boost的根目錄中運行命令行:

bjam --with-system --with-thread --with-date_time --with-regex --with-serialization stage

這裏假設已經生成了bjam.更多信息見Boost.Build文檔.

可選的獨立編譯

默認狀況下,Boost.Asio僅是一個有頭文件的庫.然而,有些開發者更願意分開編譯Boost.Asio的源碼.只須要在程序的源文件中加入#include<boost/asio/impl/src.hpp>,然後在項目/編譯器設置中加入BOOST_ASIO_SEPARATE_COMPILATION編譯選項並進行編譯.或者使用BOOST_ASIO_DYN_LINK選項將Boost.Asio獨立編譯爲靜態庫.

若是使用Boost.Asio的SSL支持,須要加入#include <boost/asio/ssl/impl/src.hpp>.

下表中的宏可用來控制Boost.Asio的行爲.

 

宏Macro

描述Description

BOOST_ASIO_ENABLE_BUFFER_DEBUGGING

激活Boost.Asio的緩衝區調試支持,可幫助識別出在讀寫中引用的無效緩衝區(例如,向std::string寫數據,但在操做完成前就已經被釋放了).

在Microsoft Visual C++中,若是激活編譯器的迭代調試支持這個宏會自動定義,不然須要定義BOOST_ASIO_DISABLE_BUFFER_DEBUGGING.

在G++中,若是啓動標準庫調試(定義了_GLIBCXX_DEBUG宏)這個宏會自動定義,不然須要定義BOOST_ASIO_DISABLE_BUFFER_DEBUGGING 宏.

BOOST_ASIO_DISABLE_BUFFER_DEBUGGING

顯式的關閉Boost.Asio緩衝區調試支持.

BOOST_ASIO_DISABLE_DEV_POLL

顯式關閉Solaris的/dev/poll支持,強制使用select-based實現.

BOOST_ASIO_DISABLE_EPOLL

顯式關閉Linux的epoll支持,強制使用select-based實現.

BOOST_ASIO_DISABLE_EVENTFD

顯式關閉Linux的eventfd支持,強制使用管道進行中斷阻塞的epoll/select系統調用.

BOOST_ASIO_DISABLE_KQUEUE

Explicitly disables kqueue support on Mac OS X and BSD variants, forcing the use of a select-based implementation.

BOOST_ASIO_DISABLE_IOCP

Explicitly disables I/O completion ports support on Windows, forcing the use of a select-based implementation.

BOOST_ASIO_DISABLE_THREADS

Explicitly disables Boost.Asio's threading support, independent of whether or not Boost as a whole supports threads.

BOOST_ASIO_NO_WIN32_LEAN_AND_MEAN

By default, Boost.Asio will automatically define WIN32_LEAN_AND_MEAN when compiling for Windows, to minimise the number of Windows SDK header files and features that are included. The presence of BOOST_ASIO_NO_WIN32_LEAN_AND_MEAN prevents WIN32_LEAN_AND_MEAN from being defined.

BOOST_ASIO_NO_NOMINMAX

By default, Boost.Asio will automatically define NOMINMAX when compiling for Windows, to suppress the definition of the min() and max() macros. The presence of BOOST_ASIO_NO_NOMINMAX prevents NOMINMAX from being defined.

BOOST_ASIO_NO_DEFAULT_LINKED_LIBS

When compiling for Windows using Microsoft Visual C++ or Borland C++, Boost.Asio will automatically link in the necessary Windows SDK libraries for sockets support (i.e.ws2_32.libandmswsock.lib, orws2.lib when building for Windows CE). The BOOST_ASIO_NO_DEFAULT_LINKED_LIBS macro prevents these libraries from being linked.

BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY

Determines the maximum number of arguments that may be passed to the basic_socket_streambuf class template's connect member function. Defaults to 5.

BOOST_ASIO_SOCKET_IOSTREAM_MAX_ARITY

Determines the maximum number of arguments that may be passed to the basic_socket_iostream class template's constructor and connect member function. Defaults to 5.

BOOST_ASIO_ENABLE_CANCELIO

Enables use of the CancelIo function on older versions of Windows. If not enabled, calls to cancel() on a socket object will always fail with asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows. When running on Windows Vista, Windows Server 2008, and later, the CancelIoEx function is always used.

The CancelIo function has two issues that should be considered before enabling its use:

* It will only cancel asynchronous operations that were initiated in the current thread.

* It can appear to complete without error, but the request to cancel the unfinished operations may be silently ignored by the operating system. Whether it works or not seems to depend on the drivers that are installed.

For portable cancellation, consider using one of the following alternatives:

* Disable asio's I/O completion port backend by defining BOOST_ASIO_DISABLE_IOCP.

* Use the socket object's close() function to simultaneously cancel the outstanding operations and close the socket.

BOOST_ASIO_NO_TYPEID

Disables uses of the typeid operator in Boost.Asio. Defined automatically if BOOST_NO_TYPEID is defined.

BOOST_ASIO_HASH_MAP_BUCKETS

Determines the number of buckets in Boost.Asio's internal hash_map objects. The value should be a comma separated list of prime numbers, in ascending order. The hash_map implementation will automatically increase the number of buckets as the number of elements in the map increases.

Some examples:

* Defining BOOST_ASIO_HASH_MAP_BUCKETS to 1021 means that the hash_map objects will always contain 1021 buckets, irrespective of the number of elements in the map.

* Defining BOOST_ASIO_HASH_MAP_BUCKETS to 53,389,1543 means that the hash_map objects will initially contain 53 buckets. The number of buckets will be increased to 389 and then 1543 as elements are added to the map.

 

郵件列表

Boost.Asio 的郵件列表可從SourceForge.net查找.新聞組見Gmane.

Wiki

用戶一般受益於Boost.Asio WIKI上的共享範例,提示和FAQ.,請見http://think-async.com/Asio/.

入門教程

基本技能

入門教程的第一小節介紹使用asio工具的基本概念.在開發複雜的網絡程序前,範例程序闡述使用異步定時器的基本技巧.

Sockets簡介

本節教程展現如何使用asio開發一個簡單的客戶端和服務端程序.程序使用TCP和UDP實現了基本的daytime協議.

前面三個程序使用TCP實現daytime協議.

後面三個程序使用UDP實現daytime協議.

本節最後的程序展現如何在一個程序中使用asio來組合TCP和UDP服務.

定時器1-使用同步定時器

本教程演示如何使用asio的定時器執行阻塞等待.

須要包含必要的頭文件:

簡單的包含"asio.hpp"頭文件便可使用全部的asio類.

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

因爲本例使用了定時器,須要包含相應的Boost.Date_Time頭文件以便於操做定時器.

#include <boost/date_time/posix_time/posix_time.hpp>

全部使用asio的程序都要建立一個io_service對象.這個類提供IO功能支持.在main函數中首先聲明這個類的對象.

int main()
{
  boost::asio::io_service io;

接着聲明一個boost::asio::deadline_timer類型的對象.這些提供IO功能的核心asio類 (或是定時器功能)構造函數的第一個參數老是io_service類的實例.第二個參數設置過時時間爲從如今起5秒鐘之後.

  boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

這是在定時器上阻塞等待的簡單範例.調用deadline_timer::wait()會在定時器到期(5秒鐘)後才返回.

定時器老是有兩個狀態: "到期" 或 "未到期".若是deadline_timer::wait()函數在一個已過時的時間上執行,會當即返回.

  t.wait();

最後在時間到期後輸出著名的"Hello, world!"消息.

  std::cout << "Hello, world!\n";
  return 0;
}

定時器2--異步使用定時器

本範例修改了time1範例,經過在定時器上異步等待演示瞭如何使用asio異步回調功能.

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

使用asio的異步功能意味着須要定義一個回調函數,在異步操做完成後執行.本程序中定義了一個叫作print的函數,異步等待完成後調用.

void print(const boost::system::error_code& /*e*/)
{
  std::cout << "Hello, world!\n";
}
 
int main()
{
  boost::asio::io_service io;
 
  boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

接下來,替換範例1中的阻塞等待.調用deadline_timer::async_wait()函數執行異步等待.在這個函數中傳遞了上面定義的print回調函數.

  t.async_wait(&print);

最後,必須在io_service對象上調用io_service::run()成員函數.

Asio庫保證回調函數在調用了io_service::run()的線程上執行.所以只有調用了io_service::run()函數,異步等待完成的回調函數才能被調用.

Io_service::run()將會一直在運行,由於其還有工做要作.本例中,這個工做是異步等待定時器,所以直到定時器過時並且回調函數執行完畢,纔會退出.

在調用io_service::run()前爲其指定工做是很重要的.例如,若是上例中忘記調用deadline_timer::async_wait(),io_service無事可作,則io_service::run()調用會當即返回.

  io.run();
  return 0;
}

定時器3-向處理器傳遞參數

本程序從Timer2程序修改而來,將timer改成每秒觸發一次.用來闡述如何向處理函數傳遞其餘參數.

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

爲了讓timer循環的過時,須要在回調函數中修改timer的過時時間,然後開始一個新的異步等待.固然必須讓回調函數能夠訪問到timer對象.要實現這個效果須要向print函數傳遞兩個參數:

  • 指向timer對象的指針.
  • Timer觸發計數器,以便於實現觸發6次後結束程序運行.
void print(const boost::system::error_code& /*e*/,
    boost::asio::deadline_timer* t, int* count)
{

如上所述,程序使用計時器來中止運行,若是定時器觸發測試超過6次則退出.然而這裏並無顯式的讓io_service中止運行.在定時器2範例中咱們知道,只有無事可作時io_service::run()纔會退出.當計數器等於5時就不在啓動新的異步等待,則io_service完成了工做並中止運行.

  if (*count < 5)
  {
    std::cout << *count << "\n";
    ++(*count);

另外將過時時間從前一個過時時間向後延遲一秒.在原來基礎上定義新的過時時間,能夠確保定時器標記的秒數不會因爲處理函數執行而延時.

    t->expires_at(t->expires_at() + boost::posix_time::seconds(1));

然後在timer上啓動一個新的異步等待.boost::bind()函數用來將回調函數與參數相關聯.deadline_timer::async_wait()函數但願的回調函數形式爲void(const boost::system::error_code&).bind函數將回調函數print連同附加的參數轉換爲這種正確的函數簽名.

更多Boost.Bind信息見Boost.Bind documentation.

本例中,boost::bind()的boost::asio::placeholders::error參數是傳遞給成了函數的錯誤對象名稱佔位符.若是使用boost::bind()初始化異步操做,必須指定與句柄參數列表相一致的參數.在範例4中回調函數不須要這個參數會省略掉.

    t->async_wait(boost::bind(print,
          boost::asio::placeholders::error, t, count));
  }
}
 
int main()
{
  boost::asio::io_service io;

添加一個新的count變量,以便於在timer觸發6次後結束程序運行.

  int count = 0;
  boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));

如步驟4,在main函數中調用了deadline_timer::async_wait()時會向print函數傳遞附加參數.

  t.async_wait(boost::bind(print,
        boost::asio::placeholders::error, &t, &count));
 
  io.run();

最後,驗證print句柄函數中的count變量,輸出其值.

  std::cout << "Final count is " << count << "\n";
 
  return 0;
}

定時器4 -使用成員函數做爲處理器

本例演示如何將類的成員函數做爲回調處理函數.程序功能與Timer3徹底同樣.

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

上一個範例中使用print函數做爲回調處理函數,本例中首先定義一個printer類.

class printer
{
public:

類的構造函數中有個io_service的引用,用來初始化timer_成員.關閉程序的計時器也聲明爲類的成員.

  printer(boost::asio::io_service& io)
    : timer_(io, boost::posix_time::seconds(1)),
      count_(0)
  {

Boost::bind()函數也可用於處理類的成員函數.因爲全部的非靜態類成員函數都有一個隱式的this參數,咱們須要將this綁定到函數上.與Timer3範例同樣,使用boost::bind()轉換回調處理函數,將其處理爲void(constboost::system::error_code&)形式的簽名.

注意這裏boost::asio::placeholders::error佔位符被忽略,成員函數print不在接收錯誤對象參數.

    timer_.async_wait(boost::bind(&printer::print, this));
  }

在類的析構函數輸出最終的計數器值.

  ~printer()
  {
    std::cout << "Final count is " << count_ << "\n";
  }

成員函數print與上例的print函數很是相似.因爲計數器和timer都作爲了類的成員,所以沒必要在進行參數傳遞了.

  void print()
  {
    if (count_ < 5)
    {
      std::cout << count_ << "\n";
      ++count_;
 
      timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(1));
      timer_.async_wait(boost::bind(&printer::print, this));
    }
  }
 
private:
  boost::asio::deadline_timer timer_;
  int count_;
};

Main函數與上例也很類似,如今聲明一個printer對象並運行io_service.

int main()
{
  boost::asio::io_service io;
  printer p(io);
  io.run();
 
  return 0;
}

同步TCP daytime客戶端

本例展現如何使用asio實現一個TCP客戶端.

須要包含以下頭文件.

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

程序的目的是要訪問daytime服務器,所以須要用戶指定一個服務器.

using boost::asio::ip::tcp;
 
int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: client <host>" << std::endl;
      return 1;
    }

全部使用asio的程序都須要一個io_service對象.

    boost::asio::io_service io_service;

咱們須要將程序參數中指定的服務器名稱轉換爲TCP端點,所以須要定義一個ip::tcp::resolver對象.

    tcp::resolver resolver(io_service);

分析器使用一個查詢對象並獲取一個端點列表.使用服務器的名稱(參數argv[1])以及服務名稱(本例中是daytime)做爲參數來構造查詢對象.

    tcp::resolver::query query(argv[1], "daytime");

返回的端點列表爲ip::tcp::resolver::iterator類型的迭代器.

    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

如今建立並鏈接Socket.上述的端點列表可能包括IPv4或IPv6類型的端點,須要嘗試每種可能獲取可用的端點.這樣客戶端就獨立於IP版本號了. boost::asio::connect()自動完成這些功能.

    tcp::socket socket(io_service);
    boost::asio::connect(socket, endpoint_iterator);

鏈接已經打開.如今就能夠從服務端讀取數據了.

使用boost::array 來接收數據. boost::asio::buffer()函數自動管理數組大小以防止緩衝區溢出.除了boost::array ,還可使用char[]或std::vector.

    for (;;)
    {
      boost::array<char, 128> buf;
      boost::system::error_code error;
 
      size_t len = socket.read_some(boost::asio::buffer(buf), error);

當服務端關閉了鏈接,ip::tcp::socket::read_some() 函數接收到boost::asio::error::eof 錯誤信息,退出客戶端循環.

      if (error == boost::asio::error::eof)
        break; // Connection closed cleanly by peer.
      else if (error)
        throw boost::system::system_error(error); // Some other error.
 
      std::cout.write(buf.data(), len);
    }

最後處理可能拋出的異常.

  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

TCP daytime同步服務器

本例闡述如何使用asio實現TCP服務端應用程序.

#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
 
using boost::asio::ip::tcp;

定義一個make_daytime_string()函數生成發送給客戶端的字符串.這個函數在全部daytime服務應用程序中重用.

std::string make_daytime_string()
{
  using namespace std; // For time_t, time and ctime;
  time_t now = time(0);
  return ctime(&now);
}
 
int main()
{
  try
  {
    boost::asio::io_service io_service;

ip::tcp::acceptor對象用來偵聽新到的鏈接.這裏初始化爲使用IPv4協議來偵聽13端口.

    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));

這是個可迭代的服務端,即每次只能處理一個鏈接.對每一個客戶端連接建立一個Socket,然後等待新到來的鏈接.

    for (;;)
    {
      tcp::socket socket(io_service);
      acceptor.accept(socket);

客戶端訪問咱們服務端.當前的時間將傳遞給客戶端.

      std::string message = make_daytime_string();
 
      boost::system::error_code ignored_error;
      boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
    }
  }

最後處理異常.

  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }
 
  return 0;
}

異步TCP daytime服務端

main函數

int main()
{
  try
  {

首先建立一個服務對象接收客戶端的鏈接. io_service對象提供IO服務,如socket就使用這個IO服務對象.

    boost::asio::io_service io_service;
    tcp_server server(io_service);

運行io_service對象使其執行異步操做.

    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }
 
  return 0;
}

tcp_server 類

class tcp_server
{
public:

構造函數初始化接收器監聽TCP的13端口.

  tcp_server(boost::asio::io_service& io_service)
    : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13))
  {
    start_accept();
  }
 
private:

函數start_accept()建立一個Socket並初始化爲異步接收操做,等待新的鏈接.

  void start_accept()
  {
    tcp_connection::pointer new_connection =
      tcp_connection::create(acceptor_.get_io_service());
 
    acceptor_.async_accept(new_connection->socket(),
        boost::bind(&tcp_server::handle_accept, this, new_connection,
          boost::asio::placeholders::error));
  }

由start_accept()函數初始化的異步接收操做完成後會調用handle_accept()函數.其響應客戶端的請求並再次調用start_accept()函數初始化下一次接收操做.

  void handle_accept(tcp_connection::pointer new_connection,
      const boost::system::error_code& error)
  {
    if (!error)
    {
      new_connection->start();
    }
 
    start_accept();
  }

Tcp鏈接類

使用shared_ptr和enable_shared_from_this保證tcp_connetion在有相應操做期間保持激活狀態.

class tcp_connection
  : public boost::enable_shared_from_this<tcp_connection>
{
public:
  typedef boost::shared_ptr<tcp_connection> pointer;
 
  static pointer create(boost::asio::io_service& io_service)
  {
    return pointer(new tcp_connection(io_service));
  }
 
  tcp::socket& socket()
  {
    return socket_;
  }

在函數start()中調用boost::asio::async_write()向客戶端寫數據.注意使用的是boost::asio::async_write()而不是ip::tcp::socket::async_write_some(),這樣能夠確保發送所有的數據.

  void start()
  {

發送的數據存儲在類成員message_中,在異步操做完成前必須保證數據的有效性.

    message_ = make_daytime_string();

初始化異步操做後,使用boost::bind()傳遞的參數必須與事件處理函數參數列表相匹配.本例中, boost::asio::placeholders::error 和 boost::asio::placeholders::bytes_transferred參數佔位符都被去掉了,由於他們沒有在handle_write()中使用.

    boost::asio::async_write(socket_, boost::asio::buffer(message_),
        boost::bind(&tcp_connection::handle_write, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));

再次處理客戶端鏈接請求須要由handle_write()負責.

  }
 
private:
  tcp_connection(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }
 
  void handle_write(const boost::system::error_code& /*error*/,
      size_t /*bytes_transferred*/)
  {
  }
 
  tcp::socket socket_;
  std::string message_;
};

移除無用的事件處理函數的參數

你可能發現error和bytes_transferred參數在函數handle_write()中是無用的.若是參數不須要可將其移除:

  void handle_write()
  {
  }

boost::asio::async_write()的調用調整爲以下形式:

  boost::asio::async_write(socket_, boost::asio::buffer(message_),
      boost::bind(&tcp_connection::handle_write, shared_from_this()));

DDaytime4-同步UDP daytime客戶端

本範例程序展現如何使用asio來實現UPD客戶端程序.

#include/span> <iostream>

#include <boost/array.hpp>

#include <boost/asio.hpp>

 

using boost::asio::ip::udp;;

啓動程序的方式和TCP daytime客戶端相同.

int/span> main(int argc, char* argv[])

{

  try

  {

    if (argc != 2)

    {

      std::cerr << "Usage: client <host>" << std::endl;

      return 1;

    }

 

    boost::asio::io_service io_service;;

ip::udp::resolver傳遞主機和服務名稱獲取正確的遠程端點.查詢經過ip::udp::v4()參數限制使其返回一個IPv4協議的端點.

    udp::resolver resolver(io_service);

    udp::resolver::query query(udp::v4(), argv[1], "daytime"));

ip::udp::resolver::resolve()函數保證若是查詢成功的話會返回至少有一個端點的列表.所以能夠安全的直接對返回值進行解引用.

   udp::endpoint receiver_endpoint = *resolver.resolve(query));

因爲UDP是基於數據報的,沒有使用基於流的Socket.建立一個ip::udp::socket並使用遠程端點進行初始鏈接.

   udp::socket socket(io_service);

   socket.open(udp::v4());

   boost::array<char, 1> send_buf  = {{ 0 }};

   socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint));

如今須要準備好接收服務端發送過來的數據.客戶端接收服務端響應的端點要經過ip::udp::socket::receive_from()指定.

   boost::array<char, 128> recv_buf;

   udp::endpoint sender_endpoint;

   size_t len = socket.receive_from(

       boost::asio::buffer(recv_buf), sender_endpoint);

   std::cout.write(recv_buf.data(), len);

 }}

 

 catch (std/span>::exception& e)

 {

   std::cerr << e.what() << std::endl;

 }

 return 0;

}

Daytime5- daytime同步UDP服務端

本範例程序展現如何使用asio實現同步UDP服務程序.

int main()
{
  try
  {
    boost::asio::io_service io_service;

建立ip::udp::socket對象接收UDP 13端口上的請求.

    udp::socket socket(io_service, udp::endpoint(udp::v4(), 13));

等待客戶端請求鏈接.remote_endpoint對象將傳入ip::udp::socket::receive_from()函數.

    for (;;)
    {
      boost::array<char, 1> recv_buf;
      udp::endpoint remote_endpoint;
      boost::system::error_code error;
      socket.receive_from(boost::asio::buffer(recv_buf),
          remote_endpoint, 0, error);
 
      if (error && error != boost::asio::error::message_size)
        throw boost::system::system_error(error);

接着實現向客戶端發送數據.

      std::string message = make_daytime_string();

向remote_endpoint發送響應.

      boost::system::error_code ignored_error;
      socket.send_to(boost::asio::buffer(message),
          remote_endpoint, 0, ignored_error);
    }
  }

最後處理異常.

  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }
 
  return 0;
}

Daytime6- daytime異步UDP服務端

main()函數

int main()
{
  try
  {

建立服務對象接收客戶端請求,並運行io_service對象.

    boost::asio::io_service io_service;
    udp_server server(io_service);
    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }
 
  return 0;
}

udp_server類

class udp_server
{
public:

構造函數初始化socket並偵聽UDP的13端口.

  udp_server(boost::asio::io_service& io_service)
    : socket_(io_service, udp::endpoint(udp::v4(), 13))
  {
    start_receive();
  }
 
private:
  void start_receive()
  {

ip::udp::socket::async_receive_from()函數使應用程序在後臺偵聽新的請求.當接收到請求,io_service對象使用兩個參數來調用handle_receive函數:一個描述操做成功失敗的boost::system::error_code類型參數,和一個size_t類型的bytes_transferred 指定接收到的字節數.

    socket_.async_receive_from(
        boost::asio::buffer(recv_buffer_), remote_endpoint_,
        boost::bind(&udp_server::handle_receive, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

handle_receive()函數處理客戶端請求.

  void handle_receive(const boost::system::error_code& error,
      std::size_t /*bytes_transferred*/)
  {

error參數包含異步操做的結果.因爲這裏只提供了一個字節的recv_buffer_來存放客戶端請求,若是客戶端發送的數據過長則io_service會返回錯誤信息.若是發生錯誤則忽略.

    if (!error || error == boost::asio::error::message_size)
    {

生成發送給客戶端的數據.

      boost::shared_ptr<std::string> message(
          new std::string(make_daytime_string()));

調用ip::udp::socket::async_send_to()函數將數據發送到客戶端.

      socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
          boost::bind(&udp_server::handle_send, this, message,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));

當初始化異步操做時,若是使用boost::bind(),必須指定與回調函數相匹配的參數列表.本程序中,兩個參數佔位符(boost::asio::placeholders::error 和 boost::asio::placeholders::bytes_transferred)應該被移除.

開始偵聽下一個客戶端請求.

      start_receive();

對客戶請求的更多處理由handle_send()負責.

    }
  }

handle_send()函數在服務請求結束後調用.

  void handle_send(boost::shared_ptr<std::string> /*message*/,
      const boost::system::error_code& /*error*/,
      std::size_t /*bytes_transferred*/)
  {
  }
 
  udp::socket socket_;
  udp::endpoint remote_endpoint_;
  boost::array<char, 1> recv_buffer_;
};

Daytime7-TCP/UDP異步服務

本範例程序展現如何將已經寫過的兩個異步服務整合到一個服務程序中.

main()函數

int main()
{
  try
  {
    boost::asio::io_service io_service;

建立一個接收TCP客戶端鏈接的服務對象.

    tcp_server server1(io_service);

建立一個接收UDP客戶端請求的服務對象.

    udp_server server2(io_service);

已經建立了兩個基於io_service對象上的工做對象.

    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }
 
  return 0;
}

tcp_connection 和 tcp_server 類

以下兩個類取自Daytime3範例.

class tcp_connection
  : public boost::enable_shared_from_this<tcp_connection>
{
public:
  typedef boost::shared_ptr<tcp_connection> pointer;
 
  static pointer create(boost::asio::io_service& io_service)
  {
    return pointer(new tcp_connection(io_service));
  }
 
  tcp::socket& socket()
  {
    return socket_;
  }
 
  void start()
  {
    message_ = make_daytime_string();
 
    boost::asio::async_write(socket_, boost::asio::buffer(message_),
        boost::bind(&tcp_connection::handle_write, shared_from_this()));
  }
 
private:
  tcp_connection(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }
 
  void handle_write()
  {
  }
 
  tcp::socket socket_;
  std::string message_;
};
 
class tcp_server
{
public:
  tcp_server(boost::asio::io_service& io_service)
    : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13))
  {
    start_accept();
  }
 
private:
  void start_accept()
  {
    tcp_connection::pointer new_connection =
      tcp_connection::create(acceptor_.get_io_service());
 
    acceptor_.async_accept(new_connection->socket(),
        boost::bind(&tcp_server::handle_accept, this, new_connection,
          boost::asio::placeholders::error));
  }
 
  void handle_accept(tcp_connection::pointer new_connection,
      const boost::system::error_code& error)
  {
    if (!error)
    {
      new_connection->start();
    }
 
    start_accept();
  }
 
  tcp::acceptor acceptor_;
};

udp_server 類

一樣,下面的類取自上一個範例.

class udp_server
{
public:
  udp_server(boost::asio::io_service& io_service)
    : socket_(io_service, udp::endpoint(udp::v4(), 13))
  {
    start_receive();
  }
 
private:
  void start_receive()
  {
    socket_.async_receive_from(
        boost::asio::buffer(recv_buffer_), remote_endpoint_,
        boost::bind(&udp_server::handle_receive, this,
          boost::asio::placeholders::error));
  }
 
  void handle_receive(const boost::system::error_code& error)
  {
    if (!error || error == boost::asio::error::message_size)
    {
      boost::shared_ptr<std::string> message(
          new std::string(make_daytime_string()));
 
      socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
          boost::bind(&udp_server::handle_send, this, message));
 
      start_receive();
    }
  }
 
  void handle_send(boost::shared_ptr<std::string> /*message*/)
  {
  }
 
  udp::socket socket_;
  udp::endpoint remote_endpoint_;
  boost::array<char, 1> recv_buffer_;
};

範例

分配內存

本範例展現如何爲異步操做分配必需的內存.

緩衝區

本範例展現如何爲Socket讀寫操做建立有引用計數的緩衝區.

聊天程序

本範例實現了聊天程序的服務端和客戶端.這個程序使用具備6字節的消息頭和可變長消息體的自定義協議.

下面針對POSIX的聊天客戶端演示如何使用posix::stream_descriptor類執行控制檯輸入輸出.

Echo

展現如何使用同步和異步操做實現一系列簡單的客戶端和服務端.

fock

針對POSIX 的範例程序演示瞭如何使用Boost.Asio結合fork()系統調用.第一個範例闡述瞭如何啓動守護進程:

第二個範例闡述瞭如何在完成句柄中啓動進程.

HTTP客戶端

範例程序簡單的實現了HTTP1.0客戶端.程序演示瞭如何使用read_until和async_read_until函數.

HTTP服務

範例闡述了在單線程中使用asio實現HTTP1.0服務.演示瞭如何執行清除命令結束未完成的異步操做.

HTTP服務2

使用io_service-per-CPU 設計的HTTP服務.

HTTP服務3

使用單個io_service並在線程池上調用io_service::run()的HTTP服務.

HTTP服務4

使用無棧協同技術實現單線程HTTP服務.

ICMP

展現如何在ICMP上使用原生的Socket ping遠程主機.

調用

展現如何自定義處理函數調用.將完成句柄加入到一個優先級隊列而不是直接調用.

iostream

展現如何使用ip::tcp::iostream.

廣播

展現使用廣播向訂閱組傳輸包.

序列化

展現如何在asio中使用Boost.Serialization序列號和反序列化結構體,從而實如今Socket上傳輸數據對象.

服務

展現如何向asio的io_service整合自定義功能(本例中是添加了日誌功能),以及在basic_stream_socket<>上使用自定義服務.

SOCKS4協議

範例客戶端程序實現了經過代理通訊的SOCKS4協議.

SSL

客戶端和服務端範例程序演示了在異步操做中使用ssl::stream<>模板.

超時設定

下面的一系列範例展現瞭如何在指定時間以後取消長時間運行的異步操做.

定時器

範例演示瞭如何定製deadline_time實現不一樣類型的計時器.

Porthopper

本例演示了混合同步和異步操做,及如何在Boost.Asio中使用Boost.Lambda.

無阻塞

展現如何整合實現IO操做的第三方庫來實現Reactor風格的操做.

UNIX領域的Socket

展現如何使用UNIX領域的Socket.

Windows

展現如何在Boost.Asio中使用Windows特有的TransmitFile函數.

相關文章
相關標籤/搜索