天天看點

boost::asio中文文檔

Christopher Kohlhoff

Copyright © 2003-2012 Christopher M. Kohlhoff

以Boost1.0的軟體授權進行釋出(見附帶的LICENSE_1_0.txt檔案或從http://www.boost.org/LICENSE_1_0.txt)

Boost.Asio是用于網絡和低層IO程式設計的跨平台C++庫,為開發者提供了C++環境下穩定的異步模型.

綜述

基本原理

應用程式與外界互動的方式有很多,可通過檔案,網絡,序列槽或控制台.例如在網絡通信中,完成獨立的IO操作需要很長時間.對應用程式開發者提出了一個挑戰.

Boost.Asio 提供了管理需長時間運作操作的工具,但不必涉及到線程的并發模式和顯示鎖定.

Boost.Asio 庫使用C++來實作,提供如網絡程式設計等常用的作業系統接口. Boost.Asio實作了如下目标:

· 可移植性Portability.庫支援一系列的常用系統操作,具有穩定的跨平台特性.

· 可擴充性Scalability.庫可以幫助開發者建構數千并發連接配接的網絡應用程式.庫中實作的每個系統操作都使用了最具擴充性的機制.

· 效率Efficiency.庫支援發散聚合IO(scatter-gather I/O)等技術,使應用程式盡量少的拷貝資料.

· 可以像BSD Sockets一樣通過API函數建立模型概念. BSD Socket API應用廣為人知,有很多相關的程式庫.其他程式設計語言通常使用其簡單的網絡API接口.出于這些原因, Boost.Asio同樣使用已存在的技術建構.

· 易于使用Ease of use.庫提供了工具箱而不是架構來降低新手的入門門檻.可使用最少的時間投入來學習基本的規則和方針.而後,庫的使用者隻需要了解用到的特定函數即可.

· 基于更多的抽象.Basis for further abstraction.庫提供了高層次的抽象允許其他庫的開發者進行擴充.例如,實作常用的HTTP協定.

雖然Boost.Asio一開始定位于網絡通信,但其異步IO的概念已經擴充到了如序列槽通信,檔案描述符等其他作業系統的IO操作方面.

核心概念和功能

解析Boost.Asio

Boost.Asio 可用于如socket等IO對象的同步或異步操作.在使用Boost.Asio前了解Boost.Asio概念圖, 及與應用程式的互相內建是很有幫助的.

第一個範例,看看處理socket連接配接的情況.首先從同步操作開始.

boost::asio中文文檔

應用程式必須有一個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被賦予操作的結果值,但不會抛出異常.

對于異步操作,事件順序不同.

boost::asio中文文檔

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 通知作業系統其需要開始一個異步連接配接.

時序過程.(在同步情況下等待包括連接配接操作時間.)

boost::asio中文文檔

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不依賴平台細節的特性.

boost::asio中文文檔
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|[email protected]_wait      
@asio|1298160085.070888|0*2|[email protected]_accept      
@asio|1298160085.070913|0|[email protected]      
@asio|1298160118.075438|>2|ec=asio.system:0      
@asio|1298160118.075472|2*3|[email protected]_receive      
@asio|1298160118.075507|2*4|[email protected]_accept      
@asio|1298160118.075527|<2|      
@asio|1298160118.075540|>3|ec=asio.system:0,bytes_transferred=122      
@asio|1298160118.075731|3*5|[email protected]_send      
@asio|1298160118.075778|<3|      
@asio|1298160118.075793|>5|ec=asio.system:0,bytes_transferred=156      
@asio|1298160118.075831|5|[email protected]      
@asio|1298160118.075855|<5|      
@asio|1298160122.827317|>1|ec=asio.system:0,signal_number=2      
@asio|1298160122.827333|1|[email protected]      
@asio|1298160122.827359|<1|      
@asio|1298160122.827370|>4|ec=asio.system:125      
@asio|1298160122.827378|<4|      
@asio|1298160122.827394|0|[email protected]      

每行的格式為:

<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::socket, ip::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證書驗證:

  • ssl::context::set_default_verify_paths()
  • ssl::context::set_verify_mode()
  • ssl::context::set_verify_callback()
  • ssl::context::load_verify_file()
  • ssl::stream::set_verify_mode()
  • ssl::stream::set_verify_callback()

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

  • ssl::rfc2818_verification

下例示範用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:

  • 為buffer()函數提供了重載.
  • 相對于ip::address_v4::bytes_type和ip::address_v6::bytes_type優先使用boost::array<>.
  • 固定大小的數組優先使用boost::array<>.

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.lib

 and 

mswsock.lib

, or 

ws2.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工具的基本概念.在開發複雜的網絡程式前,範例程式闡述使用異步定時器的基本技巧.

  • Timer.1 - Using a timer synchronously 同步定時程式
  • Timer.2 - Using a timer asynchronously 異步定時程式
  • Timer.3 - Binding arguments to a handler向處理函數綁定參數
  • Timer.4 - Using a member function as a handler 使用成員函數作為處理函數
  • Timer.5 - Synchronising handlers in multithreaded programs 多線程程式中的處理函數同步

Sockets簡介

本節教程展示如何使用asio開發一個簡單的用戶端和服務端程式.程式使用TCP和UDP實作了基本的daytime協定.

前面三個程式使用TCP實作daytime協定.

  • Daytime.1 - A synchronous TCP daytime client
  • Daytime.2 - A synchronous TCP daytime server
  • Daytime.3 - An asynchronous TCP daytime server

後面三個程式使用UDP實作daytime協定.

  • Daytime.4 - A synchronous UDP daytime client
  • Daytime.5 - A synchronous UDP daytime server
  • Daytime.6 - An asynchronous UDP daytime server

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

  • Daytime.7 - A combined TCP/UDP asynchronous server

定時器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(const

boost::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_;      
};      

範例

配置設定記憶體

本範例展示如何為異步操作配置設定必需的記憶體.

  • boost_asio/example/allocation/server.cpp

緩沖區

本範例展示如何為Socket讀寫操作建立有引用計數的緩沖區.

  • boost_asio/example/buffers/reference_counted.cpp

聊天程式

本範例實作了聊天程式的服務端和用戶端.這個程式使用具有6位元組的消息頭和可變長消息體的自定義協定.

  • boost_asio/example/chat/chat_message.hpp
  • boost_asio/example/chat/chat_client.cpp
  • boost_asio/example/chat/chat_server.cpp

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

  • boost_asio/example/chat/posix_chat_client.cpp

Echo

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

  • boost_asio/example/echo/async_tcp_echo_server.cpp
  • boost_asio/example/echo/async_udp_echo_server.cpp
  • boost_asio/example/echo/blocking_tcp_echo_client.cpp
  • boost_asio/example/echo/blocking_tcp_echo_server.cpp
  • boost_asio/example/echo/blocking_udp_echo_client.cpp
  • boost_asio/example/echo/blocking_udp_echo_server.cpp

fock

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

  • boost_asio/example/fork/daemon.cpp

第二個範例闡述了如何在完成句柄中啟動程序.

  • boost_asio/example/fork/process_per_connection.cpp

HTTP用戶端

範例程式簡單的實作了HTTP1.0用戶端.程式示範了如何使用read_until和async_read_until函數.

  • boost_asio/example/http/client/sync_client.cpp
  • boost_asio/example/http/client/async_client.cpp

HTTP服務

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

  • boost_asio/example/http/server/connection.cpp
  • boost_asio/example/http/server/connection.hpp
  • boost_asio/example/http/server/connection_manager.cpp
  • boost_asio/example/http/server/connection_manager.hpp
  • boost_asio/example/http/server/header.hpp
  • boost_asio/example/http/server/main.cpp
  • boost_asio/example/http/server/mime_types.cpp
  • boost_asio/example/http/server/mime_types.hpp
  • boost_asio/example/http/server/reply.cpp
  • boost_asio/example/http/server/reply.hpp
  • boost_asio/example/http/server/request.hpp
  • boost_asio/example/http/server/request_handler.cpp
  • boost_asio/example/http/server/request_handler.hpp
  • boost_asio/example/http/server/request_parser.cpp
  • boost_asio/example/http/server/request_parser.hpp
  • boost_asio/example/http/server/server.cpp
  • boost_asio/example/http/server/server.hpp

HTTP服務2

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

  • boost_asio/example/http/server2/connection.cpp
  • boost_asio/example/http/server2/connection.hpp
  • boost_asio/example/http/server2/header.hpp
  • boost_asio/example/http/server2/io_service_pool.cpp
  • boost_asio/example/http/server2/io_service_pool.hpp
  • boost_asio/example/http/server2/main.cpp
  • boost_asio/example/http/server2/mime_types.cpp
  • boost_asio/example/http/server2/mime_types.hpp
  • boost_asio/example/http/server2/reply.cpp
  • boost_asio/example/http/server2/reply.hpp
  • boost_asio/example/http/server2/request.hpp
  • boost_asio/example/http/server2/request_handler.cpp
  • boost_asio/example/http/server2/request_handler.hpp
  • boost_asio/example/http/server2/request_parser.cpp
  • boost_asio/example/http/server2/request_parser.hpp
  • boost_asio/example/http/server2/server.cpp
  • boost_asio/example/http/server2/server.hpp

HTTP服務3

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

  • boost_asio/example/http/server3/connection.cpp
  • boost_asio/example/http/server3/connection.hpp
  • boost_asio/example/http/server3/header.hpp
  • boost_asio/example/http/server3/main.cpp
  • boost_asio/example/http/server3/mime_types.cpp
  • boost_asio/example/http/server3/mime_types.hpp
  • boost_asio/example/http/server3/reply.cpp
  • boost_asio/example/http/server3/reply.hpp
  • boost_asio/example/http/server3/request.hpp
  • boost_asio/example/http/server3/request_handler.cpp
  • boost_asio/example/http/server3/request_handler.hpp
  • boost_asio/example/http/server3/request_parser.cpp
  • boost_asio/example/http/server3/request_parser.hpp
  • boost_asio/example/http/server3/server.cpp
  • boost_asio/example/http/server3/server.hpp

HTTP服務4

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

  • boost_asio/example/http/server4/coroutine.hpp
  • boost_asio/example/http/server4/file_handler.cpp
  • boost_asio/example/http/server4/file_handler.hpp
  • boost_asio/example/http/server4/header.hpp
  • boost_asio/example/http/server4/main.cpp
  • boost_asio/example/http/server4/mime_types.cpp
  • boost_asio/example/http/server4/mime_types.hpp
  • boost_asio/example/http/server4/reply.cpp
  • boost_asio/example/http/server4/reply.hpp
  • boost_asio/example/http/server4/request.hpp
  • boost_asio/example/http/server4/request_parser.cpp
  • boost_asio/example/http/server4/request_parser.hpp
  • boost_asio/example/http/server4/server.cpp
  • boost_asio/example/http/server4/server.hpp
  • boost_asio/example/http/server4/unyield.hpp
  • boost_asio/example/http/server4/yield.hpp

ICMP

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

  • boost_asio/example/icmp/ping.cpp
  • boost_asio/example/icmp/ipv4_header.hpp
  • boost_asio/example/icmp/icmp_header.hpp

調用

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

  • boost_asio/example/invocation/prioritised_handlers.cpp

iostream

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

  • boost_asio/example/iostreams/daytime_client.cpp
  • boost_asio/example/iostreams/daytime_server.cpp
  • boost_asio/example/iostreams/http_client.cpp

廣播

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

  • boost_asio/example/multicast/receiver.cpp
  • boost_asio/example/multicast/sender.cpp

序列化

展示如何在asio中使用Boost.Serialization序列号和反序列化結構體,進而實作在Socket上傳輸資料對象.

  • boost_asio/example/serialization/client.cpp
  • boost_asio/example/serialization/connection.hpp
  • boost_asio/example/serialization/server.cpp
  • boost_asio/example/serialization/stock.hpp

服務

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

  • boost_asio/example/services/basic_logger.hpp
  • boost_asio/example/services/daytime_client.cpp
  • boost_asio/example/services/logger.hpp
  • boost_asio/example/services/logger_service.cpp
  • boost_asio/example/services/logger_service.hpp
  • boost_asio/example/services/stream_socket_service.hpp

SOCKS4協定

範例用戶端程式實作了通過代理通信的SOCKS4協定.

  • boost_asio/example/socks4/sync_client.cpp
  • boost_asio/example/socks4/socks4.hpp

SSL

用戶端和服務端範例程式示範了在異步操作中使用ssl::stream<>模闆.

  • boost_asio/example/ssl/client.cpp
  • boost_asio/example/ssl/server.cpp

逾時設定

下面的一系列範例展示了如何在指定時間之後取消長時間運作的異步操作.

  • boost_asio/example/timeouts/async_tcp_client.cpp
  • boost_asio/example/timeouts/blocking_tcp_client.cpp
  • boost_asio/example/timeouts/blocking_udp_client.cpp
  • boost_asio/example/timeouts/server.cpp

定時器

範例示範了如何定制deadline_time實作不同類型的計時器.

  • boost_asio/example/timers/tick_count_timer.cpp
  • boost_asio/example/timers/time_t_timer.cpp

Porthopper

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

  • boost_asio/example/porthopper/protocol.hpp
  • boost_asio/example/porthopper/client.cpp
  • boost_asio/example/porthopper/server.cpp

無阻塞

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

  • boost_asio/example/nonblocking/third_party_lib.cpp

UNIX領域的Socket

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

  • boost_asio/example/local/connect_pair.cpp
  • boost_asio/example/local/stream_server.cpp
  • boost_asio/example/local/stream_client.cpp

Windows

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

  • boost_asio/example/windows/transmit_file.cpp

繼續閱讀