Linux 事件驱动库 libevent从安装到简单使用

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 监听事件,通过evconnlistener_new_bind()函数,完成了监听事件的初始化操作。其次在listener_cb()回调函数中,完成了bufferevent 事件的创建,并通过bufferevent_socket_new()函数完成了bufferevent 事件的初始化操作,主要包括事件的套接字绑定,并将事件添加到event_base框架中。最后当准备工作完成之后,读写操作就可以利用相对应的封装函数进行,bufferevent_write(),bufferevent_read() 函数。当然还可以利用 ufferevent_setcb()函数进行读写事件的反馈回调操作等。

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);
}