事件建立和触发机制如下图所示:
几个重要的结构体
struct evconnlistener_ops
{
int(*enable)(struct evconnlistener *);
int(*disable)(struct evconnlistener *);
void(*destroy)(struct evconnlistener *);
void(*shutdown)(struct evconnlistener *);
evutil_socket_t (*getfd)(struct evconnlistener *);
structevent_base *(*getbase)(struct evconnlistener*);
};
该结构体内保存了常用方法的函数指针。
struct evconnlistener
{
const struct evconnlistener_ops *ops;
void*lock;
evconnlistener_cb cb;
evconnlistener_errorcb errorcb;
void*user_data;
unsignedflags;
shortrefcnt;
unsignedenabled : 1;
};
该结构体内是监听结构体,保存了回调函数指针,用户数据等常用参数。
struct evconnlistener_event
{
structevconnlistener base;
struct event listener;
}
该结构体是对监听结构体的再次封装,将监听结构体和事件结构体关联起来。
在主程序中一般调用如下方法得到监听结构体,并设置回调函数。
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in sin;
base =event_base_new();
memset(&sin, 0, sizeof(sin));
sin.sin_family =AF_INET;
sin.sin_port =htons(PORT);
listener =evconnlistener_new_bind(base, listener_cb, (void*)base,LEV_OPT_REUSEABLE, -1,(structsockaddr*)&sin,sizeof(sin));
struct evconnlistener *
evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,void *ptr, unsignedflags, int backlog, conststruct sockaddr *sa,intsocklen)
{
//建立套接字,设置套接字参数,端口绑定
//建立实际的evconnlistener结构体
listener = evconnlistener_new(base, cb, ptr,flags, backlog, fd);
返回监听结构体
}
struct evconnlistener *evconnlistener_new(struct event_base *base,evconnlistener_cb cb, void *ptr, unsignedflags, int backlog,evutil_socket_t fd)
{
//在建立好的套接字上启动监听
//创建evconnlistener_event结构体,在该结构体内部的evconnlistener结构体上设置回调函数和相关参数。
//设置evconnlistener_event结构体内的event结构体(lev->listener)属性。
event_assign(&lev->listener,base, fd, EV_READ|EV_PERSIST,listener_read_cb, lev);
//将该监听事件增加到监听队列中
evconnlistener_enable(&lev->base);
}
int event_assign(structevent *ev, structevent_base *base, evutil_socket_t fd, shortevents, void (*callback)(evutil_socket_t, short, void *), void *arg)
{
//设置回调函数,此回调函数非最开始设置的回调函数,套接字、事件类型等参数
}
int evconnlistener_enable(struct evconnlistener *lev)
{
调用evconnlistener_ops结构体体的enable静态方法,实际是下面的方法
}
static int event_listener_enable(struct evconnlistener *lev)
{
首先进行地址转换,得到evconnlistener_event地址指针
structevconnlistener_event *lev_e =EVUTIL_UPCAST(lev, structevconnlistener_event, base);
//事先事件增加,内部调用event_add_internal
returnevent_add(&lev_e->listener, NULL);
}
static inline int event_add_internal(structevent *ev, conststruct timeval *tv,inttv_is_absolute)
{
//将事件增加到哈希表中,并将该事件的fd增加到底层监听数组中。
res = evmap_io_add(base,ev->ev_fd, ev);
//将该事件增加到base的 eventqueue列表中
event_queue_insert(base, ev,EVLIST_INSERTED);
}
到此处监听事件已经增加完毕,底层开始监听:
event_base_dispatch(base);
在下面的函数内进行事件监听
win32_dispatch(struct event_base *base, structtimeval *tv)
{
//监听事件
res =select(fd_count,
(structfd_set*)win32op->readset_out,
(structfd_set*)win32op->writeset_out,
(structfd_set*)win32op->exset_out, tv);
//激活事件
evmap_io_active(base,s, EV_READ);
}
evmap_io_active(struct event_base *base, evutil_socket_t fd, short events)
{
//得到该fd下的evmap_io对象
//对所有的fd下的事件进行激活
TAILQ_FOREACH(ev, &ctx->events,ev_io_next)
{
if(ev->ev_events & events)
{
event_active_nolock(ev,ev->ev_events & events, 1);
}
}
}
void event_active_nolock(structevent *ev, intres, short ncalls)
{
//经过一系列判断后是调用如下函数,将该事件增加到event_base的激活队列中
event_queue_insert(base, ev, EVLIST_ACTIVE);
}
int event_base_loop(structevent_base *base, int flags)
{
//处理激活事件,该函数内部调用event_process_active_single_queue(base, activeq);
event_process_active(base);
}
static int
event_process_active_single_queue(struct event_base *base, structevent_list *activeq)
{
//从激活队列里删除
event_queue_remove(base, ev, EVLIST_ACTIVE);
//在如下函数内调用listener_read_cb(evutil_socket_tfd, short what, void*p)回调函数
event_persist_closure(base, ev);
}
static void listener_read_cb(evutil_socket_tfd, short what, void*p)
{
//创建通讯套接字
evutil_socket_t new_fd = accept(fd, (struct sockaddr*)&ss, &socklen);
调用我们自己的监听回调函数
}
到此处一个完整的监听过程就结束了
static voidlistener_cb(struct evconnlistener *listener,evutil_socket_t fd,struct sockaddr *sa, int socklen, void*user_data)