本文地址:https://segmentfault.com/a/1190000005601925
Libevent的輔助函式和資料型別
標頭檔案是<event2/util.h>
。以下只列出我自己會用到的部分。
基本型別
evutil_socket_t
Socket的抽象。除了Windows之外,其他系統都是一個int型別。如果考慮Windows的相容性的話,建議用這個型別。
標準整型
以下是幾種資料長度的定義
----------------------------------------------------------
Type 位寬 符號數 最大值 最小值
----------------------------------------------------------
ev_uint64_t 64 x EV_UINT64_MAX 0
ev_int64_t 64 √ EV_INT64_MAX EV_INT64_MIN
ev_uint32_t 32 x EV_UINT32_MAX 0
ev_int32_t 32 √ EV_INT32_MAX EV_INT32_MIN
ev_uint16_t 16 x EV_UINT16_MAX 0
ev_int16_t 16 √ EV_INT16_MAX EV_INT16_MIN
ev_uint8_t 8 x EV_UINT8_MAX 0
ev_int8_t 8 √ EV_INT8_MAX EV_INT8_MIN
其他一些型別ev_ssize_t
ev_off_t
適配函式的巨集
#define evutili_timer_add(tvp, uvp, vvp)
#define evutili_timer_sub(tvp, uvp, vvp)
計算timeval資料加減的巨集,vvp = tvp +/- uvp。注意三者都要使用指標
#define evutil_timerclear(tvp)
#define evutil_timerisset(tvp)
將timeval清零,或者判斷是否被清零
#define evutil_timercmp(tvp, uvp, cmp)
判斷timeval的先後,其中cmp是比較富豪,比如==
, <=
, >=
, <
, >
, !=
int evutil_gettimeofday (struct timeval *tv, struct timezone *tz);
Socket相關的函式
#define evutil_socket_geterror (sock)
#define evutil_socket_error_to_string (errcode)
獲得指定socket的error code,以及轉為可讀的string
int evutil_make_socket_nonblocking (evutil_sopcket_t sock);
將一個socket非阻塞。
字串操作
ev_int64_t evutil_strtoll (const char *s, char **endptr, int base);
int evutil_snprintf (char *but, size_t buflen, const char *format, ...);
int evutil_vsnprintf (char *bug, size_t buflen, const char *format, va_list ap);
資料結構體
#define evutil_offsetof (type, field)
Bufferevent:概念和基本知識
傳統的libevent使用方法:
- 當需要放資料的時候,存入資料到buffer
- 等待socket可寫
- 儘量向socket中寫更多的data
- 如果還有data未寫入,則再等待socket可寫
使用標頭檔案<event2/bufferevent.h>
可以使用bufferevent,節省read/write呼叫,只需要將資料放入/取出一個buffer即可
目前bufferevent只支援TCP,未來可能支援UDP
每個bufferevent有一個read buffer和一個write buffer,都是struct evbuffer
。這個後文再講。
回撥和bufferevent
Bufferevent使用叫做watermarks
(水位線)的東西來定義回撥函式的呼叫時機。有以下幾個watermarks:
Read low-water mark
:當read buffer的量大於等於這麼多時,呼叫callback。預設是0,即一有資料就回撥。
Read high-water mark
:當read buffer的量大於等於這麼多時,停止read,直到buffer裡面的資料低於這個值為止,重新開始read。預設是無限。
Write low-water mark
:當write buffer的量小於等於這麼多時,呼叫回撥。預設是0
Write high-water mark
:bufferevent未直接使用這個值。參見後文
Bufferevent也有錯誤回撥和事件回撥,用於告知一些非資料時間和錯誤。如下:
BEV_EVENT_READING
BEV_EVENT_WRITING
BEV_EVENT_ERROR
:操作發生錯誤。需要呼叫EVUTIL_SOCKET_ERROR()
來判斷出現了什麼錯誤
BEV_EVENT_TIMEOUT
BEV_EVENT_EOF
BEV_EVENT_CONNECTED
:請求連線已經完成
延遲迴調
一般情況下,bufferevent的callback時立刻呼叫的。但是如果呼叫關係很複雜的話可能會出bug,這個時候可以將bufferevent設定為延遲的(defered
),這樣會使得回撥函式放在event loop中被執行,單執行緒。
Bufferevent的選項
BEV_OPT_CLOSE_ON_FREE
:當bufferevent釋放時,關閉底層傳輸BEV_OPT_THREADSAFE
:為bufferevent使用lockBEV_OPT_DEFER_CALLBACKS
:將callback設為延遲的BEV_OPT_UNLOCK_CALLBACKS
:預設情況下如果有THREADSAFE標誌,呼叫callback時會加鎖。使用這個標誌是的即便有THREADSAFE標誌,呼叫callback也不加鎖
使用基於socket的bufferevent
struct bufferevent *bufferevent_socket_new (
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options);
這裡的fd可以不指定,此時fd的引數是-1。如果指定了fd,這個fd必須是已經nonblock的。
int bufferevent_socket_connect (struct bufferevent *bev,
struct sockaddr *address,
int addrlen);
這是對connect()
的封裝。如果bev的fd是-1,那麼會自動呼叫socket()
,並且設定nonblock。,隨後再非同步呼叫connect()
;如果fd已經指定了,那麼只是告訴bev去做connect()
操作。
正常情況下,這會引起BEV_EVENT_CONNECTED
回撥
int bufferevent_socket_connect_hostname (
struct bufferevent *bev,
struct event_base *dns_base,
int family,
const char *hostname,
int port);
這是connect()
封裝的另一個版本,但是目標改為hostname。這會導致bufferevent自動去解析DNS。其中family
可選以下值:AF_INET
, AF_INET6
, AF_UNSPEC
。
dns_base引數可選。如果是NULL,那麼bufferevent會一直阻塞直到DNS解析完成——當然不推薦這麼做。如果帶了引數,則libevent會非同步處理DNS請求。
剩下的工作與上面的connect封裝相同。
int bufferevent_socket_get_dns_error (struct bufferevent *bev);
通用的bufferevent操作
void bufferevent_free (struct bufferevent *bev);
釋放bfferevent。如果callback是defered的,那麼bufferevent會等到callback返回之後才釋放。
如果指定了BEV_OPT_CLOSE_ON_FREE,那麼socket也會被close掉。
typedef void (*bufferevent_data_cb) (struct bufferevent *bev, void *ctx);
typedef void (*bufferevent_event_cb) (struct bufferevent *bev, short events, void *ctx);
void buffevent_setcb (struct buffevent *bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevent_event_cb eventcb,
void *cbarg);
void bufferevent_get_cb (struct buffevent *bufev,
bufferevent_data_cb *readcb_ptr,
bufferevent_data_cb *writecb_ptr,
bufferevent_event_cb *eventcb_ptr,
void **cbarg_ptr);
設定。獲取bufferevent的callback。如果不想使用某個callback,則傳入NULL。
void bufferevent_enable (struct bufferevent *bufev, short events);
void bufferevent_disable (struct bufferevent *bufev, short events);
short bufferevent_getenabled (struct bufferevent *bufev);
使能/禁用指定的的callback。預設情況下,剛初始化的bufferevent,write使能,而read禁止。
void bufferevent_setwatermark (struct buffevent *bev,
short events,
size_t lowmark,
size_t highmark);
設定watermark。對於high-watermark,0表示無限。
struct evbuffer *bufferevent_get_input (struct bufferevent *bev);
struct evbuffer *bufferevent_get_output(struct bufferevent *bev);
獲取到bufferevent中對應的read/write buffer。
int bufferevent_write (struct bufferevent *ev, const void *data, size_t size);
int bufferevent_write_buffer (struct bufferevent *bev, struct evbuffer *buf);
函式一:直接向bufferevent附加資料
函式二:將evbuffer的全部內容附加到bufferevent中並晴空evbuffer
size_t bufferevent_read (struct bufferevent *bev, void *data, size_t size);
int bufferevent_read_buffer (struct bufferevent *bev, struct evbuffer *buf);
函式一:直接從bufferevent中讀出資料,返回資料長度
函式二:將bufferevent中的全部資料抽取到evbuffer中
void bufferevent_set_timeouts (struct bufferevent *bev,
const struct timeval *timeout_read,
const struct timeval *timeout_write);
設定timeout,使得當一段時間沒有資料時,觸發回撥函式。此時的實踐中會包含 BEV_EVENT_TIMEOUT
位
int bufferevent_flush (struct bufferevent *bufev,
short iotype,
enum bufferevent_flush_mode state);
強制讀/寫儘可能多的資料。這個函式目前對socket沒有作用。
型別特定的bufferevent函式
以下幾個函式的含義正如字面意思,就不特別說明了
int bufferevent_priority_set (struct bufferevent *bev, int pri);
int bufferevent_get_priority (struct bufferevent *bev);
int bufferevent_setfd (struct bufferevent *bev, evutil_socket_t fd);
evutil_socket_t bufferevent_getfd (struct bufferevent *bev);
struct event_base *bufferevent_get_base (struct bufferevent *bev);
struct bufferevent *bufferevent_get_underlying (struct bufferevent *bev);
void bufferevent_lock (struct bufferevent *bev);
void buyfferevent_unlock(struct bufferevent *bev);
Bufferevents:高階主題
這裡講了很多bufferevent的高階功能。本文章只是列出其中會使用到的部分
限制每次read/write的長度
int bufferevent_set_max_single_read (struct bufferevent *bev, size_t size);
int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size);
同時也有相對應的get函式
速率(頻寬)限制
#define EV_RATE_LIMIT_MAX EV_SSIZE_MAX
struct ev_token_bucket_cfg;
struct ev_token_bucket_cfg *ev_token_bucket_cfg_new (
size_t read_rate, size_t read_burst,
size_t write_rate, size_t write_burst,
const struct timeval *tick_len);
void ev_token_bucket_cfg_free (struct ev_token_bucket_cfg *cfg);
int bufferevent_set_rate_limit (struct bufferevent *bev,
struct ev_token_bucket_cfg *cfg);
其中ev_token_bucket_cfg_new()
的前四個函式的單位均為bytes/tick
,而tick
的單位由tick_len
指定。如果tick_len
為NULL,那麼預設為1秒。
Bufferevents 和 SSL
這裡其實是一個很重要的內容,講的是如何在bufferevent中使用SSL。Libevent將SSL深度耦合了進來,使得你可以很方便地使用bufferevent來完成SSL通訊。
呃,缺點就是libevent和OpenSSL的缺點的集合。其實對於嵌入式開發來說,因為CPU是分立的,所以效能上的缺點並不明顯,最大的問題是佔用磁碟空間啊!Libevent的庫本身就不小,加上OpenSSL更是超大。我弄懂libevent的時候,我們的系統已經準備改用其他的非同步I/O和SSL庫,所以我也就不看了
另外吐槽一下:我們這麼多年了還是沒時間把libevent和OpenSSL完全替換的工作做完,在這期間我自己都把libev、libuv、PolarSSL(mbedTLS)、cyaSSL看了……
系列篇
Libevent官方文件學習筆記(1. libevent_core部分)
Libevent官方文件學習筆記(2. bufferevent部分)(本文)
Libevent官方文件學習筆記(3. evbuffer部分)