跨平台事件驱动 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,如下所示。
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去创建一个事件,并添加进这个框架。
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 初始化一个新的事件结构体,事件中包含了事件类型,回调函数等,
// 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这个框架中去,否则不能对其进行操作。
// event_add
int event_add(
struct event *ev, //创建的事件
const struct timeval *tv
/*tv有如下传值:
NULL:事件被触发,直接调用回调函数
0-100:具体时间,阻塞该时间,待到这段时间过后,在调用回调函数,和select函数时间参数一样
返回值:函数调用成功返回0,失败返回1 */
);
4.开始事件循环
事件添加框架之后,就可以开启事件循环,直到事件结束。一般不会使用退出函数,知道就ok。
// event_base_dispatch(简化版event_base_loop())
int event_base_dispatch(struct event_base* base);
//等同于没有设置标志的 event_base_loop ( )
//将一直运行,直到没有已经注册的事件了
其他:需要关注的是事件循环的停止,如果需要设定循环的时间,或者立即退出循环有以下函数可以调用
//等待事件循环结束之后,立即退出。
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.释放资源
// 释放事件
void event_free(struct event *event); //释放事件
event_base_free(base);//释放框架
为了更加的直观理解整个事件的处理结构,整理了以下的核心伪代码:
#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 事件框架的创建,同上。
在这个结构体中的回调函数中,
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()
函数进行读写事件的反馈回调操作等。
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.事件的循环,资源的释放
这两步和上同样
event_base_dispatch(base);//事件循环
evconnlistener_free(listener);//释放监听事件,由于bufferevent事件封装在里面,一同释放
event_base_free(base);//释放框架
4.相关函数的形参注解
为了加深对API的理解,在下面的完整伪代码中,分别对重要函数的内部参数进行了一定的注解,方便大家理解阅读。
#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);
}
更多推荐
所有评论(0)