前言
可以說對于任何網絡庫(模塊)而言,一個緩沖模塊都是必不可少的。緩沖模塊主要用于緩沖從網絡接收到的數據,以及
用戶提交的數據(用于發送)。很多時候,我們還需要將網絡模塊層(非TCP層)的這些緩沖數據拷貝到用戶層,而這些內存拷貝
都會消耗時間。
在這里,我簡要分析下libevent的相關代碼(event.h和buffer.c)。
結構
關于libevent的緩沖模塊,主要就是圍繞evbuffer結構體展開。先看下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分別設置相關指針(一個指標)用于指示讀出位置和寫入位置。其大致結構如圖:

區域的大小,misalign表示buffer相對于orig_buffer的偏移,off表示有效數據的長度。
實際運作
這里我將結合具體的代碼分析libevent是如何操作上面那個隊列式的evbuffer的,先看一些輔助函數:
該函數主要操作一些指標,當每次從evbuffer里讀取數據時,libevent便會將buffer指針后移,同時增大misalign,減小off,
而該函數正是做這件事的。說白了,該函數就是用于調整緩沖隊列的前向指標。
該函數用于擴充evbuffer的容量。每次向evbuffer寫數據時,都是將數據寫到buffer+off后,buffer到buffer+off之間已被
使用,保存的是有效數據,而orig_buffer和buffer之間則是因為讀取數據移動指標而形成的無效區域。
evbuffer_expand的擴充策略在于,首先判斷如果讓出orig_buffer和buffer之間的空閑區域是否可以容納添加的數據,如果
可以,則移動buffer和buffer+off之間的數據到orig_buffer和orig_buffer+off之間(有可能發生內存重疊,所以這里移動調用的
是memmove),然后把新的數據拷貝到orig_buffer+off之后;如果不可以容納,那么重新分配更大的空間(realloc),同樣會移動
數據。
擴充內存的策略為:確保新的內存區域最小尺寸為256,且以乘以2的方式逐步擴大(256、512、1024、...)。
了解了以上兩個函數,看其他函數就比較簡單了。可以看看具體的讀數據和寫數據:
該函數用于添加一段用戶數據到evbuffer中。很簡單,就是先判斷是否有足夠的空閑內存,如果沒有則調用evbuffer_expand
擴充之,然后直接memcpy,更新off指標。
該函數用于將evbuffer中的數據復制給用戶空間(讀數據)。簡單地將數據memcpy,然后調用evbuffer_drain移動相關指標。
移動數據從一個evbuffer到另一個evbuffer。
實際上還是調用了evbuffer_add添加數據到outbuf中。但會清除inbuf中的數據。
返回值:成功返回0, 失敗返回-1。

查找緩沖區中是否存在指定的字符串what。
注意這里使用的是u_char類型,說明有可能查找的數據不是以’\0’結尾
如果存在返回指向字符串what的指針,沒有則返回NULL。
讀取數據以"\r\n","\n\r", "\r" 或者 "\n"結尾。
返回動態分配內存,需要調用者自己使用free來釋放內存。返回一個以“\0”結尾的字符串。
void (*cb)(struct evbuffer *, size_t, size_t, void *),
void *cbarg)
Evbuffer提供的API已經全部介紹完畢,接下來我們通過一個實例進一步學習如何使用evbuffer, 想要使用evbuffer,系統里必須已經安裝了libevent。
例子代碼如下:evbuffer-test.c
#include <string.h>
#include <assert.h>
//引入libevent頭文件
#include "event.h"
int main(int argc, char** argv)
{
struct evbuffer* buff = NULL;
char c, c2[3] = {0};
buff = evbuffer_new();
assert(buff != NULL);
evbuffer_add(buff, "1", 1);
evbuffer_add(buff, "2", 1);
evbuffer_add(buff, "3", 1);
evbuffer_add_printf(buff, "%d%d", 4, 5);
assert(buff->off == 5);
evbuffer_remove(buff, &c, sizeof(char));
assert(c == '1');
evbuffer_remove(buff, &c, sizeof(char));
assert(c == '2');
evbuffer_remove(buff, &c, sizeof(char));
assert(c == '3');
evbuffer_remove(buff, c2, 2);
assert(strcmp(c2, "45") == 0);
assert(buff->off == 0);
evbuffer_add(buff, "test\r\n", 6);
assert(buff->off == 6);
char* line = evbuffer_readline(buff);
assert(strcmp(line, "test") ==0);
assert(buff->off == 0);
free(line);
evbuffer_free(buff);
printf("ok\n");
return 0;
}