天天看點

BOOST ASIO 學習專貼

本文已于20170903更新完畢,所有boost asio 代碼均為本人手抄。編譯器為vs2013,并且所有代碼已經上傳,本文下方可下載下傳源碼

BOOST ASIO 學習專貼

<a href="https://files.cnblogs.com/files/zhangdongsheng/boost_asio_%E4%BB%A3%E7%A0%81_vs2013%E7%BC%96%E8%AF%91%E6%88%90%E5%8A%9F.rar" target="_blank"> </a>

為了學習boost asio庫,我是從boost的官方boost asio的教程學起的。

每一個示例我都抄寫了一遍以加深記憶,每一個例子我都用自己的話概括一遍,雖然概括的不是很好,代碼覺得難懂的地方我都加注釋。

1.同步使用Timer

本便使用了boost::asio::deadline_timer,這個timer有兩種狀态:過期和不過期。wait函數調用一個過期的timer直接傳回。

2.異步使用Timer

下在示範了使用deadline_timer的asyn_wati函數實作異步等待。但要注意的一點是異步等待必須要調用io.run才可以。而且必須在io.run函數執行之前調用asyn_wait,否則io.run會立即傳回,因為他沒有可以做的事。這說明io.run必須至少有一個等待的,否則它會直接傳回。asio函數保證回調函數執行和io.run所在的線程一樣!

3.為回調函數綁定參數

這個例子一個是說明異步Timer的可持續性問題,也就是在回調中設定Time的逾時時間。另一個說明回調函數參數的綁定 。但是實際發現我官的代碼沒有發生那個重複回調的效果。原因是我隻是調用了expire_at而沒有調用再次等待函數async_wait。這讓我更加明白expires_at這個函數相當于下次觸發的時間。而async_wait送出一個等待申請。

async_wait送出一次,回調函數執行一次,而expire_at設定下次回調函數調用的時間。

4.類成員做為timer的回調函數

這個例子主要示範了,如何綁定一個類成員函數作為一個回調

4.在多線程程式中的同步回調

先前的例子通過io_service.run和同步回調在同一個線程内,正如你所知的那樣,asio保證回調函數隻能被在io_service.run()所在的線程調用 。是以,隻有在一個線程内調用io_service::run保證回調函數不會并發執行。這樣在伺服器程式中有兩個局限性:

1.當回調函數執行時間比較長時響應太慢

2.沒有起到多處理器的優勢

如果你注意到這個局限性,一個可供選擇的方案是建立一個線程池去調用io_service.run()函數,這樣實作的回調的并發,我們需要去同步一個共享變量。

下面的例子使用到了An boost::asio::strand ,他保證這些回調函數通過strans派遣,它可以允許一個回調函數在另一個回調函數執行之前完成。簡單點說,這裡的strand就是讓回調函數不會并發的執行。但是這裡的strand到底的意圖在哪裡?不是要示範多線程執行回調嗎?這裡又做了strand使回調又依次執行好想沒有達到多線程效果

下面有時間研究一下 boost::asio::strand的用法

5.簡單的一個TCP服務端

下面程式示範一個boost做的最簡單的一個服務端程式,用戶端連接配接之後伺服器給用戶端發送一個目前時間的字元串

 下面值得一提的是tcp::acceptor,他被封裝為socket的服務端接收器,構造他時需要一個io_service和一個tcp::endpoint。

6.簡單的一個TCP用戶端

7.TCP異步服務端

以上代碼調用異步函數asyn_accept和asyn_write分别進行異步接受socket和異步socket發送。

以上代碼是官方tutorial的代碼,有幾點特别的地方值得學習:

構造函數私有化

一般自己寫代碼構造函數不可能給私有化,而類tcp_connection使用一個靜态類成員函數Create生産一個對象,而使得類的構造函數可以私有。

使用enable_shared_from_this

boost類enable_shared_from_this的好處是避免在類成員函數中傳遞this而傳遞一個shared_ptr智能指針,這樣不用擔心釋放的問題。而在這裡,如果傳指針則有可能所持有的指針指向的對象已經被釋放,如果用shared_ptr則可以保證不被釋放,引用官方的一句話:We will use <code>shared_ptr</code> and <code>enable_shared_from_this</code> because we want to keep the <code>tcp_connection</code> object alive as long as there is an operation that refers to it.

不指定沒有用的參數,有可能注意到handle_write()沒有error和byte_transfered參數,因為body中沒有用到這兩個參數,如果參數不使用可能以移除參數

8.Custom Allocation

以上代碼在調用socket.async_read_some的時候,第二個參數原來是一個Handler,原型如下:

首先:回調用函數應該是一個執行體,也就是std::function,而這裡來一個custom_alloc_handler&lt;Handler&gt;對象,對象也可以當作執行體?

