1. 程式人生 > >muduo網路庫——Buffer類的設計與使用

muduo網路庫——Buffer類的設計與使用

muduo Buffer設計要點: 1.一塊連續的記憶體(char *p, int len)。

2.size()可以自動增長,以適應不同大小的訊息。

3.內部以std::vector<char>來儲存資料,並提供相應的訪問函式。

Buffer像一個queue,從末尾寫入資料,從頭部讀出資料。

TcpConnection會有兩個Buffer成員:input buffer, output buffer

1.input buffer, TcpConnection會從socket讀取資料,然後寫入input buffer (用Buffer::readFd()完成);客戶程式碼從input buffer讀取資料。

2.output buffer, 客戶程式碼會把資料寫入output buffer( 用TcpConnection::send()完成);TcpConnection從output buffer讀取資料並寫入socket。

Buffer的資料結構

Buffer的內部是一個std::vector<char>,有兩個成員指向該vector中的元素。如圖所示:

兩個index把vector的內容分為三塊:prependable, readable, writable

各塊大小:

  prependable = readIndex

        readable = writeIndex - readIndex

         writable = size() - writeIndex

muduo Buffer 裡有兩個常數kCheapPrepend 和 kInitialSize,定義了prependable的初始大小和writable的初始大小,readable初始大小為0。初始化之後Buffer資料結構如圖:

Buffer的操作

基本的read-write cycle

1.初始化之後向Buffer寫入了200位元組,那麼佈局如圖:

2.如果Buffer read() & retrieve()  (讀入) 了 50位元組,結果如圖:

3.然後寫入200位元組,writeIndex向後移動200位元組,如圖:

4.接下來,一次性讀入350位元組,由於資料全部讀完了,readIndex和writeIndex返回原位以備新一輪使用:

以上過程看作傳送方傳送了兩條訊息,長度分別為50和350位元組,接收方分兩次收到資料,每次200位元組,然後進行分包。

自動增長

muduo Buffer不是固定長度的,可以自動增長。

此時寫入1000位元組,buffer自動增長以容納全部資料。

由於vector重新分配了記憶體,原來指向其元素的指標會失效。readIndex返回到了前面。

size()與capacity()

使用vector的另一個好處是它的capacity()機制減少了記憶體分配的次數。

vector的capacity()以指數方式增長。

比如經過第一次增長,size()剛好滿足寫入的需求,如圖:

但此時vector的capacity()已經大於size(),在接下來寫入capacity()-size()位元組的資料時,都不會重新分配記憶體。

內部騰挪

若經過多次讀寫,readIndex移到了比較靠後的位置,留下了巨大的prependable空間,如圖:

這時候想寫入300位元組,但writable只有200位元組。muduo Buffer在這種情況下不會重新分配記憶體,而是把先有的資料移到前面去,騰出writable空間:

這樣就就可以寫入300位元組了。

前方新增

提供prependable空間,讓程式能以很低的代價在資料前面新增幾個位元組。

比如程式以固定的4個位元組表示訊息的長度,要序列化一個訊息,但是不知道有多長,那麼可以一直append()直到序列化完成(如圖寫入了200位元組)。

然後再在序列化資料的前面新增訊息的長度(把200這個數prepend到首部):