1. 创建 event_base (乐高底座)
struct event_base *event_base_new(void);
struct event_base *base = event_base_new();
2. 创建 事件evnet, 事件为“非未决态”(没有资格被处理)。
常规事件 event --> event_new();
struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);
bufferevent --> bufferevent_socket_new();
3. 将事件 添加到 base上,事件转化为 “未决态”(有资格被处理,但还未被处理)。
int event_add(struct event *ev, const struct timeval *tv)
4. 循环监听事件满足 ==> while(1){ epoll_wait( epfd, ...); }
int event_base_dispatch(struct event_base *base);
event_base_dispatch(base);
5. 释放 event_base
event_base_free(base);
.
未决:有资格被处理,但还未被处理。
非未决:没有资格被处理。
所有事件具有相似的生命周期。调用 libevent 函数设置事件并且关联到event_base 之后, 事件进入“已初始化(initialized)”状态。此时可以将事件添加到event_base 中,这使之进入“未决(pending)”状态。在未决状态下,如果触发事件的条件发生(比如说,文件描述 符的状态改变,或者超时时间到达 ),则事件进入“激活(active)”状态,(用户提供的)事件回调函数将被执行。如果配置为“持久的(persistent)”,事件将保持为未决状态。否则, 执行完回调后,事件不再是未决的。删除操作可以让未决事件成为非未决(已初始化)的 ; 添加操作可以让非未决事件再次成为未决的。
event_new --> event ---> 非未决 --> event_add --> 未决 --> dispatch() && 监听事件被触发 --> 激活态 --> 执行回调函数 --> 处理态 --> 非未决, event_add || EV_PERSIST --> 未决 --> event_del --> 非未决
// event_add
int event_add(
struct event *ev,
const struct timeval *tv
);
- tv:参数 | Value |
---|---|
NULL: | 没有定时时间,只要事件被触发, 对应的回调就会被调用。 |
tv = {0, n} | 设置的定时时间,即使在该时间段内检测的事件没被触发, 只要时间到达之后, 不管有无读写事件,回调函数还是会被调用。 |
read_fifo.c
#include
#include
#include
#include
#include
#include
#include
#include
// typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
void read_cb(int fd, short what, void* arg){
char buf[1024] = {0}; //清空buf;这一步很重要,不做看不出现象。
read(fd, buf, sizeof(buf));
printf("read event is %s \n", what & EV_READ ? "YES" : "NO");
printf("read content is %s \n\n", buf);
sleep(2);
}
int main(int argc, char* argv[])
{
unlink("myfifo");
// 创建用于通信的管道
mkfifo("myfifo", 0644);
int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
// 创建event_base对象
struct event_base *base = event_base_new();
// 创建event对象,ev为 “非未决态”(没有资格被处理)。
// struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);
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);
printf("read over!\n");
return 0;
}
write_fifo.c
#include
#include
#include
#include
#include
#include
#include
#include
// typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
void write_cb(int fd, short what, void* arg){
char buf[] = "hello libevent";
write(fd, buf, strlen(buf));
printf("write event is %s \n", what & EV_WRITE ? "YES" : "NO");
sleep(1);
}
int main(int argc, char* argv[])
{
int fd = open("myfifo", O_WRONLY | O_NONBLOCK);
// 创建event_base对象
struct event_base *base = event_base_new();
// 创建event对象
// struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);
struct event *ev = NULL;
ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, 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);
printf("write over!\n");
return 0;
}
持续写,持续读 的结果:
ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);
ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);
问题:
写事件怎么触发的?
回答:
缓冲区可写就触发了。
非持续写,持续读 的结果:
ev = event_new(base, fd, EV_WRITE, write_cb, NULL);
ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);
注意:
这里每次读管道之前,都用代码,清空 buf 缓冲区。
char buf[1024] = {0}; //清空buf;这一步很重要,不做看不出现象。
只触发了一次写,持续触发读。
参考链接:
详解libevent网络库(一)---框架的搭建