事件注冊與循環監聽
在libevent中為了監聽某種事件的發生,設置事件觸發后的回調函數,也就是說對該事件注冊到當前的IO模型中。
事件注冊
事件初始化
使用`event_new`函數來對事件進行初始化。
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);/* 回調函數 */ struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg); void event_free(struct event *event);
/* * base:event_base類型,event_base_new的返回值 * fd:監聽的fd,listen的fd * what:事件的類型及屬性 * cb:綁定的回調函數 * arg:給回調函數的參數 */
其中,事件類型及屬性如下:
#define EV_TIMEOUT 0x01 /*定時事件*/ #define EV_READ 0x02 /*I/O事件*/ #define EV_WRITE 0x04 /*I/O事件*/ #define EV_SIGNAL 0x08 /*信號*/ #define EV_PERSIST 0x10 /*永久事件 */ #define EV_ET 0x20 /*邊沿觸發*/
此外,還有一個函數`event_assgin`,它多了一個event參數:
int event_assign(struct event *event, struct event_base *base, evutil_socket_t fd, short what, void (*callback)(evutil_socket_t, short, void *), void *arg);
注冊事件
雖然已經初始化了事件,但是該事件並不會被觸發,原因在於我們並沒有激活該事件。
`event_add`函數提供了激活事件的功能。
int event_add(struct event *ev, const struct timeval *tv);
如果是一個(non-pending)未注冊`ev`,調用`event_add`函數會注冊該事件(變為pending狀態)。如果是一個(pending)注冊過的`ev`,調用該函數會在tv時間后重新注冊該事件。成功返回0,失敗返回-1。
例子
#include <event2/event.h> void cb_func(evutil_socket_t fd, short what, void *arg) { const char *data = arg; printf("Got an event on socket %d:%s%s%s%s [%s]", (int) fd, (what&EV_TIMEOUT) ? " timeout" : "", (what&EV_READ) ? " read" : "", (what&EV_WRITE) ? " write" : "", (what&EV_SIGNAL) ? " signal" : "", data); } void main_loop(evutil_socket_t fd1, evutil_socket_t fd2) { struct event *ev1, *ev2; struct timeval five_seconds = {5,0}; struct event_base *base = event_base_new(); /* The caller has already set up fd1, fd2 somehow, and make them nonblocking. */ /* 初始化事件 */ ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func,(char*)"Reading event"); ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func,(char*)"Writing event"); /* 注冊事件 */ event_add(ev1, &five_seconds); event_add(ev2, NULL); /* 循環監聽 */ event_base_dispatch(base); }
查找正在運行的事件
struct event *event_base_get_running_event(struct event_base *base);
設置事件執行一次
該函數和`event_base`類似,但是不支持`EV_SIGNAL or EV_PERSIST`。int event_base_once(struct event_base *, evutil_socket_t, short,void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);
激活事件
如果想自己激活某個事件,那么可以執行下面的函數:void event_active(struct event *ev, int what, short ncalls);
/* * 參數ev為要激活的事件 * what可以為EV_READ, EV_WRITE, and EV_TIMEOUT * ncalls為激活次數 */
其他函數
#define evtimer_new(base, callback, arg) \ event_new((base), -1, 0, (callback), (arg)) #define evtimer_add(ev, tv) \ event_add((ev),(tv)) #define evtimer_del(ev) \ event_del(ev) #define evtimer_pending(ev, tv_out) \ event_pending((ev), EV_TIMEOUT, (tv_out))
循環監聽
當已經擁有注冊了IO復用方法的`event_base`后,可以通過`event_loop`來監聽並接受IO事件。
`event_loop`會檢查所有IO復用方法的狀態,一旦啟動就會激活並使用這些IO復用方法。
如果不使用flag,可以條用下面這個函數,功能等同於`event_base_loop`,調用該函數會一直阻塞在這里,等待時間的觸發。
打開event loop
//@param: flags //等待IO事件 #define EVLOOP_ONCE 0x01 //非阻塞,直接返回 #define EVLOOP_NONBLOCK 0x02 //其他的線程中添加事件 #define EVLOOP_NO_EXIT_ON_EMPTY 0x04 int event_base_loop(struct event_base *base, int flags);
`event_loop`會檢查所有IO復用方法的狀態,一旦啟動就會激活並使用這些IO復用方法。
如果不使用flag,可以條用下面這個函數,功能等同於`event_base_loop`,調用該函數會一直阻塞在這里,等待時間的觸發。
int event_base_dispatch(struct event_base *base);
關閉event loop
//延遲tv時間后,停止event loop int event_base_loopexit(struct event_base *base, const struct timeval *tv); //立刻停止,等同於tv = NULL int event_base_loopbreak(struct event_base *base); //獲取退出方式 int event_base_got_exit(struct event_base *base); int event_base_got_break(struct event_base *base);
debug
這個函數可以把`event_base`中信息和狀態寫入文件中。void event_base_dump_events(struct event_base *base, FILE *f);
例子
#include <sys/types.h> #include <event2/event-config.h> #include <sys/stat.h> #ifndef WIN32 #include <sys/queue.h> #include <unistd.h> #endif #include <time.h> #ifdef _EVENT_HAVE_SYS_TIME_H #include <sys/time.h> #endif #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <event2/event.h> #include <event2/event_struct.h> #include <event2/util.h> struct timeval lasttime; int event_is_persistenet; static void timeout_cb(evutil_socket_t fd, short event, void *arg) { struct timeval newtime, difference; struct event *timeout = arg; double elapsed; evutil_gettimeofday(&newtime, NULL); evutil_timersub(&newtime, &lasttime, &difference); elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6); printf("timeout_cb called at %d: %.3f seconds elapsed.\n", (int)newtime.tv_sec, elapsed); lasttime = newtime; if (! event_is_persistent) { struct timeval tv; evutil_timerclear(&tv); tv.tv_sec = 2; event_add(timeout, &tv); } } int main(int argc, char **argv) { struct event timeout; struct timeval tv; struct event_base *base; int flags; if (argc == 2 && !strcmp(argv[1], "-p")) { event_is_persistent = 1; flags = EV_PERSIST; } else { event_is_persistent = 0; flags = 0; } /* Initalize the event library */ base = event_base_new(); /* Initalize one event */ event_assign(&timeout, base, -1, flags, timeout_cb, (void*)&timeout); evutil_timerclear(&tv); tv.tv_sec = 2; event_add(&timeout, &tv); evutil_gettimeofday(&lasttime, NULL); event_base_dispatch(base); return (0); }