我们可以把libevent项目想象为造火箭的过程,我们都只是螺丝工,那么造火箭需要做什么?
1. 拿出火箭壳(创建框架)
2. 造螺丝 (创建事件)
3. 拧螺丝 (添加事件)
4. 造火箭(事件循环)
一:掏出火箭壳 —>event_base()创建与释放
我们开始第一步:创建一个event_base
// 创建event_base
struct event_base* event_base_new(void)
当然,作为一个优秀的c语言程序员(咳,我还是个菜),要在创建的同时考虑资源释放的问题,
在程序的最后我们需要 event_base_free 进行释放(但我们不得不提前考虑)
// 释放event_base_free
event_base_free(struct event_base* base);
二:造螺丝 —>event_new()创建与释放
注:创建事件:(当前篇只针对不带缓冲区event事件进行讲解,有关缓存学习请见后续更新)
// 创建新事件
struct event *event_new(
struct event_base *base,
evutil_socket_t fd, - // 文件描述符 - int **底层是对epollin与epollout的封装**
short what,
event_callback_fn cb, // 事件的处理回调函数
void *arg //回调函数传参
);
// 事件的处理回调函数
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
// 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 // 边沿模式
在程序的最后我们需要 event_free 进行释放(但我们不得不提前考虑)
// 创建event_free
void event_free(struct event *event);
调用event_new()函数之后, 新事件处于已初始化和非未决状态 (翻译:螺丝造好了,还没拧到火箭时的状态)
三:拧螺丝 —>event_add()相关函数
创建事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。
使用event_add()将事件添加到event_base。 (将螺丝拧上去)
非未决事件 -> 未决事件.
// event_add
int event_add(
struct event *ev,
const struct timeval *tv
);
- tv:参数 Value
NULL: 事件被触发, 对应的回调被调用
tv = {0, n} 设置的时间,在改时间段内检测的事件没被触发, 时间到达之后, 回调函数还是会被调用
函数调用成功返回0, 失败返回-1
设置非未决(作为了解实际中少用)
未决事件 -> 非未决事件.
//对已经初始化的事件调用 event_del()将使其成为非未决和非激活的。如果事件不是未决的或者激活的,调用将没有效果。
int event_del(struct event *ev);
//成功时函数返回 0,失败时返回-1。
有关于未决态和非未决态的理解:
1. 非未决: 没有资格被处理
2. 未决: 有资格被处理但是还没有处理
四:一节一节造火箭 —>event_base_dispatch()相关函数
最后,我们只需将添加 事件循环
使用event_base_dispatch()函数
// event_base_dispatch(简化版event_base_loop())
int event_base_dispatch(struct event_base* base);
//等同于没有设置标志的 event_base_loop ( )
//将一直运行,直到没有已经注册的事件了,或者调用 了event_base_loopbreak()或者 event_base_loopexit()为止
关于event_base_loop()函数
// event_base_loop()
int event_base_loop(struct event_base *base, int flags);
//正常退出返回0, 失败返回-1
//flages
#define EVLOOP_ONCE 0x01
//事件只会被触发一次
//事件没有被触发, 阻塞等
#define EVLOOP_NONBLOCK 0x02
//非阻塞 等方式去做事件检测
//不关心事件是否被触发了
#define EVLOOP_NO_EXIT_ON_EMPTY 0x04
//没有事件的时候, 也不退出轮询检测
关于退出事件循环函数event_base_loopexit()与event_base_loopbreak():
执行当前后退出 event_base_loopexit()
//如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出
int event_base_loopexit(
struct event_base *base,
const struct timeval *tv
);
//参数struct timeval *tv
struct timeval {
long tv_sec;
long tv_usec;
};
立即退出循环 event_base_loopbreak()
//让event_base 立即退出循环
int event_base_loopbreak(struct event_base *base);
//返回值: 成功 0, 失败 -1
Demo
最后举个栗子:
注:用不带缓冲区操作时的写法:采用fifo通讯的方式
//write_fifo.c
#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 write_cb(evutil_socket_t fd, short what, void *arg)
{
// write管道
char buf[1024] = {0};
static int num = 666;
sprintf(buf, "hello, world == %d\n", num);
write(fd, buf, strlen(buf)+1);
}
int main(int argc, const 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);
// 添加事件
event_add(ev, NULL);
// 事件循环
event_base_dispatch(base);
// 释放资源
event_free(ev);
event_base_free(base);
close(fd);
return 0;
}
//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("data len = %d, buf = %s\n", len, buf);
printf("read event: %s", what & EV_READ ? "Yes" : "No");//对what类型对判断
}
int main(int argc, const char* argv[])
{
unlink("myfifo");
//创建有名管道
mkfifo("myfifo", 0664);
// open file
int fd = open("myfifo", O_RDONLY | 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_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;
}
使用gcc对文件编译 -levevt引用libevent库
gcc read_fifo.c -o read -levent
gcc write_fifo.c -o write -levent
./read ./write 依次执行即可