天天看點

資料封裝evBuffer、資料緩沖Bufferevent[ evBuffer - 資料封裝 ][ BufferEvent - 資料緩沖 ]

[ evBuffer - 資料封裝 ]

一.evBuffer介紹

BufferEvent在進行讀取和寫入資料時,将資料存放在evBuffer中,是以,要想擷取資料的相關資訊—>需要對evBuffer結構進行解析。

二.struct evbuffer* 變量的指派2種方式

方式1:手動建立+手動向evbuffer中添加資料

struct evbuffer* evbuf = evbuffer_new();  //手動建立evbuffer
evbuffer_add(evbuf,"I am a student",100); //手動将字元串添加到evbuf中
evbuffer_add_printf(evbuf,"%s,%s,%d","Tom","Student",24); //手動将字元串格式化添加到evbuf中
...  ... 
evbuffer_free(evbuf); //手動釋放evbuffer
           

方式2:bufferevent_get_input/output–>擷取輸入/輸出evbuffer

struct evbuffer *input =bufferevent_get_input(bev);   //擷取輸入evbuffer
struct evbuffer *output =bufferevent_get_output(bev); //擷取輸出evbuffer
           

注意:

  1. bufferevent_get_input/bufferevent_get_output隻是擷取輸入/輸出緩沖區中的資料,并沒有将資料從evbuffer中移走。
  2. 而,bufferevent_write是真正的向輸出緩沖區中寫入資料;bufferevent_read是真正的讀走輸入緩沖區中的資料。

三.與evbuffer相關的函數API總結

建立 / 銷毀evbuffer
struct evbuffer *evbuffer_new(void); 配置設定和傳回一個新的空的evbuffer
void evbuffer_free(struct evbuffer *buf); 釋放evbuffer和其内容
擷取/讀寫evbuffer
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev); 擷取eventbuffer的輸入緩沖區evbuffer
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev); 擷取eventbuffer的輸出緩沖區evbuffer
size_t evbuffer_get_length(const struct evbuffer *buf); 傳回evbuffer的位元組數length
int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size); 将data中的資料寫size個位元組存放在bufev中
int bufferevent_write_buffer(struct bufferevent *bufev,struct evbuffer *buf);
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); 從bufev中讀取size個位元組的資料存放在data中
int bufferevent_read_buffer(struct bufferevent *bufev,struct evbuffer *buf);

逐行讀取:evbuffer_readln

/*
功能:從緩沖區buffer中讀取一行資料,存儲到傳回值char*中
傳回值:成功傳回讀取到的一行資料(傳回的讀取到的字元串不包括行結束符),失敗傳回NULL
參數:
	n_read_out:被設定為讀取到的一行資料的位元組個數
	eol_style:行結束格式
*/
char* evbuffer_readln(struct evbuffer* buffer,
	size_t *n_read_out,
	enum evbuffer_eol_style eol_style 
	); 
           
行結束格式
EVBUFFER_EOL_LF 行尾是單個換行符(也就是\n,ASCII 值是0x0A)
EVBUFFER_EOL_CRLF_STRICT 行尾是一個回車符,後随一個換行符(也就是\r\n,ASCII 值是0x0D 0x0A)
EVBUFFER_EOL_CRLF 行尾是一個可選的回車,後随一個換行符(也就是說,可以是\r\n 或者\n)。這種格式對于解析基于文本的網際網路協定很有用,因為标準通常要求\r\n 的行結束符,而不遵循标準的用戶端有時候隻使用\n
EVBUFFER_EOL_ANY 行尾是任意數量、任意次序的回車和換行符。這種格式不是特别有用。它的存在主要是為了向後相容。
evbuffer之間資料的轉移
int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src); 将 src 中的 [所有資料] 轉移到 dst 末尾
int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer *src); 将 src 中的 [所有資料] 轉移到 dst 之前
int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,size_t datlen); 将 src 中的 [datlen位元組的資料] 轉移到 dst 末尾
向evbuffer中添加資料
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); 将 data 處的 datalen 位元組的資料添加到 buf 的末尾
int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size); 将 data 處的 datalen 位元組的資料添加到 buf 之前
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, …); 添加格式化的資料到 buf 末尾
int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap); 添加格式化的資料到 buf 末尾
從evbuffer中移除資料
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); 從buf前面複制和移除datlen位元組的資料存放到data記憶體中
int evbuffer_drain(struct evbuffer *buf, size_t datlen); 從buf前面移除datlen位元組的資料,不進行複制
從 evbuffer 中複制出資料(即擷取緩沖區資料的副本),而不移除緩沖區中的資料
int evbuffer_copyout(struct evbuffer *buf, void *data, size_t datlen); 從buf前面複制datlen位元組的資料存放到data記憶體中,但不移除資料

[ BufferEvent - 資料緩沖 ]

