Libevent 简述

目录

安装libevent

查看libevent头文件

查看libevent库文件

在配置阶段没有指定安装目录

Libevent框架

<1>创建event_base

<2>创建事件event

常规事件event

创建一个事件(非常重要!!!)

添加事件到event_base

将事件从base上拿下

释放事件

未决和非未决

带缓冲区的事件bufferevent

bufferevent

带缓冲区的事件创建、释放

给读写缓冲区设置回调(非常重要!!!)

禁用、启动缓冲区

<3>将事件添加到base上

<4>循环监听事件满足

停止循环

<5>释放event_base

<6>其他函数

查看当前系统支持哪些多路I/O

查看当前系统用的那个 多路I/O

查看fork后子进程使用的event_base

设置event_base优先级

实现Libevent和FIFO的读写

read_fifo

write_fifo


libevent是一个轻量级的开源高性能网络库基于"事件"异步通信模型。适用于windows、linux、bsd等多种平台(跨平台),内部使用select、epoll、kqueue等系统调用管理事件机制。著名分布式缓存软件memcached也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。

安装libevent

//在本地目录解压安装包
tar zxvf libevent-2.1.8-stable.tar.gz

//到解压好的安装包文件夹下执行安装过程
cd libevent-2.1.8-stable/

//执行./configure
./configure
make
sudo make install

查看libevent头文件

/usr/local/include

查看libevent库文件

/usr/local/lib

在配置阶段没有指定安装目录

gcc helloworld.c -o helloworld -l event //去掉lib和后缀.so(动态库)就是库名

Libevent框架

<1>创建event_base

struct event_base { 
    const struct eventop *evsel; 
    void *evbase; 
    int event_count; /* counts number of total events */ 
    int event_count_active; /* counts number of active events */ 
    int event_gotterm; /* Set to terminate loop */ 
    int event_break; /* Set to terminate loop immediately */ 
    /* active event management */ 
    struct event_list **activequeues; 
    int nactivequeues; 
    /* signal handling info */ 
    struct evsignal_info sig; 
    struct event_list eventqueue; 
    struct timeval event_tv; 
    struct min_heap timeheap; 
    struct timeval tv_cache; 
};

event_base_new()函数分配并且返回一个新的具有默认设置的event_base。函数会检测环境变量,返回一个到event_base的指针。若发生错误,则返回NULL。选择各种方法时,函数会选择OS支持的最快方法

struct event_base *event_base_new(void);

struct event_base *base = event_base_new();

<2>创建事件event

  libevent 的基本操作单元是事件。每个事件代表一组条件的集合,这些条件包括:
<1>文件描述符已经就绪,可以读取或者写入
<2>文件描述符变为就绪状态, 可以读取或者写入 ( 仅对于边沿触发 IO)
<3>超时事件
<4>发生某信号
<5>用户触发事件 

常规事件event

创建一个事件(非常重要!!!)

/*
base: event_base_new() 返回值
fd: 绑定到event上的文件描述符
what:对应所需执行的读、写、异常操作
    EV_READ
    EV_WRITE
    EV_PERSIST持续触发 (可以理解为:while(read()) 或 while(write()))
cb:一旦事件满足监听条件,回调的函数
    typedef void(*event_callback_fn)(evutil_socket_t fd, short, void *)
arg:回调的函数的参数
返回值:成功创建的event
*/
struct event *event_new(struct event_base *base, evutil_socket_t fd, short what,
                        event_callback_fn cb, void *arg);

添加事件到event_base

/*
ev: event_new() 函数返回的事件
tv:为NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用
    为非0。等待期间,检查事件没有被触发,时间到,回调函数依旧会被调用
*/
int event_add(struct event *ev, const struct timeval *tv);

将事件从base上拿下

//ev:event_new()函数返回的事件
int event_del(struct event *ev);

释放事件

//成功:0, 失败:-1
int event_free(struct event *ev);

未决和非未决

未决:有资格被处理,但还没有被处理
非未决:没有资格处理

