使用libevent写一个读管道程序
头文件#include <event.h>
- 创建有名管道文件
mkfifo("fifo.tmp", 0700);
- 打开管道文件
int fd = open("fifo.tmp", O_RDONLY);
- 创建事件
struct event ev;
- 创建事件集合
- 方法1:
event_init();
- 方法2:
struct event_base *base = event_base_new();
- 此方法需要手动释放事件集合
event_base_free(base);
- 此方法需要手动释放事件集合
- 方法1:
- 将文件描述符与事件进行绑定
- 方法1:其实还是调用的event_assign,只是默认传入了全局的事件集合
event_set(&ev, fd, EV_READ | EV_PERSIST, fifo_read, NULL);
- 方法2:可以传入自己创建的事件集合base
event_assign(&ev, base, SIGINT, EV_SIGNAL | EV_PERSIST, signal_handler, NULL);
- 必须要写一个回调函数,当 监听事件发生时(即fd可读时),需要进行什么操作
void fifo_read(evutil_socket_t fd, short event, void *arg)
- 方法1:其实还是调用的event_assign,只是默认传入了全局的事件集合
- 把事件添加到集合中
event_add(&ev, NULL);
- 开始监听
- 如果是监听默认的全局事件集合
event_dispatch();
- 监听自己创建的事件集合base
event_base_dispatch(base);
- 如果是监听默认的全局事件集合
完整代码:编译时要加-levent
//mkfifo
#include <sys/types.h>
#include <sys/stat.h>
//exit
#include <stdlib.h>
//open
#include <fcntl.h>
//read
#include <unistd.h>
//perror
#include <stdio.h>
#include <errno.h>
#include <event.h>
//当监听的事件满足条件时,触发该回调函数,通过该函数读取数据
void fifo_read(evutil_socket_t fd, short event, void *arg)
{
char buf[32] = {0};
int ret = read(fd, buf, sizeof(buf));
if (-1 == ret)
{
perror("read");
exit(1);
}
printf("从管道读取 %s\n", buf);
}
int main()
{
int ret = mkfifo("fifo.tmp", 0700);
if (-1 == ret)
{
perror("mkfifo");
exit(1);
}
int fd = open("fifo.tmp", O_RDONLY);
if (-1 == fd)
{
perror("open");
exit(1);
}
//创建事件
struct event ev;
//初始化事件集合
event_init();
//把fd和ev绑定
//事件、关联的文件描述符、监听什么事件、回调函数、回调函数的参数
event_set(&ev, fd, EV_READ | EV_PERSIST, fifo_read, NULL);
//把事件添加到集合中
event_add(&ev, NULL);
//开始监听
event_dispatch();//死循环 如果集合中没有事件可以监听,则返回
//一旦有fd可读,就会把集合中所有fd清掉(类似select),因此这里读取到一次就结束
//如果希望一直监听,那么可以把event设置为EV_PERSIST,使其具有事件持久性
return 0;
}
使用libevent监听信号
- 其实只需注意怎样把事件和信号绑定起来:
event_assign(&ev, base, SIGINT, EV_SIGNAL | EV_PERSIST, signal_handler, &ev);
完整代码:
#include <stdio.h>
#include <signal.h>
#include <event.h>
int signal_count = 0;//记录一下收到多少次信号了
//evutil_socket_t既可以是文件描述符类型,又可以是信号
void signal_handler(evutil_socket_t fd, short event, void *arg)
{
struct event *ev = (struct event *)arg;
printf("收到信号 %d\n", fd);
signal_count++;
if (signal_count >= 2)
{
//把事件从集合中删除,这样就不会再监听SIGINT信号了,能够正常终止了
event_del(ev);
}
}
int main()
{
//创建事件集合
struct event_base *base = event_base_new();
//创建事件
struct event ev;
//把事件和信号绑定在一起
//和event_set相比,只是需要多传一个事件集合对象base。event_set本身就是调用的event_assign
event_assign(&ev, base, SIGINT, EV_SIGNAL | EV_PERSIST, signal_handler, &ev);//这里把ev传参过去了
//把事件添加到集合中
event_add(&ev, NULL);
//开始监听集合
//event_dispatch();是监听默认的全局的事件集合
//这里需要监听的是我们自己创建的事件集合
event_base_dispatch(base);
//手动释放事件集合
event_base_free(base);
return 0;
}
实现服务器
-
不用创建事件,有专门的函数自动在连接成功时将cfd加入事件集合
-
- 创建事件集合
-
- 创建套接字
-
- evconnlistener_new_bind函数
- 传入 事件集合、回调函数、回调函数参数、flags、最大连接队列长度、套接字、套接字大小
- flags
LEV_OPT_CLOSE_ON_FREE 自动关闭套接字
LEV_OPT_REUSEABLE 端口复用
- flags
- 得到 evconnlistener对象指针
-
- 回调函数:在此执行接收连接后的操作
读写需要通过bufferevent对象来进行
- 创建bufferevent对象
struct bufferevent *bev = bufferevent_socket_new(base, cfd, BEV_OPT_CLOSE_ON_FREE)
- 怎么读写 (bufferevent对象、读回调函数、写回调、异常回调、参数)
bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
- 设置bufferevent对象的可读
bufferevent对象创建后默认是关闭读写事件的,这里选择打开读事件,一旦有新数据可读时,就触发 读回调函数
bufferevent_enable(bev, EV_READ);
- 具体的读操作:在读回调函数中实现读取数据的操作
bufferevent_read(bev, buf, sizeof(buf));
- 回调函数:在此执行接收连接后的操作
-
- 开始监听事件集合
-
- 释放监听对象
evconnlistener_free(listener)
- 释放监听对象
-
- 释放事件集合
总结:
创建事件和事件集合、绑定事件、添加事件到集合、开启监听。而在使用套接字通信时,事件的创建、绑定和添加步骤被自动执行