一.bufferevent介紹

  1. bufferevent由一個底層的傳輸端口(如套接字),一個讀/input緩沖區和一個寫/output緩沖區組成。
  2. 每個 bufferevent 有兩個與資料相關的回調:讀取回調函數和寫入回調函數。 bufferevent中回調函數觸發的條件與普通的event回調函數觸發的條件不同( 普通的事件在底層傳輸端口已經就緒,可讀或可寫時就會執行回調函數,而 bufferevent 是在讀取或寫入了一定量的資料後才會調用回調函數)
  3. 也請注意:目前 bufferevent 隻能用于像 TCP 這樣的面向流的協定,将來才可能會支援像 UDP 這樣的面向資料報的協定
  4. bufferevent 也有“錯誤事件”回調,用于向應用通知非面向資料的事件,如連接配接已經關閉或者發生錯誤。定義了下列事件标志:
事件标志
BEV_EVENT_READING 讀取操作時發生某事件,具體是哪種事件請看其他标志
BEV_EVENT_WRITING 寫入操作時發生某事件,具體是哪種事件請看其他标志
BEV_EVENT_TIMEOUT 發生逾時
BEV_EVENT_CONNECTED 請求的連接配接過程已經完成
BEV_EVENT_EOF 遇到檔案結束訓示
BEV_EVENT_ERROR 操 作 時 發 生 錯 誤 。 關 于 錯 誤 的 更 多 信 息 , 請 調 用EVUTIL_SOCKET_ERROR()

二.水位線與回調函數觸發情況

1. 讀取低水位線/讀取高水位線

資料封裝evBuffer、資料緩沖Bufferevent[ evBuffer - 資料封裝 ][ BufferEvent - 資料緩沖 ]

2. 寫入低水位線/寫入高水位線

資料封裝evBuffer、資料緩沖Bufferevent[ evBuffer - 資料封裝 ][ BufferEvent - 資料緩沖 ]

三.延遲回調

預設情況下,在相應的條件發生時,bufferevent的回調函數将會立即被調用。但是,在依賴關系複雜的情況下,這種立即調用會造成麻煩。比如說,假如某個回調函數在evbuffer A空的時候向其中寫入資料,而另一個回調函數在evbuffer A滿的時候從中取出資料。這些調用都是在棧上發生的,在依賴關系足夠複雜的時候,有棧溢出的風險。

==> 要解決此問題,可以請求 bufferevent(或者 evbuffer)延遲其回調。條件滿足時,

延遲回調不會立即調用,而是在 event_loop()調用中被排隊,然後在通常的事件回調之後執行

四.bufferevent的使用、相關API

bufferevent使用步驟
建立基于套接字的bufferevent struct bufferevent* bufev = bufferevent_socket_new(base,fd,options)
為bufev設定回調函數 bufferevent_setcb(bufev,read_cb,write_cb,event_cb,cbarg);
設定水位線 bufferevent_setwatermark
啟用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或 EV_READ | EV_WRITE事件 bufferevent_enable/bufferevent_disable
操作bufferevent中的資料
資料封裝evBuffer、資料緩沖Bufferevent[ evBuffer - 資料封裝 ][ BufferEvent - 資料緩沖 ]

(1) bufferevent_socket_new:建立基于套接字的bufferevent

struct bufferevent *bufferevent_socket_new(
	struct event_base *base, 
	evutil_socket_t fd, 
	enum bufferevent_options options
);
           

傳回值:成功傳回一個bufferevent,失敗傳回NULL

參數

  • base:event_base
  • fd:表示套接字的檔案描述符(如果想以後設定檔案描述符,則設定fd為-1)
  • options: bufferevent 選項的位掩碼(bufferevent選項标志)
options:bufferevent 選項的位掩碼 含義
BEV_OPT_CLOSE_ON_FREE 常用 釋放bufferevent時關閉底層傳輸端口。這将關閉底層套接字,釋放底層bufferevent等
BEV_OPT_THREADSAFE 自動為 bufferevent 配置設定鎖,這樣就可以安全地在多個線程中使用 bufferevent
BEV_OPT_DEFER_CALLBACKS bufferevent 延遲所有回調
BEV_OPT_UNLOCK_CALLBACKS 預設情況下,如果設定 bufferevent 為線程安全 的,則 bufferevent 會在調用使用者提供的回調時進行鎖定。設定這個選項會讓 libevent 在執行回調的時候不進行鎖定

(2)bufferevent_free:釋放bufferevent

void bufferevent_free(struct bufferevent *bev);

  1. bufferevent内部具有引用計數 (如果釋放時還有未決的延遲回調,則在回調完成之前bufferevent不會被删除)
  2. 如果設定了 BEV_OPT_CLOSE_ON_FREE 标志,并且 bufferevent 有一個套接字或者底層 bufferevent 作為其傳輸端口,則釋放 bufferevent 将關閉這個傳輸端口

(3) 設定[水位]

設定bufev的讀取水位、寫入水位,或者二者同時設定。

