一、事件处理框架(event_base)
1. event_base
使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个event_base 结构体持有一个事件集合,可以检测以确定哪个事件是激活的。
-
相当于epoll红黑树的树根
- 抽象层, 完成对event_base的封装
- 每个 event_base 都有一种用于检测哪种事件已经就绪的 ”方法“,或者说后端。
2. 相关函数
(1)创建event_base
struct event_base* event_base_new(void); 失败返回NULL
(2)释放event_base
event_base_free(struct event_base* base);
(3)循环监听base对应的事件, 等待条件满足
event_base_dispatch();
(4)查看event_base封装的后端
const char **event_get_supported_methods(void); char* str[]; const char * event_base_get_method( const struct event_base *base );
注意:event_base和fork
- 子进程创建成功之后, 父进程可以继续使用event_base
- 子进程中需要继续使用event_base需要重新进程初始化
int event_reinit(struct event_base* base);
二、事件循环(event_loop)
1. 事件处理方式
一旦有了一个已经注册了某些事件的 event_base, 就需要让 libevent 等待事件并且通知事件的发生。
#define EVLOOP_ONCE 0x01 事件只会被触发一次 事件没有被触发, 阻塞等 #define EVLOOP_NONBLOCK 0x02 非阻塞 等方式去做事件检测 不关心事件是否被触发了 #define EVLOOP_NO_EXIT_ON_EMPTY 0x04 没有事件的时候, 也不退出轮询检测
2. 相关函数
(1)int event_base_loop(struct event_base *base, int flags);
正常退出返回0, 失败返回-1
(2)int event_base_dispatch(struct event_base* base);
- 等同于没有设置标志的 event_base_loop ( )
- 将一直运行,直到没有已经注册的事件了,或者调用 了event_base_loopbreak()或者 event_base_loopexit()为止。
3. 循环停止
(1)如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出
int event_base_loopexit( struct event_base *base, const struct timeval *tv );
(2)让event_base 立即退出循环
int event_base_loopbreak(struct event_base *base);
返回值: 成功 0, 失败 -1
其中:
struct timeval { long tv_sec; long tv_usec; };
三、事件创建 (event)
1. 创建新事件
#define EV_TIMEOUT 0x01 // 废弃 #define EV_READ 0x02 #define EV_WRITE 0x04 #define EV_SIGNAL 0x08 #define EV_PERSIST 0x10 // 持续触发 #define EV_ET 0x20 // 边沿模式 typedef void (*event_callback_fn)(evutil_socket_t, short, void *); struct event *event_new( struct event_base *base, evutil_socket_t fd, // 文件描述符 - int short what, event_callback_fn cb, // 事件的处理动作 void *arg );
注意:调用event_new()函数之后,新事件处于已初始化和非未决状态 。
2. 释放事件
void event_free(struct event *event);
3. 设置未决事件
构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到event_base, 非未决事件 -> 未决事件。
int event_add( struct event *ev, const struct timeval *tv );
参数:
ev:创建的事件
tv:
NULL: 事件被触发, 对应的回调被调用
tv = {0, 100}, 如果设置的时间,在改时间段内检测的事件没被触发, 时间到达之后, 回调函数还是会被调用
返回值:
函数调用成功返回 0,失败返回 -1 。
4. 设置非未决
int event_del(struct event *ev);
对已经初始化的事件调用 event_del() 将使其成为非未决和非激活的。如果事件不是未决的或者激活的,调用将没有效果。
返回值:
成功时函数返回 0,失败时返回-1。
5. 事件的状态转换
6. 示例
通过 libevent 实现进程间通过管道来通信:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <string.h> 7 #include <fcntl.h> 8 #include <event2/event.h> 9 10 // 对操作处理函数 11 void write_cb(evutil_socket_t fd, short what, void *arg) 12 { 13 // write管道 14 char buf[1024] = {0}; 15 static int num = 0; 16 sprintf(buf, "hello, world == %d\n", num++); 17 write(fd, buf, strlen(buf)+1); 18 } 19 20 21 // 写管道 22 int main(int argc, const char* argv[]) 23 { 24 // open file 25 int fd = open("myfifo", O_WRONLY | O_NONBLOCK); 26 if(fd == -1) 27 { 28 perror("open error"); 29 exit(1); 30 } 31 32 // 写管道 33 struct event_base* base = NULL; 34 base = event_base_new(); 35 36 // 创建事件 37 struct event* ev = NULL; 38 // 检测的写缓冲区是否有空间写 39 ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL); 40 41 // 添加事件 42 event_add(ev, NULL); 43 44 // 事件循环 45 event_base_dispatch(base); 46 47 // 释放资源 48 event_free(ev); 49 event_base_free(base); 50 close(fd); 51 52 return 0; 53 }