event_new --> event --> 非未决 --> event_add -->未决 --> dispath() && 监听事件被触发 --> 激活态 --> 执行回调函数 --> 处理态 --> 非未决 event_add && EV_PERSIST --> 未决 --> event_del --> 非未决

带缓冲区的事件bufferevent

bufferevent 由一个底层的传输端口(如 套接字 ),一个读取缓冲区和一个写入缓冲区组成。与通常的事件在底层传输端口已经就绪,可以读取或者写入的时候执行回调不同的是,bufferevent 在读取或者写入了足够量的数据之后调用用户提供的回调。
有多种共享公用接口的bufferevent类型,编写本文时已存在以下类型:
<1>基于 套接字 bufferevent : 使用 event_* 接口作为后端 , 通过 底层流式套接字发送或者接收数据 的 bufferevent
<2>异步 IO bufferevent : 使用 Windows IOCP 接口 , 通过底层流式套接字发送或者接收数据的bufferevent( 仅用于 Windows, 试验中 )
<3>过滤型 bufferevent : 将数据传输到底层 bufferevent 对象之前 , 处理输入或者输出数据的 bufferevent: 比如说 , 为了压缩或者转换数据。
<4>成对的 bufferevent : 相互传输数据的两个 bufferevent

bufferevent

#include <event2/bufferevent.h>

原理:bufferevent有两个缓冲区:也是队列实现,缓冲区内部的数据只能读一次,读完就没有了。
      由于使用fifo,数据先进先出

 

读:有数据 --》 读回调函数read_cb()被调用 --》 使用bufferevent_read() --》 读数据
写:使用bufferevent_write() --》 向写缓冲中写数据 --》 该缓冲区有数据自动写出 --》 写完,回调函数write_cb()被调用

带缓冲区的事件创建、释放

/*
创建:
base:event_base_new 函数的返回值
fd: 跟bufferevent绑定的文件描述符。类比 event_new()
options: 
BEV_OPT_CLOSE_ON_FREE:释放 bufferevent 时关闭底层传输端口。这将关闭底层
套接字,释放底层bufferevent 等。
BEV_OPT_THREADSAFE:自动为 bufferevent 分配锁,这样就可以安全地在多个线程
中使用 bufferevent。
BEV_OPT_DEFER_CALLBACKS:设置这个标志时,bufferevent 延迟所有回调,如上所述
BEV_OPT_UNLOCK_CALLBACKS:默认情况下,如果设置 bufferevent 为线程安全的,则
bufferevent 会在调用用户提供的回调时进行锁定。设置这个选项会让 libevent在执行
回调的时候不进行锁定。
*/
struct bufferevent *bufferevent_socket_new(struct event_base *base,
             evutil_socket_t fd, enum bufferevent_options options);


/*
释放:
bev: bufferevent_socket_new()的返回值
*/
void bufferevent_free(struct bufferevent *bev);

给读写缓冲区设置回调(非常重要!!!)

/*
bufev: bufferevent_socket_new() 返回值
readcb: 设置bufferevent读缓冲, 对应回调
writecb: 设置bufferevent写缓冲, 对应回调 ,可传NULL
eventcb: 设置事件回调。 也可传NULL
cbarg:上述回调函数使用的参数
*/
void bufferevent_setcb(struct bufferevent *bufev,
                        bufferevent_data_cb readcb,
                        bufferevent_data_cb writecd,
                        bufferevent_event_cb eventcb,
                        void *cbarg);

readcb对应的回调函数

typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);

如: 
void read_cb(struct bufferevent *bev, void *arg){
    ......
    bufferevent_read(); //读数据,类似read()的作用
}

读数据:从bufferevent输入缓冲区 中 移除数据

//通常用在readcb中,替代read()
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);

//常用在bufferevent_read之后,替代write()
int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);

writecb对应的回调函数

非常不使用!!!!
如:
void write_cb(struct bufferevent *bev, void *arg){
    ......
}

eventcb对应的回调函数

typedef void(*bufferevent_event_cb)(struct bufferevent *bev, 
             short events, void *ctx);

