Muduo庫中的Buffer設計

Muduo庫中的Buffer類設計

  • 非阻塞網絡編程中應用層buffer是必須的
    原因:非阻塞IO的核心思想是避免阻塞在read()或write()或其他IO系統調用上,這樣可以最大限度複用thread-of-control,讓一個線程能服務於多個socket連接。IO線程只能阻塞在IO-multiplexing函數上,如select()/poll()/epoll_wait()。這樣一來,應用層的緩衝是必須的,每個TCP socket都要有input buffer和output buffer。
  • TcpConnection必須有output buffer
    原因:使程序在write()操作上不會產生阻塞,當write()操作後,操作系統一次性沒有接受完時,網絡庫把剩餘數據則放入output buffer中,然後註冊POLLOUT事件,一旦socket變得可寫,則立刻調用write()進行寫入數據。——應用層buffer到操作系統buffer
  • TcpConnection必須有input buffer
    原因:當發送方send數據後,接收方收到數據不一定是整個的數據,網絡庫在處理socket可讀事件的時候,必須一次性把socket裏的數據讀完,否則會反覆觸發POLLIN事件,造成busy-loop。所以網路庫爲了應對數據不完整的情況,收到的數據先放到input buffer裏。——操作系統buffer到應用層buffer

總結:input和output都是針對應用層來說的

Buffer設計

1、Buffer要求

  • 對外表現爲一塊連續的內存(char*,len);
  • 其size()可以自動增長,以適應不同大小的消息;
  • 內部以vector來保存數據,並提供相應的訪問函數。

Buffer就像一個queue,從尾部寫入數據,從頭部讀出數據。

2、Buffer模型

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

  • input buffer:TcpConnection會從socket讀取數據,然後寫入input buffer(這一步實際上是由Buffer::readFd()完成的),客戶代碼從input buffer讀取數據。
  • output buffer:客戶代碼寫入output buffer(這一步實際上是由TcpConnection::send()完成的),TcpConnection從output buffer讀取數據並寫入socket。

總結:input和output是針對客戶代碼而言,客戶代碼從input讀,往output寫。TcpConnection的讀寫正好相反。

圖1 Buffer示意圖

3、Muduo Buffer的數據結構

Buffer的內部是一個vector of char,它是一塊連續的內存。Buffer有兩個數據成員:readIndex和writeIndex,指向該vector中的元素。數據結構如下:
圖2 Buffer數據結構
兩個indices把vector的內容分爲三塊:prependable、readable、writable,各塊大小計算如下:

  • prependable=readIndex
  • readable=writeIndex-readIndex
  • writable=size()-writeIndex
    Muduo Buffer裏有兩個常數kCheapPrepend和kInitialSize,定義了prependable和writeable的初始大小(readable的初始大小爲0)。在初始化之後,Buffer的數據結構如下:
    圖3 Buffer初始化數據結構

4、Muduo Buffer的操作

基本的read-write cycle

向Buffer寫入200字節,那麼佈局如下:
圖4 向Buffer寫入200字節
現在有人從Buffer讀入了50字節,那麼佈局如下:
圖5 向Buffer讀入50字節
接下來一次性讀入150字節,則readIndex和writeIndex返回原位以備新一輪使用,佈局如下所示:

圖6 一次性讀完數據

自動增長

Muduo Buffer不是固定長度的,它是可以自動增長的,這是使用vector的直接好處。即當我們需要寫入數據大於size()時,那麼Buffer會自動增長以容納全部數據。由於vector重新分配了內存,原來指向它元素的指針會失效,這就是爲什麼readIndex和writeIndex是整數下標而不是指針。

size()與capacity()

使用vector的另一個好處是它的capacity()機制減少了內存分配的次數。即可以預先分配大的容量,當size不足時,不需要重新分配內存,可以直接進行擴充size。

內部騰挪

有時候經過若干次讀寫,readIndex移到了比較靠後的位置,留下了巨大的prependable空間,當這個時候,我們想寫入數據,而writable空間不足,怎麼辦?muduo Buffer在這種情況下不會重新分配內存,而是先把已有的數據移到前面去,佔用prependable空間,騰出writable空間。這樣做原因是,如果重新分配內存,反正也是把數據拷貝到新分配的內存區域,代價只會更大。

prepend

muduo Buffer提供了prependable空間,讓程序以很低的代價在數據前面添加幾個字節。比如,程序以固定的4個字節表示消息的長度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章