1.libevent 库的下载和安装
1.打开网址:http://libevent.org/ 下载最新版即可

2.源码包拷贝到Linux系统,解压之后执行以下操作:
2.1 ./config (安装环境检测) 生成makefile 文件
2.2 make 执行Makefile文件,
2.3 sudo make install 将数据拷贝到对应的目录当中
3.sample 示例
在libevent库里有着许多的sample示例程序,如http_serve 、hello-world、event-read,如下所示。
1 2 3 4 5 6 | guoqi1@ubuntu:~/Desktop/libevent-2.1.11-stable/sample$ ls dns-example event-read-fifo.c hello-world.o http-connect.c http-server.c openssl_hostname_validation.c signal-test.o dns-example.c event-read-fifo.o hostcheck.c http-connect.o http-server.o openssl_hostname_validation.h time-test dns-example.o hello-world hostcheck.h https-client.c include.am signal-test time-test.c event-read-fifo hello-world.c http-connect http-server le-proxy.c signal-test.c time-test.o guoqi1@ubuntu:~/Desktop/libevent-2.1.11-stable/sample$ |
我们来简单的使用一下hello-world。当运行hello-world服务端之后,客户端链接之后就会成功的打印出我们耳熟能详的 hello world! 同时,服务端也会打印出 flushed answer以示回应。


