翻譯自:http://www.wangafu.NET/~nickm/libevent-book/Ref6_bufferevent.html
原文:http://blog.csdn.net/windeal3203/article/details/52815520
大多數時候,應用程序除了響應請求外,還需要處理數據(及其緩存)。當我們想要寫數據是,通常會有以下步驟
- 決定要向連接中寫入什麼數據, 把這些數據放入緩存
- 等待連接可寫
- 寫入儘可能多的數據
記住寫入了多少數據,如果還有數據沒寫完。等待連接再次變爲可寫狀態。
這樣的I/O緩衝方式很常見,因而libevent爲此提供了一種通用機制。
“bufferevent”由一個底層傳輸系統(比如socket),一個讀緩衝區和一個寫緩衝區組成。
對於普通的events, 當底層傳輸系統可讀或者可寫時,調用回調方式; 而bufferevent提供了一種替代方式:它在已經寫入、或者讀出數據的時候才調用回調函數。
libevent有多種bufferevent, 它們共享通用的接口。截止至本文撰寫時,已經存在以下類型:基於socket的bufferevent:
在底層流式socket上發送和接收數據,使用event_*
接口作爲其後端。- 異步IO的bufferevent:
使用Windows IOCP
接口來想底層流式socket發送和接收數據。(Windows only, 實驗性的) - 過濾型的bufferevent:
在數據傳送到底層bufferevent對象之前,對到來和外出的數據進行前期處理的bufferevent,比如對數據進行壓縮或者轉換 成對的bufferevent:
注意:截止Libevent2.0.2-alpha版本,bufferevent接口還沒有完全覆蓋所有的bufferevent類型。換句話說,並不是下面介紹的每一個接口都能用於所有的bufferevent類型。Libevent開發者會在未來的版本中解決該問題。
還要注意:bufferevent目前僅能工作在流式協議上,比如TCP。未來可能會支持數據報協議,比如UDP。
本文所有的函數和類型都是在<event2/bufferevent.h>
文件中聲明。與evbuffers相關的函數在<event2/buffer.h>
中聲明,有關信息參考下一章。
1. bufferevent和evbuffers
每一個bufferevent 都有一個輸入緩衝區和輸出緩衝區, 這些緩衝區(buffer)都是struct evbuffer
類型。 當你有數據要寫入bufferevent, 你要先把數據填入output buffer,; 當bufferevent上有數據需要讀取時,則可以從input buffer中抽取出來。
evbuffer
接口支持很多操作,會在以後的章節中進行討論。
2. 回調函數和水位數
每一個bufferevent都有兩個數據相關的回到函數, 一個 讀回調和一個寫回調。 默認情況下,當有數據從底層傳輸讀取時,讀回調函數就會被調用; 當ouput buffer想底層傳輸寫入足夠多的數據時, 寫回調函數就會被調用。
通過調整bufferevent的讀取和寫入“水位線”(watermarks),可以改變這些函數的默認行爲。
每個bufferevent都有4個水位線:
- 讀 低水位:
當bufferevent的輸入緩衝區的數據量到達該水位線或者更高時,bufferevent的讀回調函數就會被調用。該水位線默認爲0,所以每一次讀取操作都會導致讀回調函數被調用。 - 讀 高水位:
如果bufferevent的輸入緩衝區的數據量到達該水位線時,那麼bufferevent就會停止讀取,直到輸入緩衝區中足夠多的數據被抽走,從而數據量再次低於該水位線。默認情況下該水位線是無限制的,所以從來不會因爲輸入緩衝區的大小而停止讀取操作。 - 寫 低水位:
當寫操作使得輸出緩衝區的數據量達到或者低於該水位線時,才調用寫回調函數。默認情況下,該值爲0,所以輸出緩衝區被清空時才調用寫回調函數。 寫 高水位:
並非由bufferevent直接使用,對於bufferevent作爲其他bufferevent底層傳輸系統的時候,該水位線纔有特殊意義。所以可以參考後面的過濾型bufferevent。
bufferevent還提供了error
或者event
的回調函數,用來通知應用程序關於非數據相關的事件。比如:關閉連接或者發生錯誤。 爲此,定義了以下event
標誌:BEV_EVENT_READING:讀操作期間發生了事件。具體哪個事件參見其他標誌。
- BEV_EVENT_WRITING:寫操作期間發生了事件。具體哪個事件參見其他標誌。
- BEV_EVENT_ERROR:在bufferevent操作期間發生了錯誤,可以調用
EVUTIL_SOCKET_ERROR
函數來得到更多的錯誤信息。 - BEV_EVENT_TIMEOUT: bufferevent上發生了超時
- BEV_EVENT_EOF: bufferevent上遇到了EOF標誌
- BEV_EVENT_CONNECTED:在bufferevent上請求的連接已經完成
3. 延期回調
默認情況下, bufferevent的回調函數在響應的條件滿足時會立即執行(evbuffer 回調函數也是如此, 後面會講到)。 當依賴關係比較複雜時, 這種立即執行 的機制會導致一些問題。比如說, 假設 有一個回調函數 是用於在evbuffer A爲空時向其填入數據, 而另一個回調函數則在evbuffer A爲滿時 從中取出數據進行處理。如果所有這些調用都發生在棧上的話,在依賴關係足夠複雜的時候,有棧溢出的風險。
爲了解決這個問題,你可以告訴bufferevent(或evbuffer)它的回調函數應該被延遲執行。當延遲迴調函數 的對應條件滿足時, 延遲迴調函數不會立即執行,而是加入到event_loop()
調用隊列中。然後在常規的event回調之後執行。 。
4. bufferevent 的選項標誌
在穿件bufferevent時,可以通過一些選項來修改它的行爲:
- BEV_OPT_CLOSE_ON_FREE: 當釋放bufferevent時,關閉底層的傳輸系統。 這將關閉底層套接字,釋放底層bufferevent等。
- BEV_OPT_THREADSAFE: 自動爲bufferevent分配鎖, 從而在多線程中可以安全使用。
- BEV_OPT_DEFER_CALLBACKS: bufferevent會將其所有回調函數進行延遲調用設置。
- BEV_OPT_UNLOCK_CALLBACKS: 默認情況下, 當設置bufferevent爲線程安全的時候,任何用戶提供的回調函數調用時都會鎖住bufferevent的鎖, 設置改標誌可以在提供的回調函數被調用時不鎖住bufferevent的鎖。
5. 基於socket 的bufferevent
最簡單的bufferevents就是基於socket類型的bufferevent。基於socket的bufferevent使用Libevent底層event機制探測底層網絡socket何時準備好讀和寫,而且使用底層網絡調用(比如readv
,writev
,WSASend
或WSARecv
)進行傳送和接受數據。
5.1 創建 基於socket 的bufferevent
使用bufferevent_socket_new()
可以創建一個基於socket的bufferevent。
struct bufferevent *bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options);
base:表示event_base
options:是bufferevent
選項的位掩(BEV_OPT_CLOSE_ON_FREE
等)。
fd:參數是一個可選的socket文件描述符。如果希望以後再設置socket文件描述符,可以將fd置爲-1。
Tip:要確保提供給bufferevent_socket_new
的socket是非阻塞模式。Libevent提供了便於使用的evutil_make_socket_nonblocking
來設置非阻塞模式。
bufferevent_socket_new
成功時返回一個bufferevent
,失敗時返回NULL
。
5.2 在基於socket的bufferevent上發送連接
如果bufferevent上的socket還沒有建立連接, 我們可以發送一個新的連接:
int bufferevent_socket_connect(struct bufferevent *bev,
struct sockaddr *address, int addrlen);
address和addrlen參數類似於標準的connect函數。如果該bufferevent尚未設置socket,則調用該函數爲該bufferevent會分配一個新的流類型的socket,並且置其爲非阻塞的。
如果bufferevent已經設置了一個socket,則調用數bufferevent_socket_connect
會告知Libevent該socket尚未建立連接,在建立連接成功之前,不應該在其上進行讀寫操作。
在建立連接成功之前,向輸出緩衝區添加數據是可以的。
該函數如果在建鏈成功時,返回0,如果發生錯誤,則返回-1.
Example
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <sys/socket.h>
#include <string.h>
void eventcb(struct bufferevent *bev, short events, void *ptr)
{
if (events & BEV_EVENT_CONNECTED) {
/* We're connected to 127.0.0.1:8080. Ordinarily we'd do
something here, like start reading or writing. */
} else if (events & BEV_EVENT_ERROR) {
/* An error occured while connecting. */
}
}
int main_loop(void)
{
struct event_base *base;
struct bufferevent *bev;
struct sockaddr_in sin;
base = event_base_new();
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
sin.sin_port = htons(8080); /* Port 8080 */
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, NULL, NULL, eventcb, NULL);
if (bufferevent_socket_connect(bev,
(struct sockaddr *)&sin, sizeof(sin)) < 0) {
/* Error starting connection */
bufferevent_free(bev);
return -1;
}
event_base_dispatch(base);
return 0;
}
bufferevent_base_connect()
函數是在Libevent-2.0.2-alpha版本引入的,在這之前,需要手動調用connect()
函數,並且當連接建立的時候,bufferevent會將其作爲寫事件進行報告。
注意:如果使用bufferevent_socket_connect
進行建鏈的話,會得到BEV_EVENT_CONNECTED
事件。如果自己手動調用connect()
,則會得到write
事件。
如果在手動調用connect()
的情況下,仍然想在建鏈成功的時候得到BEV_EVENT_CONNECTED
事件,可以在connect()
返回-1,並且errno
爲EAGAIN
或EINPROGRESS
之後,調用bufferevent_socket_connect(bev, NULL, 0)
函數。
5.3 通過hostname發射連接
經常性的,你可能希望將解析主機名和建立連接操作合成一個單獨的操作,這時可以使用下面的接口:
int bufferevent_socket_connect_hostname(struct bufferevent *bev,
struct evdns_base *dns_base, int family, const char *hostname,
int port);
int bufferevent_socket_get_dns_error(struct bufferevent *bev);
該函數解析DNS名字hostname
,查找family
類型的地址(family
的類型可以是AF_INET
, AF_INET6
和AF_UNSPEC
)。如果解析主機名失敗,會以error event調用回調函數。如果成功了,則會像 bufferevent_connect
一樣,接着進行建立連接。
dns_base
參數是可選的。如果該參數爲空,則Libevent會一直阻塞,等待主機名解析完成,一般情況下不會這麼做。如果提供了該參數,則Libevent使用它進行異步的主機名解析。參考Chapter 9更多內容。
類似於bufferevent_socket_connect,該函數會告知Libevent,bufferevent上已存在的socket尚未建立連接,在解析完成,並且連接建立成功之前,不應該在其上進行讀寫操作。
如果發生了錯誤,有可能是DNS解析錯誤。可以通過調用函數bufferevent_socket_get_dns_error
函數得到最近發生的錯誤信息。如果該函數返回的錯誤碼爲0,則表明沒有檢查到任何DNS錯誤。
Example: Trivial HTTP v0 client.
/* Don't actually copy this code: it is a poor way to implement an
HTTP client. Have a look at evhttp instead.
*/
#include <event2/dns.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/util.h>
#include <event2/event.h>
#include <stdio.h>
void readcb(struct bufferevent *bev, void *ptr)
{
char buf[1024];
int n;
struct evbuffer *input = bufferevent_get_input(bev);
while ((n = evbuffer_remove(input, buf, sizeof(buf))) > 0) {
fwrite(buf, 1, n, stdout);
}
}
void eventcb(struct bufferevent *bev, short events, void *ptr)
{
if (events & BEV_EVENT_CONNECTED) {
printf("Connect okay.\n");
} else if (events & (BEV_EVENT_ERROR|BEV_EVENT_EOF)) {
struct event_base *base = ptr;
if (events & BEV_EVENT_ERROR) {
int err = bufferevent_socket_get_dns_error(bev);
if (err)
printf("DNS error: %s\n", evutil_gai_strerror(err));
}
printf("Closing\n");
bufferevent_free(bev);
event_base_loopexit(base, NULL);
}
}
int main(int argc, char **argv)
{
struct event_base *base;
struct evdns_base *dns_base;
struct bufferevent *bev;
if (argc != 3) {
printf("Trivial HTTP 0.x client\n"
"Syntax: %s [hostname] [resource]\n"
"Example: %s www.google.com /\n",argv[0],argv[0]);
return 1;
}
base = event_base_new();
dns_base = evdns_base_new(base, 1);
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, readcb, NULL, eventcb, base);
bufferevent_enable(bev, EV_READ|EV_WRITE);
evbuffer_add_printf(bufferevent_get_output(bev), "GET %s\r\n", argv[2]);
bufferevent_socket_connect_hostname(
bev, dns_base, AF_UNSPEC, argv[1], 80);
event_base_dispatch(base);
return 0;
}
6. 通用的bufferevent操作
本節介紹的bufferevent能夠在不同的bufferevent實現上工作
6.1 釋放bufferevent
void bufferevent_free(struct bufferevent *bev);
該函數釋放bufferevent
。bufferevent
在內部具有引用計數,所以即使當釋放bufferevent
時,如果bufferevent
還有未決的延遲迴調,那該bufferevent在該回調完成之前也不會刪除。
bufferevent_free
函數會盡快釋放bufferevent
。然而,如果bufferevent
的輸出緩衝區中尚有殘留數據要寫,該函數也不會在釋放bufferevent
之前對緩衝區進行flush
。
如果設置了BEV_OPT_CLOSE_ON_FREE
標誌,並且該bufferevent
有socket
或者使用了其他底層bufferevent作爲其傳輸系統,則在釋放該bufferevent
時,會關閉對應的傳輸系統。
這些函數在Libevent 0.8.中引入。
6.2 回調函數、水位線、使能操作
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
typedef void (*bufferevent_event_cb)(struct bufferevent *bev,
short events, void *ctx);
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
void bufferevent_getcb(struct bufferevent *bufev,
bufferevent_data_cb *readcb_ptr,
bufferevent_data_cb *writecb_ptr,
bufferevent_event_cb *eventcb_ptr,
void **cbarg_ptr);
函數bufferevent_setcb()
可以改變bufferevent的回調函數。readcb
、writecb
和eventcb
函數 分別在 有足夠數據可讀、有足夠數據可寫、有event時間發生時被調用。 這些回調函數的第一個參數就是發生了events的bufferevent; 而最後一個參數則是由用戶提供的bufferevent_setcb
的cbarg
參數;用戶可以通過event回調函數的events
參數是event標誌的位掩碼:參考上面的“回調函數和水位線”一節
我們可以通過向bufferevent_setcb()
傳遞NULL
參數來禁用一些回調函數。需要注意的是,cbarg
參數是針對所有回調函數的。
我們可以通過向bufferevent_getcb
傳遞指針來獲取當前的回調函數。該函數會將*readcb_ptr
設置爲當前的讀回調函數,*writecb_ptr
設置爲寫回調函數,*eventcb_ptr
設置爲當前的event回調函數,並且*cbarg_ptr
設置爲當前回調函數的參數。對於被設置爲NULL
的指針,則會忽略它。
函數bufferevent_setcb()
在Libevent 1.4.4中引入. bufferevent_data_cb
和 bufferevent_event_cb
則是在 Libevent 2.0.2-alpha中新引入的. The bufferevent_getcb()
則是在 2.1.1-alpha 中新加入的。
void bufferevent_enable(struct bufferevent *bufev, short events);
void bufferevent_disable(struct bufferevent *bufev, short events);
short bufferevent_get_enabled(struct bufferevent *bufev);
我們可以在bufferevent上enable或者disable事件 EV_READ
, EV_WRITE
, or EV_READ|EV_WRITE
。 當disable了讀寫操作,則bufferevent不會讀取或寫入數據。
當output buffer 爲空時,沒必要disable寫動作,bufferevent會自動禁止掉寫動作。
類似的,當輸入緩衝區達到它的高水位線的時候,沒必要禁止讀操作:bufferevent會自動停止讀操作,而且在有空間讀取的時候,又重新開啓讀操作。
默認情況下,新創建的bufferevent會enable寫操作,而禁止讀操作。
可以通過bufferevent_get_enabled()
來獲取當前bufferevent中有那些events被enable了。
bufferevent_get_enabled()
是在 2.0.3-alpha中引入的, 而其它函數在Libevent 0.8就被引進了。
void bufferevent_setwatermark(struct bufferevent *bufev, short events,
size_t lowmark, size_t highmark);
bufferevent_setwatermark
調整一個bufferevent的讀水位線,或寫水位線,或兩者一起調整。如果在events參數中設置了EV_READ
參數,則會調整讀水位線,如果設置了EV_WRITE
標誌,則會調整寫水位線。將高水位線標誌置爲0,表示“無限制”。
該函數在Libevent 1.4.4.中引入 。
Example
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/util.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
struct info {
const char *name;
size_t total_drained;
};
void read_callback(struct bufferevent *bev, void *ctx)
{
struct info *inf = ctx;
struct evbuffer *input = bufferevent_get_input(bev);
size_t len = evbuffer_get_length(input);
if (len) {
inf->total_drained += len;
evbuffer_drain(input, len);
printf("Drained %lu bytes from %s\n",
(unsigned long) len, inf->name);
}
}
void event_callback(struct bufferevent *bev, short events, void *ctx)
{
struct info *inf = ctx;
struct evbuffer *input = bufferevent_get_input(bev);
int finished = 0;
if (events & BEV_EVENT_EOF) {
size_t len = evbuffer_get_length(input);
printf("Got a close from %s. We drained %lu bytes from it, "
"and have %lu left.\n", inf->name,
(unsigned long)inf->total_drained, (unsigned long)len);
finished = 1;
}
if (events & BEV_EVENT_ERROR) {
printf("Got an error from %s: %s\n",
inf->name, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
finished = 1;
}
if (finished) {
free(ctx);
bufferevent_free(bev);
}
}
struct bufferevent *setup_bufferevent(void)
{
struct bufferevent *b1 = NULL;
struct info *info1;
info1 = malloc(sizeof(struct info));
info1->name = "buffer 1";
info1->total_drained = 0;
/* ... Here we should set up the bufferevent and make sure it gets
connected... */
/* Trigger the read callback only whenever there is at least 128 bytes
of data in the buffer. */
bufferevent_setwatermark(b1, EV_READ, 128, 0);
bufferevent_setcb(b1, read_callback, NULL, event_callback, info1);
bufferevent_enable(b1, EV_READ); /* Start reading. */
return b1;
}
7. bufferevent中的數據操作
如果不能操作讀寫的數據,則從網絡中讀寫數據沒有任何意義。bufferevent提供函數可以操作讀寫的數據。
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);
這兩個函數功能非常強大,它們分別反悔bufferevent的input buffer
和 output buffer
. 在evbuffer類型上所能進行的所有操作,可以參考下一章。
注意,應用程序只能從input buffer中移走(而不是添加)數據,而且只能向output buffer添加(而不是移走)數據。
如果bufferevent上的寫操作因爲數據太少而停滯(或者讀操作因爲數據太多而停滯),則向output buffer中添加數據(或者從input buffer中移走數據)可以自動重啓寫(讀)操作。
int bufferevent_write(struct bufferevent *bufev, const void*data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer*buf);
這些函數向bufferevent的output buffer中添加數據。調用bufferevent_write
函數添加data中的size個字節的數據到輸出緩衝區的末尾。調用 bufferevent_write_buffer
函數則將buf中所有數據都移動到output buffer的末尾。這些函數返回0表示成功,返回-1表示發生了錯誤。
這兩個函數在Libevent 0.8就引入了。
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_read_buffer(struct bufferevent *bufev,
struct evbuffer *buf);
這兩個函數從bufferevent的input buffer中移走數據。bufferevent_read
函數從input buffer中移動size個字節到data
中。它返回實際移動的字節數。bufferevent_read_buffer
函數則移動輸入緩衝區中的所有數據到buf
中,該函數返回0表示成功,返回-1表示失敗。
注意bufferevent_read
函數中,data緩衝區必須有足夠的空間保存size個字節。
bufferevent_read()
在 libevent 0.8中引入,bufferevent_read_buffer
在Libevent 2.0.1-alpha. 中才引入。
* Example*
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <ctype.h>
void
read_callback_uppercase(struct bufferevent *bev, void *ctx)
{
/* This callback removes the data from bev's input buffer 128
bytes at a time, uppercases it, and starts sending it
back.
(Watch out! In practice, you shouldn't use toupper to implement
a network protocol, unless you know for a fact that the current
locale is the one you want to be using.)
*/
char tmp[128];
size_t n;
int i;
while (1) {
n = bufferevent_read(bev, tmp, sizeof(tmp));
if (n <= 0)
break; /* No more data. */
for (i=0; i<n; ++i)
tmp[i] = toupper(tmp[i]);
bufferevent_write(bev, tmp, n);
}
}
struct proxy_info {
struct bufferevent *other_bev;
};
void
read_callback_proxy(struct bufferevent *bev, void *ctx)
{
/* You might use a function like this if you're implementing
a simple proxy: it will take data from one connection (on
bev), and write it to another, copying as little as
possible. */
struct proxy_info *inf = ctx;
bufferevent_read_buffer(bev,
bufferevent_get_output(inf->other_bev));
}
struct count {
unsigned long last_fib[2];
};
void
write_callback_fibonacci(struct bufferevent *bev, void *ctx)
{
/* Here's a callback that adds some Fibonacci numbers to the
output buffer of bev. It stops once we have added 1k of
data; once this data is drained, we'll add more. */
struct count *c = ctx;
struct evbuffer *tmp = evbuffer_new();
while (evbuffer_get_length(tmp) < 1024) {
unsigned long next = c->last_fib[0] + c->last_fib[1];
c->last_fib[0] = c->last_fib[1];
c->last_fib[1] = next;
evbuffer_add_printf(tmp, "%lu", next);
}
/* Now we add the whole contents of tmp to bev. */
bufferevent_write_buffer(bev, tmp);
/* We don't need tmp any longer. */
evbuffer_free(tmp);
}
8. 讀寫 超時
同其他events一樣,可以設置timeout時間,當bufferevent在timeout時間消逝後還沒有成功的讀或寫任何數據,則可以觸發某個超時事件。
void bufferevent_set_timeouts(struct bufferevent *bufev,
const struct timeval *timeout_read, const struct timeval *timeout_write);
將timeout
設置爲NULL
,意味着移除超時時間;然而在Libevent 2.1.2-alpha版本之前,這種方式並非在所有event類型上都有效。(對於較老版本的,取消超時時間的有效方法是,可以將超時時間設置爲好幾天,並且/或者使eventcb
函數忽略BEV_TIMEOUT
事件)。
當bufferevent試圖讀取數據時,等待了timeout_read
秒還沒有數據,則讀超時事件就會觸發。當bufferevent試圖寫數據時,至少等待了timeout_write
秒,則寫超時事件就會觸發。
注意,只有在bufferevent讀或寫的時候,纔會對超時時間進行計時。換句話說,如果bufferevent上禁止了讀操作,或者當輸入緩衝區滿(達到高水位線)時,則讀超時時間不會使能。類似的,如果寫操作未被使能,或者沒有數據可寫,則寫超時時間也會被禁止。
當讀或寫超時發生的時候,則bufferevent上相應的讀寫操作就會被禁止。相應的event回調函數就會以BEV_EVENT_TIMEOUT|BEV_EVENT_READING
或BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING
進行調用。
9. 在bufferevent上進行flush
int bufferevent_flush(struct bufferevent *bufev,
short iotype, enum bufferevent_flush_mode state);
對一個bufferevent進行flush,可以強制bufferevent儘可能多的從底層傳輸系統上讀取或者寫入數據,而忽略其他可能阻止寫入的限制條件。該函數的細節依賴於不同類型的bufferevent。
參數iotype
可以是EV_READ
, EV_WRITE
, EV_READ|EV_WRITE
指明處理讀操作、寫操作,還是兩者都處理. 參數state
可以是BEV_NORMAL
, BEV_FLUSH
,BEV_FINISHED
之一. BEV_FINISHED
指明應該告訴另一端已經沒有數據可以發送了;BEV_NORMAL
和 BEV_FLUSH
取決於bufferevent的類型。
bufferevent_flush
函數返回-1表示失敗,返回0表示沒有任何數據被flush,返回1表示由數據被flush。
目前(Libevent2.0.5-beta),bufferevent_flush
函數只在某些bufferevent類型上進行了實現,特別是基於socket
的bufferevent並不支持該操作。
10. 特定類型的bufferevent函數
這些函數並非在所有bufferevent類型上都能使用
int bufferevent_priority_set(struct bufferevent *bufev, int pri);
int bufferevent_get_priority(struct bufferevent *bufev);
該函數將實現bufev的events的優先級調整爲pri,關於優先級更多的信息,可以參考event_priority_set函數。
返回0表示成功,返回-1表示失敗,該函數只能工作在基於socket的bufferevent上。
函數bufferevent_priority_set()
在 Libevent 1.0 中引入; bufferevent_get_priority()
直到 2.1.2-alpha.纔出現。
int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
evutil_socket_t bufferevent_getfd(struct bufferevent *bufev);
該函數設置或者返回一個基於fd
的event
的文件描述符。只有基於socket
的bufferevent
支持setfd
操作。這些函數返回-1表示失敗,setfd
返回0表示成功。
函數bufferevent_setfd()
在 Libevent 1.4.4
中引入。 bufferevent_getfd()
在 Libevent 2.0.2-alpha 中引入。
struct event_base *bufferevent_get_base(struct bufferevent *bev);
該函數返回bufferevent
的event_base
. 該函數在2.0.9-rc中引入 .
struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev);
如果bufferevent
作爲其他bufferevent的底層傳輸系統的話,則該函數返回該底層bufferevent。參考過濾型bufferevent,獲得關於這種情況的更多信息。
這個函數在Libevent 2.0.2-alpha.中引進。
11. 對bufferevent手動加鎖或者解鎖
類似於evbuffers
,有時希望保證在bufferevent上的一系列操作是原子性的。Libevent提供了可以手動加鎖和解鎖bufferevent的函數。
void bufferevent_lock(struct bufferevent *bufev);
void bufferevent_unlock(struct bufferevent *bufev);
注意,如果一個bufferevent在創建時沒有指定BEV_OPT_THREADSAFE
標誌,或者Libevent的線程支持功能沒有激活,則加鎖一個bufferevent沒有作用。
通過該函數對bufferevent
進行加鎖的同時,也會加鎖evbuffers
。這些函數都是遞歸的:對一個已經加鎖的bufferevent再次加鎖是安全的。當然,對於每次鎖定都必須進行一次解鎖。
12. 過時的bufferevent函數
在Libevent1.4和Libevent2.0之間,bufferevent的後臺代碼經歷了大量的修改。在老接口中,訪問·bufferevent·結構的內部是很正常的,而且,還會經常使用依賴於這種訪問方式的宏。
讓問題變得更加複雜的是,老的代碼中,有時會使用以evbuffer
爲前綴的bufferevent
函數。
下表是一個Libevent2.0之前版本的函數概述
Current name | Old name |
---|---|
bufferevent_data_cb | evbuffercb |
bufferevent_event_cb | everrorcb |
BEV_EVENT_READING | EVBUFFER_READ |
BEV_EVENT_WRITE | EVBUFFER_WRITE |
BEV_EVENT_EOF | EVBUFFER_EOF |
BEV_EVENT_ERROR | EVBUFFER_ERROR |
BEV_EVENT_TIMEOUT | EVBUFFER_TIMEOUT |
bufferevent_get_input(b) | EVBUFFER_INPUT(b) |
bufferevent_get_output(b) | EVBUFFER_OUTPUT(b) |
老版本的函數定義在event.h中,而不是event2/bufferevent.h
中。
如果仍然需要訪問bufferevent內部結構中的普通部分,可以包含event2/bufferevent_struct.h
。我們不建議這樣做:bufferevent結構的內容在不同的Libevent版本中經常會發生改變。如果包含了event2/bufferevent_compat.h
文件,可以使用本節介紹的宏和名字。
老版本的代碼中,創建bufferevent結構的接口有所不同:
struct bufferevent *bufferevent_new(evutil_socket_t fd,
evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev);
bufferevent_new()
函數僅能創建基於socket
的bufferevent
,而且還是在不推薦使用的“當前”event_base上。可以通過調用bufferevent_base_set
來調整一個socket bufferevent
的event_base
。
老版本的代碼設置超時的秒數,而不是設置結構體timeval
:
void bufferevent_settimeout(struct bufferevent *bufev,
int timeout_read, int timeout_write);
最後注意,在Libevent2.0之前的版本中,底層的evbuffer
實現是非常低效的,因此使用bufferevent構建高性能的程序是不太可能的。