目录
libevent是一个轻量级的开源高性能网络库,基于"事件"异步通信模型。适用于windows、linux、bsd等多种平台(跨平台),内部使用select、epoll、kqueue等系统调用管理事件机制。著名分布式缓存软件memcached也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
安装libevent
//在本地目录解压安装包
tar zxvf libevent-2.1.8-stable.tar.gz
//到解压好的安装包文件夹下执行安装过程
cd libevent-2.1.8-stable/
//执行./configure
./configure
make
sudo make install
查看libevent头文件
/usr/local/include
查看libevent库文件
/usr/local/lib
在配置阶段没有指定安装目录
gcc helloworld.c -o helloworld -l event //去掉lib和后缀.so(动态库)就是库名
Libevent框架
<1>创建event_base
struct event_base {
const struct eventop *evsel;
void *evbase;
int event_count; /* counts number of total events */
int event_count_active; /* counts number of active events */
int event_gotterm; /* Set to terminate loop */
int event_break; /* Set to terminate loop immediately */
/* active event management */
struct event_list **activequeues;
int nactivequeues;
/* signal handling info */
struct evsignal_info sig;
struct event_list eventqueue;
struct timeval event_tv;
struct min_heap timeheap;
struct timeval tv_cache;
};
event_base_new()函数分配并且返回一个新的具有默认设置的event_base。函数会检测环境变量,返回一个到event_base的指针。若发生错误,则返回NULL。选择各种方法时,函数会选择OS支持的最快方法
struct event_base *event_base_new(void);
struct event_base *base = event_base_new();
<2>创建事件event
常规事件event
创建一个事件(非常重要!!!)
/*
base: event_base_new() 返回值
fd: 绑定到event上的文件描述符
what:对应所需执行的读、写、异常操作
EV_READ
EV_WRITE
EV_PERSIST持续触发 (可以理解为:while(read()) 或 while(write()))
cb:一旦事件满足监听条件,回调的函数
typedef void(*event_callback_fn)(evutil_socket_t fd, short, void *)
arg:回调的函数的参数
返回值:成功创建的event
*/
struct event *event_new(struct event_base *base, evutil_socket_t fd, short what,
event_callback_fn cb, void *arg);
添加事件到event_base
/*
ev: event_new() 函数返回的事件
tv:为NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用
为非0。等待期间,检查事件没有被触发,时间到,回调函数依旧会被调用
*/
int event_add(struct event *ev, const struct timeval *tv);
将事件从base上拿下
//ev:event_new()函数返回的事件
int event_del(struct event *ev);
释放事件
//成功:0, 失败:-1
int event_free(struct event *ev);
未决和非未决
未决:有资格被处理,但还没有被处理
非未决:没有资格处理
event_new --> event --> 非未决 --> event_add -->未决 --> dispath() && 监听事件被触发 --> 激活态 --> 执行回调函数 --> 处理态 --> 非未决 event_add && EV_PERSIST --> 未决 --> event_del --> 非未决
带缓冲区的事件bufferevent
bufferevent
#include <event2/bufferevent.h>
原理:bufferevent有两个缓冲区:也是队列实现,缓冲区内部的数据只能读一次,读完就没有了。
由于使用fifo,数据先进先出
读:有数据 --》 读回调函数read_cb()被调用 --》 使用bufferevent_read() --》 读数据
写:使用bufferevent_write() --》 向写缓冲中写数据 --》 该缓冲区有数据自动写出 --》 写完,回调函数write_cb()被调用
带缓冲区的事件创建、释放
/*
创建:
base:event_base_new 函数的返回值
fd: 跟bufferevent绑定的文件描述符。类比 event_new()
options:
BEV_OPT_CLOSE_ON_FREE:释放 bufferevent 时关闭底层传输端口。这将关闭底层
套接字,释放底层bufferevent 等。
BEV_OPT_THREADSAFE:自动为 bufferevent 分配锁,这样就可以安全地在多个线程
中使用 bufferevent。
BEV_OPT_DEFER_CALLBACKS:设置这个标志时,bufferevent 延迟所有回调,如上所述
BEV_OPT_UNLOCK_CALLBACKS:默认情况下,如果设置 bufferevent 为线程安全的,则
bufferevent 会在调用用户提供的回调时进行锁定。设置这个选项会让 libevent在执行
回调的时候不进行锁定。
*/
struct bufferevent *bufferevent_socket_new(struct event_base *base,
evutil_socket_t fd, enum bufferevent_options options);
/*
释放:
bev: bufferevent_socket_new()的返回值
*/
void bufferevent_free(struct bufferevent *bev);
给读写缓冲区设置回调(非常重要!!!)
/*
bufev: bufferevent_socket_new() 返回值
readcb: 设置bufferevent读缓冲, 对应回调
writecb: 设置bufferevent写缓冲, 对应回调 ,可传NULL
eventcb: 设置事件回调。 也可传NULL
cbarg:上述回调函数使用的参数
*/
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecd,
bufferevent_event_cb eventcb,
void *cbarg);
readcb对应的回调函数
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
如:
void read_cb(struct bufferevent *bev, void *arg){
......
bufferevent_read(); //读数据,类似read()的作用
}
读数据:从bufferevent输入缓冲区 中 移除数据
//通常用在readcb中,替代read()
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
//常用在bufferevent_read之后,替代write()
int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
writecb对应的回调函数
非常不使用!!!!
如:
void write_cb(struct bufferevent *bev, void *arg){
......
}
eventcb对应的回调函数
typedef void(*bufferevent_event_cb)(struct bufferevent *bev,
short events, void *ctx);
events:
BEV_EVENT_READING:读取操作时发生某事件,具体是哪种事件请看其他标志。 BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志。 BEV_EVENT_ERROR : 操 作 时 发 生 错 误 。 关 于 错 误 的 更 多 信 息 , 请 调 用 EVUTIL_SOCKET_ERROR()。
BEV_EVENT_TIMEOUT:发生超时。
BEV_EVENT_EOF:遇到文件结束指示。
BEV_EVENT_CONNECTED:请求的连接过程已经完成。
禁用、启动缓冲区
默认:新建的bufferevent写缓冲是enable的。而,读缓冲是disable的
//通常用来启用bufferevent的read缓冲
void bufferevent_enable(struct bufferevent *bufev, short events);启用缓冲区
//events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
void bufferevent_disable(struct bufferevent *bufev, short events); 禁用缓冲区
//获取缓冲区的禁用状态,需要借助& 来得到
short bufferevent_get_enabled(struct bufferevent *bufev);
<3>将事件添加到base上
/*
ev: event_new() 函数返回的事件
tv:为NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用
为非0。等待期间,检查事件没有被触发,时间到,回调函数依旧会被调用
*/
int event_add(struct event *ev, const struct timeval *tv);
<4>循环监听事件满足
int event_base_dispatch(struct event_base *base);
/*
base:event_base_new 函数的返回值
成功 :0, 失败 :-1
只有event_new 中指定了 EV_PERSIST 才持续触发,否则只触发一次,就跳出循环
通常这样:EV_WRITE|EV_PERSIST 、EV_READ|EV_PERSIST
*/
停止循环
若想在移除所有已注册的事件之间停止活动的事件循环,可以调用两个稍有不同的函数。
//在指定时间后停止循环 tv:设置超时时间
int event_base_loopexit(struct event_base *base, const struct timeval *tv);
//立即停止循环
int event_base_loopbreak(struct event_base *base);
<5>释放event_base
void event_base_free(struct event_base *base);
<6>其他函数
查看当前系统支持哪些多路I/O
//返回:字符指针数组
const char **event_get_supported_methods(void);
查看当前系统用的那个 多路I/O
const char * event_base_get_method(const struct event_base *base);
查看fork后子进程使用的event_base
不是所有事件后端都在调用fork()之后可以正确工作。所以,若在使用fork()或者其他相关系统调用启动新进程之后,希望在新进程中继续使用event_base,就需要进行重新初始化。
/*
成功:0,失败:-1
使用该函数后,父进程创建的base才能在子进程中生效
*/
int event_reinit(struct event_base *base);
设置event_base优先级
libevent支持为事件设置多个优先级。然而,event_base默认只支持单个优先级。可以调用event_base_priority_init()设置event_base的优先级数目。
/*
成功时这个函数返回0,失败时返回-1。base是要修改的event_base,n_priorities是要支持的优先级数目,这个数目至少为1.每个新的事件可用的优先级将从0(最高)到n_priorities-1(最低)。
*/
int event_base_priority_init(struct event_base *base, int n_priorities);
常量 EVENT_MAX_PRIORITIES 表示 n_priorities 的上限。调用这个函数时为 n_priorities 给出更大的值是错误的。
必须在任何事件激活之前调用这个函数,最好在创建event_base后立刻调用。
实现Libevent和FIFO的读写
read_fifo
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <event2/event.h>
//对操作处理函数
void read_cb(evutil_socket_t fd, short what, void *arg){
//读管道
char buf[1024] = {0};
int len = read(fd, buf, sizeof(buf));
printf("read event: %s \n", what & EV_READ ? "Yes" : "No");
printf("data len = %d, buf = %s\n", len, buf);
sleep(1);
}
//读管道
int main(int argc, const char* argv[]){
unlink("myfifo");
//创建有名管道 0664:设置fifo权限
mkfifo("myfifo", 0664);
//open file
int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
if(fd == -1){
perror("open error");
exit(1);
}
//创建个event_base
struct event_base *base = NULL;
base = event_base_new();
//创建事件
struct event* ev = NULL;
ev = event_new(base, fd , EV_READ | EV_PERSIST, read_cb, NULL);
//添加事件
event_add(ev, NULL);
//事件循环
event_base_dispatch(base);
//释放资源
event_free(ev);
event_base_free(base);
close(fd);
return 0;
}
write_fifo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <event2/event.h>
//对操作处理函数
void write_cb(evutil_socket_t fd, short what, void *arg){
//write管道
char buf[1024] = {0};
static int num = 0;
sprintf(buf, "hello,world-%d\n", num++);
write(fd, buf, strlen(buf)+1);
sleep(1);
}
//写管道
int main(int argc, char *argv[]){
//open file
int fd = open("myfifo", O_WRONLY | O_NONBLOCK);
if(fd == -1){
perror("open error");
exit(1);
}
//写管道
struct event_base* base = NULL;
base = event_base_new();
//创建事件
struct event* ev = NULL;
//检测的写缓冲区是否有空间写
//ev = event_new(base, fd, EV_WRITE, write_cb, NULL);
ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);
//添加事件
event_add(ev, NULL);
//事件循环
event_base_dispatch(base);
//释放资源
event_free(ev);
event_base_free(base);
close(fd);
return 0;
}