2. libevent库的使用
2.1 没有带数据缓冲区的事件处理
当libevent库安装完成之后,句可以使用了,
使用套路:
1.创建一个事件处理框架
事件处理框架——event_base结构体的创建。
函数给我们分配了一块指向event_base类型的结构体的内存,内存当中封装的是poll,select,epoll,kequeue这些I/O接口,这个框架里面是没有任何具体的事件,需要接下event_new去创建一个事件,并添加进这个框架。
1 2 3 4 | struct event_base* event_base_new(void); /*函数给我们分配了一块指向event_base类型的结构体的内存,内存当中封装的是poll,select,epoll,kequeue这些I/O接口*/ struct event_base* base = NULL; base = event_base_new(); |
2.创建一个事件
event_new 初始化一个新的事件结构体,事件中包含了事件类型,回调函数等,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // short what ~~#define EV_TIMEOUT 0x01~~ // 废弃 #define EV_READ 0x02 //读事件 #define EV_WRITE 0x04 //写时间 #define EV_SIGNAL 0x08 //libevent封装了信号相关的操作 SIGNAL #define EV_PERSIST 0x10 // 持续触发模式 #define EV_ET 0x20 // 边沿模式模式 // 创建新事件 struct event *event_new( struct event_base *base, evutil_socket_t fd, - // 文件描述符 - int 类型 short what, //处理的事件类型,常用的包括读,写,信号操作等 event_callback_fn cb, // 回调处理动作 _cb void *arg //回调函数传参 ); // 事件的处理回调函数 typedef void (*event_callback_fn)(evutil_socket_t, short, void *); |
3.事件添加到事件处理框架上
当事件被创建之后,还需要将其添加到event_base这个框架中去,否则不能对其进行操作。
1 2 3 4 5 6 7 8 9 | // event_add int event_add( struct event *ev, //创建的事件 const struct timeval *tv /*tv有如下传值: NULL:事件被触发,直接调用回调函数 0-100:具体时间,阻塞该时间,待到这段时间过后,在调用回调函数,和select函数时间参数一样 返回值:函数调用成功返回0,失败返回1 */ ); |
4.开始事件循环
事件添加框架之后,就可以开启事件循环,直到事件结束。一般不会使用退出函数,知道就ok。
1 2 3 4 | // event_base_dispatch(简化版event_base_loop()) int event_base_dispatch(struct event_base* base); //等同于没有设置标志的 event_base_loop ( ) //将一直运行,直到没有已经注册的事件了 |
其他:需要关注的是事件循环的停止,如果需要设定循环的时间,或者立即退出循环有以下函数可以调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //等待事件循环结束之后,立即退出。 int event_base_loopexit( struct event_base *base, const struct timeval *tv/*设定一定的时间之后退出,但是在设定的时间到了之后 循环依旧在执行当前的事件,那么也会等待事件执行完毕之后退出。*/ ); //参数struct timeval *tv struct timeval { long tv_sec; long tv_usec; }; //立即退出循环,不管事件循环处理得什么样子。 int event_base_loopbreak(struct event_base *base); //返回值: 成功 0, 失败 -1 |
5.释放资源
1 2 3 | // 释放事件 void event_free(struct event *event); //释放事件 event_base_free(base);//释放框架 |
为了更加的直观理解整个事件的处理结构,整理了以下的核心伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <event2/event.h> int main() { //创建事件框架 struct event_base* base = NULL; base = event_base_new(); // 创建事件 struct event* ev = NULL; ev = event_new(base, fd, EV_WRITE , write_cb, NULL);//初始化事件、回调函数 // 添加事件 event_add(ev, NULL); // 事件循环 event_base_dispatch(base); // 释放资源 event_free(ev); event_base_free(base); close(fd); return 0; } // 回调函数 void write_cb(evutil_socket_t fd, short what, void *arg) { char buf[1024] = {0}; write(fd, buf, strlen(buf)+1); } |
2.2 bufferevent 带缓冲区的事件
带缓存区的事件和以上事件的创建处理流程是一样的,但是带缓存区的事件主要还是用来处理读写操作,所以在读写缓冲区的回调函数操作中就多了一些新的API。以下将会以常用的Socket为例来详解libevent库的具体使用。
1.bufferevent-base 事件框架的创建,同上。
在这个结构体中的回调函数中,
1 2 | struct event_base *base; base = event_base_new(); |
2.bufferevent 事件的创建以及添加到事件框架中
对于服务端来说,对于最常使用的socket来说,libevent库中给我们封装了一系列的socket监听处理接口,首先创建evconnlistener *listener 监听事件,通过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | struct evconnlistener *listener; listener = evconnlistener_new_bind(base, listener_cb, (void *)base, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr*)&sin, sizeof(sin));//listener监听事件的初始化 //listener_cb回调函数 static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data) { struct event_base *base = user_data; struct bufferevent *bev;//bufferevent事件的创建 bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);//bufferevent事件的初始化 if (!bev) { event_base_loopbreak(base); return; } bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);//bufferevent事件的回调操作 bufferevent_enable(bev, EV_WRITE);//写操作使能 bufferevent_disable(bev, EV_READ);//关闭读操作 bufferevent_write(bev, MESSAGE, strlen(MESSAGE)); } |
3.事件的循环,资源的释放
这两步和上同样
1 2 3 | event_base_dispatch(base);//事件循环 evconnlistener_free(listener);//释放监听事件,由于bufferevent事件封装在里面,一同释放 event_base_free(base);//释放框架 |
4.相关函数的形参注解
为了加深对API的理解,在下面的完整伪代码中,分别对重要函数的内部参数进行了一定的注解,方便大家理解阅读。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #include <string.h> #include <errno.h> #include <stdio.h> #include <signal.h> # include <arpa/inet.h> #include <sys/socket.h> #include <event2/bufferevent.h>//数据缓冲区的事件响应 #include <event2/buffer.h> #include <event2/listener.h> #include <event2/util.h> #include <event2/event.h> static const int PORT = 9999; int main(int argc, char **argv) { struct event_base *base; struct evconnlistener *listener; struct event *signal_event; struct sockaddr_in sin; base = event_base_new(); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(PORT); /* evconnlistener_new_bind ( struct event_base *base; evconnlistener_cb cd, //回调函数,接受连接之后,用户需要做的操作在回调函数里面 void *ptr,//回调函数传参 unsigned flags, int backlog,//-1 使用默认的最大值 const struct sockaddr *sa,//服务器的IP和端口信息 int socklen; ) */ listener = evconnlistener_new_bind(base, listener_cb, (void *)base, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr*)&sin, sizeof(sin)); /*evconnlistener_new_bind(); 创建监听socket 绑定 监听 等待并接受连接 */ event_base_dispatch(base);//事件循环 evconnlistener_free(listener);//释放sock资源 event_base_free(base); return 0; } void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data) { struct event_base *base = user_data; struct bufferevent *bev; /*struct bufferevent_socket_new( struct event_base *base, evutil_socket_t fd, enum bufferevent_options options ); *///创建带缓冲区的事件 bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); /* void bufferevent_setcb( struct bufferevent *bufev, bufferevent_cb readcb, bufferevent_cb writecb, bufferevent_cb eventcb, void *cbarg);//给读写缓冲区设置回调函数 */ bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, NULL); bufferevent_enable(bev, EV_WRITE);//写操作使能 bufferevent_disable(bev, EV_READ);//关闭读操作 char MESSAGE[] = "Hello, World\n"; bufferevent_write(bev, MESSAGE, strlen(MESSAGE)); } void conn_readcb(struct bufferevent *bev, void *user_data) { //**** bufferevent_free(bev); } void conn_writecb(struct bufferevent *bev, void *user_data) { //*** bufferevent_free(bev); } void conn_eventcb(struct bufferevent *bev, short events, void *user_data) { //*** bufferevent_free(bev); } |