[1] 如果events參數設定了EV_READ,調整讀取水位

[2] 如果events參數設定了EV_WRITE,調整寫入水位

其中,對于高水位,0表示“無限”

void bufferevent_setwatermark(
	struct bufferevent *bufev, 
	short events,
	size_t lowmark, 
	size_t highmark
);
           

(4)設定/擷取 [回調函數]

(4-1)回調函數類型

當相應的事件到來時,會觸發相應的回調函數,此時

回調函數的參數

已經會被

自動指派

typedef void (*bufferevent_data_cb)( 
	struct bufferevent *bev, //觸發了的事件bufferevent
	void  *ctx  //調用bufferevent_setcb()時使用者提供的 cbarg 參數
	);
	
typedef void (*bufferevent_event_cb)(
	struct bufferevent *bev,
	short events, //一個表示事件标志的位掩碼
	void *ctx
    );
           

(4-2)設定/擷取bufferevent的回調函數、參數

void bufferevent_setcb( //給bufev設定回調函數、回調函數參數
	struct bufferevent *bufev,
	bufferevent_data_cb readcb, //已經讀取足夠的資料時被回調
	bufferevent_data_cb writecb, //已經寫入足夠的資料時被回調
	bufferevent_event_cb eventcb, //發生錯誤時被回調
	void *cbarg //使用者提供的 cbarg 參數
	);
	
void bufferevent_getcb( //擷取bufev的回調函數、回調函數參數
	struct bufferevent *bufev,
	bufferevent_data_cb *readcb_ptr,
	bufferevent_data_cb *writecb_ptr,
	bufferevent_event_cb *eventcb_ptr,
	void **cbarg_ptr
	);
           

(5) 啟用bufferevent_enable / 禁用bufferevent_disable

  1. 預設情況下,新建立的bufferevent的寫入EV_WRITE 是啟用的,讀取EV_READ是沒有啟用的。
  2. 可以啟用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。
  3. 沒有啟用讀取或者寫入事件時, bufferevent 将不會試圖進行資料讀取或者寫入。
void bufferevent_enable(  //啟用bufev上的events事件
	struct bufferevent *bufev, 
	short events
	);
void bufferevent_disable( //禁用bufev上的events事件
	struct bufferevent *bufev, 
	short events
	);
	
short bufferevent_get_enabled( //擷取 bufferevent 上目前啟用的事件
	struct bufferevent *bufev
	);
           

(6)操作bufferevent中的資料

(6.1) 向bufferevent的輸出緩沖區添加資料

成功時,都傳回 0;發生錯誤時,則傳回-1

//從data處開始的size位元組資料從記憶體中移除,并添加到輸出緩沖區的末尾
int bufferevent_write(
	struct bufferevent *bufev,
	const void *data, 
	size_t size
	);
//将buf中的所有内容從記憶體中移除,并添加到輸出緩沖區的末尾
int bufferevent_write_buffer(
	struct bufferevent *bufev,
	struct evbuffer *buf
	);
           

(6.2) 從bufferevent的輸入緩沖區移除資料

//至多從輸入緩沖區bufev移除 size 位元組的資料,将其存儲到記憶體中 data 處
//傳回值:實際移除的位元組數
//注意,對于 bufferevent_read(),data 處的記憶體塊必須有足夠的空間容納 size 位元組資料。
size_t bufferevent_read(
	struct bufferevent *bufev, 
	void *data, 
	size_t size
	);
	
//抽空輸入緩沖區bufev的所有内容,将其放置到 buf 中
//成功時,都傳回 0;發生錯誤/失敗時,則傳回-1
int bufferevent_read_buffer(
	struct bufferevent *bufev,
	struct evbuffer *buf
	);
           

(7)bufferevent的清空操作

  1. bufferevent_flush()相當于fflush()重新整理輸出緩沖區中的資料,使資料顯示到标準輸出上。
  2. 但是bufferevent_flush()函數的功能更加強大,bufferevent_flush()函數既可以重新整理輸出緩沖區中的資料,又可以重新整理輸入緩沖區中的資料
  3. bufferevent_flush()函數重新整理緩沖區的功能,可以強制從底層傳輸端口寫入或讀取資料,不受水位線的限制
int bufferevent_flush(
	struct bufferevent *bufev,
	short iotype, 
	enum bufferevent_flush_mode state
	);
           

傳回值

  • 失敗:-1
  • 成功,且沒有資料被清空:0
  • 成功,且有資料被清空:1

參數

  • iotype:EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE, 用于訓示應該處理讀取、寫入, 還是二者都處理
  • state:可以是 BEV_NORMAL、BEV_FLUSH 或者 BEV_FINISHED
    • BEV_NORMAL 和 BEV_FLUSH 的差別依賴于具體的bufferevent 類型
    • BEV_FINISHED訓示應該告知另一端,沒有更多資料需要發送了