libevent 学习(一)-Hello world
Contents
记录下libevent的学习过程,主要是想理解下其设计思想。源码在Github上, 下载后编译安装即可。
|
|
简单的例子
理解原理还为时过早,先从如何使用入手, 创建一个简单timer的例子,每1秒打印Hello world!
,打印3次后退出:
|
|
编译:
|
|
主要流程:
首先需要调用event_base_new创建event_base,event_base里可以包括多个event,通过poll来监控哪一个被激活。
event_new() 创建了一个新的event,其函数原型如下:
1
struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn callback, void *callback_arg);
fd在上面代码中是
-1
,代表event只能timeout激活或者手动调用event_active()激活,在这里就是timeout
events参数是EV_PERSIST
, 代表除非调用event_del()
删除event, 否则它会一直在
callback 参数是hello_cb()
,主要打印输出
callback_arg 参数传入的是event_self_cbarg()
, 其作用是将event本身作为event_new()的参数,这是如何实现的? 在源码中event_self_cbarg()
定义为1 2 3 4 5
void * event_self_cbarg(void) { return &event_self_cbarg_ptr_; }
这个
event_self_cbarg_ptr_
指针是一个全局静态变量。1
static void *event_self_cbarg_ptr_ = NULL;
event_new()函数中为新的event分配了空间,没有看到
arg
指针的操作。1 2 3 4 5 6 7 8 9 10 11 12 13 14
struct event * event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg) { struct event *ev; ev = mm_malloc(sizeof(struct event)); if (ev == NULL) return (NULL); if (event_assign(ev, base, fd, events, cb, arg) < 0) { mm_free(ev); return (NULL); } return (ev); }
进入event_assign()中发现会判断
arg
指针是否是event_self_cbasrg_ptr_
,如果是就将arg
赋值为ev。1 2 3 4 5 6 7
event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg) { if (!base) base = current_base; if (arg == &event_self_cbarg_ptr_) arg = ev; ...
event_base
event_base 结构体:保存了libevent的loop过程中的状态以及所需的各种信息, 定义在event-internal.h中。 每个成员变量都有注释。
|
|
较为重要的是evsel
, 它是一个eventop
结构体,描述backend的行为,这个结构体类似C++中的基类。无论是poll或epoll只要实现接口,实现对应的函数就可以作为一个backend。
socket echo server
大概熟悉后,第二个例子稍微复杂点,使用libevent实现一个echo server。
|
|
|
|
运行效果大概是server会返回收到的字符,client会发送hello world
后退出。
server代码中,首先创建了一个socket, 设置为NONBLOCK,这是I/O多路复用的常规操作。
|
|
然后bind到指定的端口,并设置全连接队列长度为SOMAXCONN, 这个值可以通过cat /proc/sys/net/core/somaxconn
查看。
|
|
之后创建一个event_base,将server的socket fd放入event中监控, 我们只关心客户端发来的请求,所以只注册EV_READ。
|
|
同时,event_new最后的自定义参数, 把base指针传进去,这样可以在on_accept()
中把client的fd也add到同一个event_base中,一起监控起来。accept的client的fd也记得要设置成nonblock。