events:
BEV_EVENT_READING:读取操作时发生某事件,具体是哪种事件请看其他标志。 BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志。 BEV_EVENT_ERROR : 操 作 时 发 生 错 误 。 关 于 错 误 的 更 多 信 息 , 请 调 用 EVUTIL_SOCKET_ERROR()。
BEV_EVENT_TIMEOUT:发生超时。
BEV_EVENT_EOF:遇到文件结束指示。
BEV_EVENT_CONNECTED:请求的连接过程已经完成。

禁用、启动缓冲区

默认:新建的bufferevent写缓冲是enable的。而,读缓冲是disable的

//通常用来启用bufferevent的read缓冲
void bufferevent_enable(struct bufferevent *bufev, short events);启用缓冲区

//events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
void bufferevent_disable(struct bufferevent *bufev, short events); 禁用缓冲区

//获取缓冲区的禁用状态,需要借助& 来得到
short bufferevent_get_enabled(struct bufferevent *bufev);

<3>将事件添加到base上

/*
ev: event_new() 函数返回的事件
tv:为NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用
    为非0。等待期间,检查事件没有被触发,时间到,回调函数依旧会被调用
*/
int event_add(struct event *ev, const struct timeval *tv);

<4>循环监听事件满足

int event_base_dispatch(struct event_base *base);
/*
base:event_base_new 函数的返回值
成功 :0, 失败 :-1

只有event_new 中指定了 EV_PERSIST 才持续触发,否则只触发一次,就跳出循环
通常这样:EV_WRITE|EV_PERSIST 、EV_READ|EV_PERSIST
*/

停止循环

若想在移除所有已注册的事件之间停止活动的事件循环,可以调用两个稍有不同的函数。

//在指定时间后停止循环 tv:设置超时时间
int event_base_loopexit(struct event_base *base, const struct timeval *tv);

//立即停止循环
int event_base_loopbreak(struct event_base *base);

<5>释放event_base

void event_base_free(struct event_base *base);

<6>其他函数

查看当前系统支持哪些多路I/O

//返回:字符指针数组
const char **event_get_supported_methods(void);

查看当前系统用的那个 多路I/O

const char * event_base_get_method(const struct event_base *base);

查看fork后子进程使用的event_base

不是所有事件后端都在调用fork()之后可以正确工作。所以,若在使用fork()或者其他相关系统调用启动新进程之后,希望在新进程中继续使用event_base,就需要进行重新初始化。

/*
成功:0,失败:-1
使用该函数后,父进程创建的base才能在子进程中生效
*/
int event_reinit(struct event_base *base);

设置event_base优先级

libevent支持为事件设置多个优先级。然而,event_base默认只支持单个优先级。可以调用event_base_priority_init()设置event_base的优先级数目。

/*
成功时这个函数返回0,失败时返回-1。base是要修改的event_base,n_priorities是要支持的优先级数目,这个数目至少为1.每个新的事件可用的优先级将从0(最高)到n_priorities-1(最低)。
*/
int event_base_priority_init(struct event_base *base, int n_priorities);

常量 EVENT_MAX_PRIORITIES 表示 n_priorities 的上限。调用这个函数时为 n_priorities 给出更大的值是错误的。

必须在任何事件激活之前调用这个函数,最好在创建event_base后立刻调用。

实现Libevent和FIFO的读写

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("read event: %s \n", what & EV_READ ? "Yes" : "No");
    printf("data len = %d, buf = %s\n", len, buf);
    sleep(1);
}


//读管道
int main(int argc, const char* argv[]){

    unlink("myfifo");
    //创建有名管道  0664:设置fifo权限
    mkfifo("myfifo", 0664);

    //open file
    int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
    if(fd == -1){
        perror("open error");
        exit(1);
    }

    //创建个event_base
    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;
}              

write_fifo

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.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 = 0;
    sprintf(buf, "hello,world-%d\n", num++);
    write(fd, buf, strlen(buf)+1);
    sleep(1);
}

//写管道
int main(int argc, 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);
    ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);

    //添加事件
    event_add(ev, NULL);

    //事件循环
    event_base_dispatch(base);

    //释放资源
    event_free(ev);
    event_base_free(base);
    close(fd);

    return 0;
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值