目标
- 学会libevent的安装。
- 学会编译和运行使用libevent的程序。
- 了解libevent反应器原理
- 熟悉libevent的开发步骤
- 能够使用libevent解决一些简单问题。
1.简介
- 适用于windows、linux、bsd等多种平台
- 轻量级的开源的高性能的事件触发的网络库
- 内部使用
select
、poll
、epoll
等系统调用管理事件机制
2. 下载安装
2.1 自动安装
- Centos
yum install libevent-devel
- Ubuntu
apt-get install libevent-dev
2.2 手动安装
- 下载官网
- 配置
./configure
- 编译
make
- 安装
sudo make install
- 测试
ll /usr/lib/libevent*.so
或者
ll /usr/local/lib/libevent*.so
3. 构成
No. | 模块 | 说明 |
---|---|---|
1 | libevent_core |
所有核心的事件和缓冲功能,包含event_base ,evbuffer ,bufferevent ,工具函数 |
2 | libevent_pthreads |
基于pthread可移植线程库的线程和锁,它独立于libevent_core,这样程序使用libevent时就不需要链接到pthread,除非是以多线程方式使用libevent。 |
3 | libevent_extra |
定义了特定协议HTTP、DNS、RPC |
4 | libevent |
因为历史原因而存在,不要使用这个库,未来版本可能被去掉libevent_core 和libevent_extra
|
4. 功能
No. | 功能 | 含义 |
---|---|---|
1 | 事件通知 | 当文件描述符可读可写时将执行回调函数。 |
2 | IO缓存 | 缓存事件提供了输入输出缓存,能自动的读入和写入,用户不必直接操作IO。 |
3 | 定时器 | 定时器的机制,在一定的时间间隔之后调用回调函数。 |
4 | 信号 | 触发信号,执行回调。 |
5 | 异步的DNS解析 | 异步解析DNS服务器的DNS解析函数集。 |
6 | 事件驱动的HTTP服务器 | HTTP服务器。 |
7 | RPC客户端服务器框架 | RPC服务器和客户端框架,自动的封装和解封数据结构。 |
8 | Reactor(反应器)模式 | 应用程序提供相应的接口并且注册到reactor |
相应的事件发生后,reactor自动调用相应的注册的接口函数(类似于回调函数)通知。
5. 接口
头文件:event2/event.h
库:event
执行需要指定动态链接库位置
export LD_LIBRARY_PATH=动态链接库位置
6. 使用流程
在libevent中主要有两部分构成反应器(event_base)和事件(event)构成。事件是基本操作单元。
- 创建反应器
struct event_base* pBase = event_base_new();
- 创建事件/赋值事件
struct event* pEvent = event_new(pBase,fd,what,cb,arg);
或者
struct event evt;
event_new(&evt,pBase,fd,what,cb,arg);
- 添加事件
event_add(pEvent,NULL);
- 分发事件
event_base_dispatch(pBase);
- 释放事件
event_free(pEvent);
- 释放反应器
event_base_free(pBase);
事件状态
No. | 状态 | 说明 | 对应接口 |
---|---|---|---|
1 | 已初始化(initialized) | 初始化事件后的状态 | 调用event_new() /event_assign()
|
2 | 未决状态(pending) | 添加事件后的状态 | 调用event_add()
|
3 | 激活状态(active) | 触发事件发生 | 回调cb
|
持久(persisitent)
7. 函数
- 反应器
No. | 功能 | 函数 |
---|---|---|
1 | 创建反应器 | struct event_base* event_base_new(); |
2 | 释放反应器 | void event_base_free(struct event_base* base); |
3 | 分发事件 | int event_base_dispatch(struct event_base* base); |
- 事件
No. | 功能 | 函数 |
---|---|---|
1 | 创建事件 | struct event* event_new(struct event_base* base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg) |
2 | 释放事件 | void event_free(struct event* ev); |
3 | 赋值事件 | int event_assign(struct event* ev,struct event_base* base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg); |
4 | 添加事件 | int event_add(struct event* ev,const struct timeval *tv); |
5 | 删除事件 | int event_del(struct event* ev); |
6 | 未决事件 | int event_pending(struct event* ev,short what,struct timeval *tv_out); |
7.1 反应器函数
7.1.1 创建反应器
struct event_base* event_base_new();
- 返回值
No. | 参数 | 说明 |
---|---|---|
1 | NULL |
失败 |
2 | 非NULL
|
反应器指针 |
7.1.2 释放反应器
void event_base_free(struct event_base* base);
- 参数
No. | 参数 | 说明 |
---|---|---|
1 | base |
反应器指针 |
7.1.3 分发事件
int event_base_dispatch(struct event_base* base);
- 参数
No. | 参数 | 说明 |
---|---|---|
1 | base |
反应器指针 |
- 返回值
No. | 参数 | 说明 |
---|---|---|
1 | -1 |
失败 |
2 | 0 |
成功 |
7.2 事件函数
7.2.1 创建事件
struct event* event_new(struct event_base* base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg)
- 参数
No. | 参数 | 说明 |
---|---|---|
1 | base |
反应器指针 |
2 | fd |
文件描述符 |
3 | what |
事件类型 |
4 | cb |
事件回调函数 |
5 | arg |
事件回调函数参数 |
对于文件描述符
fd
,如果没有文件描述符传入-1
,例如定时器事件。如果是系统事件,传入对应的事件宏定义。
- 事件类型
No. | 事件类型 | 说明 | 值 |
---|---|---|---|
1 | EV_TIMEOUT |
超时事件 | 0x01 |
2 | EV_READ |
读事件 | 0x02 |
3 | EV_WRITE |
写事件 | 0x03 |
4 | EV_SIGNAL |
信号事件 | 0x04 |
5 | EV_PERSIST |
持久配置 | 0x05 |
6 | EV_ET |
读写的边沿触发事件 | 0x06 |
event_callback_fn
typedef void (*event_callback_fn)(evutil_socket_t,short,void*);
- 返回值
No. | 参数 | 说明 |
---|---|---|
1 | NULL |
失败 |
2 | 非NULL
|
事件指针 |
7.2.2 释放事件
void event_free(struct event* ev);
- 参数
No. | 参数 | 说明 |
---|---|---|
1 | ev |
事件指针 |
7.2.3 赋值事件
int event_new(struct event* evt,struct event_base* base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg);
参数
除了第一个参数表示需要赋值的事件外,其他的与event_new()
相同。返回值
No. | 参数 | 说明 |
---|---|---|
1 | -1 |
失败 |
2 | 0 |
成功 |
7.2.4 添加事件
int event_add(struct event* ev,const struct timeval *tv);
- 参数
No. | 参数 | 说明 |
---|---|---|
1 | ev |
事件指针 |
2 | tv |
指定超时事件,NULL 表示永不超时 |
- 返回值
No. | 参数 | 说明 |
---|---|---|
1 | -1 |
失败 |
2 | 0 |
成功 |
7.2.5 删除事件
int event_del(struct event* ev);
- 参数
No. | 参数 | 说明 |
---|---|---|
1 | ev |
事件指针 |
- 返回值
No. | 参数 | 说明 |
---|---|---|
1 | -1 |
失败 |
2 | 0 |
成功 |
7.2.6 未决事件
int event_pending(struct event* ev,short what,struct timeval *tv_out);
- 参数
No. | 参数 | 说明 |
---|---|---|
1 | ev |
事件指针 |
2 | what |
事件类型 |
2 | tv_out |
返回超时事件 |
- 返回值
No. | 参数 | 说明 |
---|---|---|
1 | 非零0
|
未决状态的事件类型 |
8. 示例
libevent事件有两种创建方式:堆上创建和非堆(栈或者全局变量)上创建。
下面通过下面几个案例二者的区别。
8.1 读取控制台数据
- 堆上创建读取事件
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <event2/event.h>
void onRead(evutil_socket_t fd, short event, void *arg) {
char buf[BUFSIZ];
read(fd,buf,BUFSIZ);
printf("%s\n",buf);
}
int main() {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建事件
struct event* pEvent = NULL;
pEvent = event_new(pBase,STDIN_FILENO,EV_READ,onRead,NULL);
// 添加事件
event_add(pEvent,NULL);
// 事件循环
event_base_dispatch(pBase);
// 释放事件
event_free(pEvent);
// 释放反应器
event_base_free(pBase);
return 0;
}
- 非堆上创建读取事件
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <event2/event.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本
void onRead(evutil_socket_t fd, short event, void *arg) {
char buf[BUFSIZ];
read(fd,buf,BUFSIZ);
printf("%s\n",buf);
}
int main() {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建事件
struct event evt;
event_assign(&evt,pBase,STDIN_FILENO,EV_READ,onRead,NULL);
// 添加事件
event_add(&evt,NULL);
// 事件循环
event_base_dispatch(pBase);
// 不需要释放事件
// 释放反应器
event_base_free(pBase);
return 0;
}
-
两种方式进行比较
8.2 定时器事件
- 堆上创建定时器事件
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg) {
printf("Hello,World!\n");
struct event** ppEvent = (struct event**)(arg);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 重新添加定时事件(定时事件触发后默认自动删除)
event_add(*ppEvent, &tv);
}
int main() {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建定时事件
struct event* pEvent = NULL;
pEvent = event_new(pBase,-1,EV_TIMEOUT,onTime,&pEvent);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 添加定时事件
event_add(pEvent, &tv);
// 事件循环
event_base_dispatch(pBase);
// 释放事件
event_free(pEvent);
// 释放反应器
event_base_free(pBase);
return 0;
}
- 非堆栈上创建定时器事件
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本
// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg) {
printf("Hello,World!\n");
struct event* pEvent = (struct event*)(arg);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 重新添加定时事件(定时事件触发后默认自动删除)
event_add(pEvent, &tv);
}
int main() {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建定时事件
struct event evt;
event_assign(&evt,pBase,-1,EV_TIMEOUT,onTime,&evt);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 添加定时事件
event_add(&evt, &tv);
// 事件循环
event_base_dispatch(pBase);
// 释放反应器
event_base_free(pBase);
return 0;
}
二者比较
- 持续化处理
默认处理是不持续的,即事件只能使用一次,可以通过设置EV_PERSIST
把事件设置成持续的。
堆栈上创建定时器事件
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
void onTime(evutil_socket_t fd, short event, void *arg) {
printf("Hello,World!\n");
}
int main() {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建定时事件
struct event* pEvent = NULL;
pEvent = event_new(pBase,-1,EV_TIMEOUT|EV_PERSIST,onTime,NULL);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 添加定时事件
event_add(pEvent, &tv);
// 事件循环
event_base_dispatch(pBase);
// 释放事件
event_free(pEvent);
// 释放反应器
event_base_free(pBase);
return 0;
}
非堆栈上创建定时器事件
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本
// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg) {
printf("Hello,World!\n");
}
int main() {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建定时事件
struct event evt;
event_assign(&evt,pBase,-1,EV_TIMEOUT|EV_PERSIST,onTime,NULL);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 添加定时事件
event_add(&evt, &tv);
// 事件循环
event_base_dispatch(pBase);
// 不需要释放事件
// 释放反应器
event_base_free(pBase);
return 0;
}
8.3 信号事件
堆栈上创建信号事件
#include <stdio.h>
#include <event2/event.h>
#include <signal.h>
void signal_cb(evutil_socket_t fd, short event, void *arg){
struct event *signal = (struct event*)arg;
printf("signal_cb: got signal %d\n", event_get_signal(signal));
}
int main(int argc, char **argv) {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建信号事件
struct event *pEvent = event_new(pBase, SIGINT,EV_SIGNAL,signal_cb,event_self_cbarg());
// 添加信号事件
event_add(pEvent, NULL);
// 事件循环
event_base_dispatch(pBase);
// 释放事件
event_free(pEvent);
// 释放反应器
event_base_free(pBase);
return 0;
}
非堆栈上创建信号事件
#include <stdio.h>
#include <event2/event.h>
#include <signal.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本
void signal_cb(evutil_socket_t fd, short event, void *arg){
struct event *signal = (struct event*)arg;
printf("signal_cb: got signal %d\n", event_get_signal(signal));
}
int main(int argc, char **argv) {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建信号事件
struct event evt;
event_new(&evt,pBase, SIGINT,EV_SIGNAL,signal_cb,event_self_cbarg());
// 添加信号事件
event_add(&evt, NULL);
// 事件循环
event_base_dispatch(pBase);
// 不需要释放事件
// 释放反应器
event_base_free(pBase);
return 0;
}
8.4 FIFO读事件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <event2/event.h>
static void fifo_read(evutil_socket_t fd, short event, void *arg)
{
char buf[255];
int len;
struct event *ev = (struct event*)arg;
fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
(int)fd, event, arg);
// 读取管道
len = read(fd, buf, sizeof(buf) - 1);
if (len <= 0) {
if (len == -1)
perror("read");
else if (len == 0)
fprintf(stderr, "Connection closed\n");
// 删除事件
event_del(ev);
// 退出事件循环
event_base_loopbreak(event_get_base(ev));
return;
}
// 打印管道
buf[len] = '\0';
fprintf(stdout, "Read: %s\n", buf);
}
int main(int argc, char **argv) {
const char *fifo = argv[1];
// 创建命名管道
unlink(fifo);
if (mkfifo(fifo, 0600) == -1) {
perror("mkfifo");
exit(1);
}
// 打开命名管道
int fifo_fd = open(fifo, O_RDONLY | O_NONBLOCK, 0);
if (fifo_fd == -1) {
perror("open");
exit(1);
}
// 初始化反应器
struct event_base* base = event_base_new();
// 创建FIFO读事件
struct event* evfifo = event_new(base, fifo_fd, EV_READ|EV_PERSIST, fifo_read,
event_self_cbarg());
// 添加FIFO读事件
event_add(evfifo, NULL);
// 事件循环
event_base_dispatch(base);
// 释放反应器
event_base_free(base);
// 关闭管道
close(fifo_fd);
// 删除管道
unlink(fifo);
return (0);
}
9. 定时器宏定义
对定时器事件,libevent提供了几个以evtimer_
开头的宏定义,简化代码。
// 创建定时器事件
#define evtimer_new(base,callback,arg) \
event_new((base),-1,0,(callback),(arg))
// 赋值定时器事件
#define evtimer_assign(event,base,callback,arg) \
event_assign((event),(base),-1,0,(callback),(arg))
// 添加定时器事件
#define evtimer_add(ev,tv) \
event_add((ev),(tv))
// 删除定时器事件
#define evtimer_del(ev) \
event_add(ev)
// 未决定时器事件
#define evtimer_pending(ev,what,tv_out) \
event_pending((ev),(what),(tv_out))
使用宏定义代替
// 创建定时事件
struct event* pEvent = NULL;
pEvent = event_new(pBase,-1,EV_TIMEOUT,onTime,&pEvent);
// 添加定时事件
event_add(pEvent, &tv);
代替
// 创建定时事件
struct event* pEvent = NULL;
pEvent = evtimer_new(pBase,onTime,&pEvent);
// 添加定时事件
evtimer_add(pEvent, &tv);
- 代码
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
#include <assert.h>
// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg) {
printf("Hello,World!\n");
assert(NULL != arg);
struct event** ppEvent = (struct event**)(arg);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 重新添加定时事件(定时事件触发后默认自动删除)
evtimer_add(*ppEvent, &tv);
}
int main() {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建定时事件
struct event* pEvent = NULL;
pEvent = evtimer_new(pBase,onTime,&pEvent);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 添加定时事件
evtimer_add(pEvent, &tv);
// 事件循环
event_base_dispatch(pBase);
// 释放事件
event_free(pEvent);
// 释放反应器
event_base_free(pBase);
return 0;
}
使用宏定义代替
// 创建定时事件
struct event evt;
event_assign(&evt,pBase,-1,EV_TIMEOUT|EV_PERSIST,onTime,NULL);
// 添加定时事件
event_add(&evt, &tv);
代替
// 创建定时事件
struct event evt;
evtimer_assign(&evt,pBase,onTime,&evt);
// 添加定时事件
evtimer_add(&evt, &tv);
- 代码
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本
// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg) {
struct event* pEvent = (struct event*)(arg);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 重新添加定时事件(定时事件触发后默认自动删除)
evtimer_add(pEvent, &tv);
}
int main() {
// 初始化反应器
struct event_base* pBase = event_base_new();
// 创建定时事件
struct event evt;
evtimer_assign(&evt,pBase,onTime,&evt);
// 定时间隔
struct timeval tv;
evutil_timerclear(&tv);
tv.tv_sec = 1;
// 添加定时事件
evtimer_add(&evt, &tv);
// 事件循环
event_base_dispatch(pBase);
// 释放反应器
event_base_free(pBase);
return 0;
}
10. 信号宏定义
对信号事件,libevent提供了几个以evsignal_
开头的宏定义,简化代码。
#define evsignal_new(base,signum,cb,arg) \
event_new(base,signum,EV_SIGNAL|EV_PERSIST,cb,arg)
#define evsignal_assign(ev,base,signum,cb,arg) \
event_assign(ev,base,signum,EV_SIGNAL|EV_PERSIST,cb,arg)
#define evsignal_add(ev,tv) \
event_add((ev),(tv))
#define evsignal_del(ev) \
event_del(ev)
#define evsignal_pending(ev,what,tv_out) \
event_pending((ev),(what),(tv_out))
- 示例
#include <stdio.h>
#include <event2/event.h>
#include <signal.h>
void signal_cb(evutil_socket_t fd, short event, void *arg){
struct event *signal = (struct event*)arg;
printf("signal_cb: got signal %d\n", event_get_signal(signal));
}
int main(int argc, char **argv) {
struct event_base* base = event_base_new();
struct event *signal_int = evsignal_new(base, SIGINT, signal_cb, event_self_cbarg());
event_add(signal_int, NULL);
event_base_dispatch(base);
event_free(signal_int);
event_base_free(base);
}
11. 废弃/老版的函数
头文件:event.h
No. | 函数 | 功能 |
---|---|---|
1 | event_init() |
初始化事件 |
2 | event_dispatch() |
事件分发 |
3 | event_set() |
初始化设置事件 |
- 示例
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event.h>
// 定时事件回调函数
void onTime(int sock, short event, void *arg) {
printf("Hello,World!\n");
// 事件间隔
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
// 重新添加定时事件(定时事件触发后默认自动删除)
event_add((struct event*)arg, &tv);
}
int main() {
// 初始化
event_init();
// 设置定时事件
struct event ev_time;
evtimer_set(&ev_time, onTime, &ev_time);
// 事件间隔
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
// 添加定时事件
event_add(&ev_time, &tv);
// 事件循环
event_dispatch();
return 0;
}
new event_base
与event_init()
区别:event_init()
是创建一个全局的new event_base
。
实现相同的功能,即能用系统函数,又能用第三方库,使用那种方式实现?