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到首部):