libevent: evbuffer緩衝

http://blog.sina.com.cn/s/blog_0001988f0100lb2v.html


在開發網絡程序的時候,特別是TCP基於字節流的數據,需要從字節流數據中解析出自己的通訊協議,比如讀一行數據:我們每次調用read函數的時候指定了我們期望讀多少數據,但這個數據並不一定正好能讀到‘\n’,這個時候就需要藉助一個緩衝區來保存多餘的數據,以便於和下一次讀到的數據合併在一起繼續分析。Evbuffer就是提供了這樣一個緩衝區。

 

關於libevent的緩衝模塊,主要就是圍繞evbuffer結構體展開。先看下evbuffer的定義:

 

struct evbuffer{
  // 當前有效緩衝區的內存起始地址
 u_char *buffer;
  // 整個分配(realloc)用來緩衝的內存起始地址
  u_char *orig_buffer;
  // origin_buffer和buffer之間的字節數
 size_t misalign;
  // 整個分配用來緩衝的內存字節數
 size_t totallen;
  // 當前有效緩衝區的長度(字節數)
 size_t off;
  //回到函數,當緩衝區有變化的時候會被調用
 void (*cb)(struct evbuffer *, size_t, size_t, void *);
  //回調函數的參數
 void *cbarg;
};


下面簡單介紹一下evbuffer提供的 API,我所使用的libevent版本是1.4.10-stable:

struct evbuffer* evbuffer_new(void)

動態分配一個struct evbuffer結構,需要調用evbuffer_free釋放內存。


void evbuffer_free(struct evbuffer *buffer)

釋放buffer所佔用的內存。


int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)

將data追加到evbuffer中

先判斷緩衝區的大小是否可以容納的下datlen大小,如果不能,則使用evbuffer_expand擴充容量。然後將data追加到evbuffer->buffer + evbuffer->off後。 並且更新有效緩衝區長度off。

如果datlen > 0, 並且設置了回調函數,則調用回調函數
返回值:成功返回0,失敗返回-1。


int evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)

移動數據從一個evbuffer到另一個evbuffer。

實際上還是調用了evbuffer_add添加數據到outbuf中。但會清除inbuf中的數據。

返回值:成功返回0, 失敗返回-1。


int evbuffer_add_printf( struct evbuffer *, const char* fmt, ...)

添加一個格式化的字符串到evbuffer尾部。


int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)

添加一個va_list格式的字符串到evbuffer尾部。

void evbuffer_drain(struct evbuffer *buf, size_t len)

從evbuffer起始位置刪除指定長度len字節數據

如果len的長度大於等於緩衝區的off的長度,則表明緩衝區的數據都被清空。 

如果緩衝區發生變化,並且設置了回調函數,則調用回調函數。


int evbuffer_expand(struct evbuffer *buf, size_t datlen)

該函數用於擴充evbuffer的容量。每次向evbuffer寫數據時,都是將數據寫到buffer+off後,buffer到buffer+off之間已被使用,保存的是有效數據,而orig_buffer和buffer之間則是因爲讀取數據移動指針而形成的無效區域。

 

evbuffer_expand的擴充策略在於:

1,計算出加上datlen後需要的緩衝區大小need

2, 判斷當前緩衝區的長度是否可以容納的下need大小,如果可以則不需要改變緩衝區的大小,直接返回。

3,如果當前緩衝哦你去的長度容納不下need大小,則判斷orig_buffer和buffer之間的空閒區域是否可以容納添加的數據,如果

可以,則移動buffer和buffer+off之間的數據到orig_buffer和orig_buffer+off之間,然後把新的數據拷貝到orig_buffer+off之後;

4,如果misalign不可以容納,那麼重新分配更大的空間(realloc),同樣會移動數據。

 

擴充內存的策略爲:確保新的內存區域最小尺寸爲256,且以乘以2的方式逐步擴大(256、512、1024、...)。

返回值:成功返回0, 失敗返回-1。


u_char *evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)

查找緩衝區中是否存在指定的字符串what。

注意這裏使用的是u_char類型,說明有可能查找的數據不是以’\0’結尾

如果存在返回指向字符串what的指針,沒有則返回NULL。


int evbuffer_read(struct evbuffer *buf, int fd, int howmuch)

調用read/recv函數,從文件描述符fd上讀取數據到evbuffer中。如果緩衝區不夠,調用evbuffer_expand擴充緩衝區。

int evbuffer_write(struct evbuffer *buffer, int fd)

把緩衝區中的數據,調用send/write函數寫入文件描述符fd上, 如果send/write函數寫入的字節數大於0,則調用evbuffer_drain刪除已寫的數據。

char *evbuffer_readline(struct evbuffer *buffer)

讀取數據以"\r\n","\n\r", "\r" 或者 "\n"結尾。

返回動態分配內存,需要調用者自己使用free來釋放內存。返回一個以“\0”結尾的字符串。

int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)

將evbuffer緩衝區中的數據讀到data中, 最多讀datlen字節。如果緩衝區裏的數據小於datlen,則拷貝緩衝區中全部數據。

然後調用evbuffer_drain刪除已讀數據。


void evbuffer_setcb(struct evbuffer *buffer,
    void (*cb)(struct evbuffer *, size_t, size_t, void *),
    void *cbarg)

設置回調函數。當緩衝區中發生變化時, 調用設置的回調函數。


 

Evbuffer提供的API已經全部介紹完畢,接下來我們通過一個實例進一步學習如何使用evbuffer, 想要使用evbuffer,系統裏必須已經安裝了libevent。

例子代碼如下:evbuffer-test.c


 

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <assert.h>
      4
      5 //引入libevent頭文件
      6 #include "event.h"
      7
      8 int main(int argc, char** argv)
      9 {
     10     struct evbuffer* buff = NULL;
     11     char c, c2[3] = {0};
     12
     13     buff = evbuffer_new();
     14     assert(buff != NULL);
     15
     16     evbuffer_add(buff, "1", 1);
     17     evbuffer_add(buff, "2", 1);
     18     evbuffer_add(buff, "3", 1);
     19     evbuffer_add_printf(buff, "%d%d", 4, 5);
     20     assert(buff->off == 5);
     21
     22     evbuffer_remove(buff, &c, sizeof(char));
     23     assert(c == '1');
     24     evbuffer_remove(buff, &c, sizeof(char));
     25     assert(c == '2');
     26     evbuffer_remove(buff, &c, sizeof(char));
     27     assert(c == '3');
     28     evbuffer_remove(buff, c2, 2);
     29     assert(strcmp(c2, "45") == 0);
     30
     31     assert(buff->off == 0);
     32
     33     evbuffer_add(buff, "test\r\n", 6);
     34     assert(buff->off == 6);
     35
     36     char* line = evbuffer_readline(buff);
     37     assert(strcmp(line, "test") ==0);

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