其次:這個函數沒有用到asio_handler_allocate和asio_handler_deallocate,我也不知道如何使用。這個放到以後再研究 

經過學習和查詢資訊得出的結果:

異步操作可以增加一個臨時的配置設定對象asio_handler_allocate。因為異步操作有一個handler函數對象,這個臨時對象可以堪稱是與handler函數對象相關聯的。本例中asio_handler_allocate為handler類對象的一個友元成員函數。這樣在配置設定記憶體時,預設就調用此函數進行配置設定記憶體。任何與handler相關聯的臨時對象會在handler執行完之後被析構,而asio_handler_allocate這裡除了size參數可以額外增加參數,例如本例中的this_handler參數一樣,是以這裡允許同一塊記憶體可以被後來的異步操作重複利用,asio_handler_allocate原型如下:

Handler允許有多種形式存在

函數形式

類對象(重載括号運算符)

類成員函數

通過以上知識點,可以清楚知道本例代碼是如何執行的了。

9.Buffers

代碼解析:

本例主要示範了,異步操作中可以自定義的buffer。

以上代碼自定義一個類shared_const_buffer,在調用async_write用這個類對象。async_write有多個重載,這裡主要說示例中用到的重載形式,即:

第二具模闆參數ConstBufferSequence為一個模闆參數,自定義ConstBufferSequence模闆類有一些要求如下 :

在下面要求清單中,X表示為一個包含類型T對象的類。a表示表示一個類型為X的值,u表示一個辨別符

本例中T為boost::asio::const_buffer buffer_,X為本例中的shared_const_buffer。

X::value_type 傳回類型為T,用于表示X實際表示的value_type為T,本例中為boost::asio::const_buffer

X::const_iterator 指向T的疊代器類型,表示iterator類型實際為哪種類型,本例中為 const boost::asio::const_buffer * 

X(a)  構造函數

X u(a) 暫時不知如何解釋

(&amp;a)-&gt;~X() 暫時不知如何解釋

a.begin() 傳回起始疊代器

a.end() 傳回終止疊代器

10.Chat_message資料包類

這個類比較簡單,他把一個資料包定義為頭和體。頭部是一個整形,代表body的大小。 

11.Chat_Server詳解

先上代碼

服務端做異步監聽,當有用戶端到來,把這個用戶端(session) 放到聊天室對象裡,當這個用戶端斷開時,從聊天室用戶端清單裡删除。

這個聊天室實作了一個廣播功能,當用戶端發送消息至伺服器時,伺服器給所有用戶端廣播這條消息,并且聊天室記錄最近用戶端發送到伺服器的消息,當用戶端連接配接到伺服器時,伺服器主動把最近消息記錄發送給這個用戶端。

這裡要注意到一點的是,他的發送是類似消息驅動的形式,就是用一個對象儲存要發送的消息,當發送成功回調OnSend裡發現有未發完的消息時,再骈PostSend。而不是主動發送。我暫時不知道這種做法的意圖。但是可以注意到的一點是這種發送是依次的,也就是PostSend順序是這樣的 PostSend OnSend PostSend OnSend,而我們經常的做法則是PostSend PostSend OnSend OnSend。這個好處不言而喻。提供了一種緩存機制。

12.Chat_Client詳解

這個用戶端沒有什麼特點,最大的特别就是我上節在服務端說到的,消息回調Post機制。

13.echo

echo都是非常簡單的socket示例,暫時不做熬述

14.Futures

知識點:

 io_service::work 這是一個很小的輔助類,隻支援構造函數和析構函數。構造一個 work時,outstanding_work_+1,使得io.run在完成異步消息之後判斷outstanding_work_時不為0,因而會使io.run()不至于傳回。通俗的講它就是讓io.run一直運作不退出,隻到work析構。

std::future 他是擷取異步執行函數的傳回值的,相當于你建立了一個線程線程在計算某個結果,你要得到這個結果時,你得同步一下,還要看一下,結果算完了沒有。future就是做這件事的。關于這個std::future我會另外開一篇文章寫一下。這裡有一篇檔案詳細介紹一下這個std::future幹了什麼http://blog.csdn.net/wangshubo1989/article/details/49872199

io.stop 這個函數是告訴io_service要停止 。

18.HttpServer

本例用boost asio  寫了一個簡易http伺服器,與前面的相比新的知識點不多。

下面提供源碼下載下傳:

<a href="https://files.cnblogs.com/files/zhangdongsheng/boost_asio_%E4%BB%A3%E7%A0%81_vs2013%E7%BC%96%E8%AF%91%E6%88%90%E5%8A%9F.rar" target="_blank">源碼下載下傳</a>