Linux/Centos: libevent浅见

1.简介

  1. 适用于windows、linux、bsd等多种平台
  2. 轻量级的开源的高性能的事件触发的网络库
  3. 内部使用selectpollepoll等系统调用管理事件机制

2. 下载安装

2.1 自动安装

  • Centos

 

yum install libevent-devel
  • Ubuntu

 

apt-get install libevent-dev

2.2 手动安装

  1. 下载官网
  2. 配置./configure
  3. 编译make
  4. 安装sudo make install
  5. 测试

 

ll /usr/lib/libevent*.so

或者

 

ll /usr/local/lib/libevent*.so

3. 构成

No.模块说明
1libevent_core所有核心的事件和缓冲功能,包含event_baseevbufferbufferevent,工具函数
2libevent_pthreads基于pthread可移植线程库的线程和锁,它独立于libevent_core,这样程序使用libevent时就不需要链接到pthread,除非是以多线程方式使用libevent。
3libevent_extra定义了特定协议HTTP、DNS、RPC
4libevent因为历史原因而存在,不要使用这个库,未来版本可能被去掉libevent_corelibevent_extra

4. 功能

No.功能含义
1事件通知当文件描述符可读可写时将执行回调函数。
2IO缓存缓存事件提供了输入输出缓存,能自动的读入和写入,用户不必直接操作IO。
3定时器定时器的机制,在一定的时间间隔之后调用回调函数。
4信号触发信号,执行回调。
5异步的DNS解析异步解析DNS服务器的DNS解析函数集。
6事件驱动的HTTP服务器HTTP服务器。
7RPC客户端服务器框架RPC服务器和客户端框架,自动的封装和解封数据结构。
8Reactor(反应器)模式应用程序提供相应的接口并且注册到reactor

相应的事件发生后,reactor自动调用相应的注册的接口函数(类似于回调函数)通知。

 

5. 接口

头文件:event2/event.h
库:event

执行需要指定动态链接库位置

export LD_LIBRARY_PATH=动态链接库位置

6. 使用流程

在libevent中主要有两部分构成反应器(event_base)和事件(event)构成。事件是基本操作单元。

  1. 创建反应器

 

struct event_base* pBase = event_base_new();
  1. 创建事件/赋值事件

 

struct event* pEvent = event_new(pBase,fd,what,cb,arg);

或者

 

struct event evt;
event_new(&evt,pBase,fd,what,cb,arg);
  1. 添加事件

 

event_add(pEvent,NULL);
  1. 分发事件

 

event_base_dispatch(pBase);
  1. 释放事件

 

event_free(pEvent);
  1. 释放反应器

 

event_base_free(pBase);

事件状态

No.状态说明对应接口
1已初始化(initialized)初始化事件后的状态调用event_new()/event_assign()
2未决状态(pending)添加事件后的状态调用event_add()
3激活状态(active)触发事件发生回调cb

持久(persisitent)

7. 函数

  • 反应器
No.功能函数
1创建反应器struct event_base* event_base_new();
2释放反应器void event_base_free(struct event_base* base);
3分发事件int event_base_dispatch(struct event_base* base);
  • 事件
No.功能函数
1创建事件struct event* event_new(struct event_base* base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg)
2释放事件void event_free(struct event* ev);
3赋值事件int event_assign(struct event* ev,struct event_base* base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg);
4添加事件int event_add(struct event* ev,const struct timeval *tv);
5删除事件int event_del(struct event* ev);
6未决事件int event_pending(struct event* ev,short what,struct timeval *tv_out);

7.1 反应器函数

7.1.1 创建反应器

 

struct event_base*  event_base_new();
  • 返回值
No.参数说明
1NULL失败
2NULL反应器指针

7.1.2 释放反应器

 

void event_base_free(struct event_base* base);
  • 参数
No.参数说明
1base反应器指针

7.1.3 分发事件

 

int  event_base_dispatch(struct event_base* base);
  • 参数
No.参数说明
1base反应器指针
  • 返回值
No.参数说明
1-1失败
20成功

7.2 事件函数

7.2.1 创建事件

 

struct event* event_new(struct event_base* base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg)
  • 参数
No.参数说明
1base反应器指针
2fd文件描述符
3what事件类型
4cb事件回调函数
5arg事件回调函数参数

对于文件描述符fd,如果没有文件描述符传入-1,例如定时器事件。如果是系统事件,传入对应的事件宏定义。

  • 事件类型
No.事件类型说明
1EV_TIMEOUT超时事件0x01
2EV_READ读事件0x02
3EV_WRITE写事件0x03
4EV_SIGNAL信号事件0x04
5EV_PERSIST持久配置0x05
6EV_ET读写的边沿触发事件0x06
  • event_callback_fn

 

typedef void (*event_callback_fn)(evutil_socket_t,short,void*);
  • 返回值
No.参数说明
1NULL失败
2NULL事件指针

7.2.2 释放事件

 

void event_free(struct event* ev);
  • 参数
No.参数说明
1ev事件指针

7.2.3 赋值事件

 

int event_new(struct event* evt,struct event_base* base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg);
  • 参数
    除了第一个参数表示需要赋值的事件外,其他的与event_new()相同。

  • 返回值

No.参数说明
1-1失败
20成功

7.2.4 添加事件

 

int event_add(struct event* ev,const struct timeval *tv);
  • 参数
No.参数说明
1ev事件指针
2tv指定超时事件,NULL表示永不超时
  • 返回值
No.参数说明
1-1失败
20成功

7.2.5 删除事件

 

int event_del(struct event* ev);
  • 参数
No.参数说明
1ev事件指针
  • 返回值
No.参数说明
1-1失败
20成功

7.2.6 未决事件

 

int event_pending(struct event* ev,short what,struct timeval *tv_out);
  • 参数
No.参数说明
1ev事件指针
2what事件类型
2tv_out返回超时事件
  • 返回值
No.参数说明
1非零0未决状态的事件类型

8. 示例

libevent事件有两种创建方式:堆上创建和非堆(栈或者全局变量)上创建。

下面通过下面几个案例二者的区别。

8.1 读取控制台数据

  1. 堆上创建读取事件

 

#include <stdlib.h>  
#include <stdio.h>   
#include <unistd.h>
#include <event2/event.h>  

void onRead(evutil_socket_t fd, short event, void *arg)  { 
    char buf[BUFSIZ];
    read(fd,buf,BUFSIZ);
    printf("%s\n",buf);  
}   
      
int main()  {   
    // 初始化反应器   
    struct event_base* pBase = event_base_new();   

    // 创建事件  
    struct event* pEvent = NULL;
    pEvent = event_new(pBase,STDIN_FILENO,EV_READ,onRead,NULL);

    // 添加事件   
    event_add(pEvent,NULL);   
      
    // 事件循环   
    event_base_dispatch(pBase);   

    // 释放事件
    event_free(pEvent);

    // 释放反应器
    event_base_free(pBase);      
    return 0;   
}  
  1. 非堆上创建读取事件

 

#include <stdlib.h>  
#include <stdio.h>   
#include <unistd.h>
#include <event2/event.h>  
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本

void onRead(evutil_socket_t fd, short event, void *arg)  { 
    char buf[BUFSIZ];
    read(fd,buf,BUFSIZ);
    printf("%s\n",buf);  
}   
      
int main()  {   
    // 初始化反应器   
    struct event_base* pBase = event_base_new();   

    // 创建事件  
    struct event evt;
    event_assign(&evt,pBase,STDIN_FILENO,EV_READ,onRead,NULL);

    // 添加事件   
    event_add(&evt,NULL);   
      
    // 事件循环   
    event_base_dispatch(pBase);   

    // 不需要释放事件

    // 释放反应器
    event_base_free(pBase);      
    return 0;   
}  
  • 两种方式进行比较

     

8.2 定时器事件

  1. 堆上创建定时器事件

 

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>

// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg)  {
    printf("Hello,World!\n");
    struct event** ppEvent = (struct event**)(arg);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 重新添加定时事件(定时事件触发后默认自动删除)
    event_add(*ppEvent, &tv);
}

int main()  {
    // 初始化反应器
    struct event_base* pBase = event_base_new();

    // 创建定时事件
    struct event* pEvent = NULL;
    pEvent = event_new(pBase,-1,EV_TIMEOUT,onTime,&pEvent);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 添加定时事件
    event_add(pEvent, &tv);

    // 事件循环
    event_base_dispatch(pBase);

    // 释放事件
    event_free(pEvent);

    // 释放反应器
    event_base_free(pBase);
    return 0;
}
  1. 非堆栈上创建定时器事件

 

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本

// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg)  {
    printf("Hello,World!\n");
    struct event* pEvent = (struct event*)(arg);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 重新添加定时事件(定时事件触发后默认自动删除)
    event_add(pEvent, &tv);
}

int main()  {
    // 初始化反应器
    struct event_base* pBase = event_base_new();

    // 创建定时事件
    struct event evt;
    event_assign(&evt,pBase,-1,EV_TIMEOUT,onTime,&evt);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 添加定时事件
    event_add(&evt, &tv);

    // 事件循环
    event_base_dispatch(pBase);

    // 释放反应器
    event_base_free(pBase);
    return 0;
}

二者比较

  1. 持续化处理
    默认处理是不持续的,即事件只能使用一次,可以通过设置EV_PERSIST把事件设置成持续的。

堆栈上创建定时器事件

 

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>

void onTime(evutil_socket_t fd, short event, void *arg)  {
    printf("Hello,World!\n");
}

int main()  {
    // 初始化反应器
    struct event_base* pBase = event_base_new();

    // 创建定时事件
    struct event* pEvent = NULL;
    pEvent = event_new(pBase,-1,EV_TIMEOUT|EV_PERSIST,onTime,NULL);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 添加定时事件
    event_add(pEvent, &tv);

    // 事件循环
    event_base_dispatch(pBase);

    // 释放事件
    event_free(pEvent);

    // 释放反应器
    event_base_free(pBase);
    return 0;
}

非堆栈上创建定时器事件

 

 

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本

// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg)  {
    printf("Hello,World!\n");
}

int main()  {
    // 初始化反应器
    struct event_base* pBase = event_base_new();

    // 创建定时事件
    struct event evt;
    event_assign(&evt,pBase,-1,EV_TIMEOUT|EV_PERSIST,onTime,NULL);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 添加定时事件
    event_add(&evt, &tv);

    // 事件循环
    event_base_dispatch(pBase);

    // 不需要释放事件

    // 释放反应器
    event_base_free(pBase);
    return 0;
}

8.3 信号事件

堆栈上创建信号事件

 

#include <stdio.h>
#include <event2/event.h>
#include <signal.h>

void signal_cb(evutil_socket_t fd, short event, void *arg){
        struct event *signal = (struct event*)arg;
        printf("signal_cb: got signal %d\n", event_get_signal(signal));
}
int main(int argc, char **argv) {
    // 初始化反应器
    struct event_base* pBase = event_base_new();

    // 创建信号事件
    struct event *pEvent = event_new(pBase, SIGINT,EV_SIGNAL,signal_cb,event_self_cbarg());

    // 添加信号事件
    event_add(pEvent, NULL);

    // 事件循环
    event_base_dispatch(pBase);

    // 释放事件
    event_free(pEvent);

    // 释放反应器
    event_base_free(pBase);

    return 0;
}

非堆栈上创建信号事件

 

#include <stdio.h>
#include <event2/event.h>
#include <signal.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本

void signal_cb(evutil_socket_t fd, short event, void *arg){
        struct event *signal = (struct event*)arg;
        printf("signal_cb: got signal %d\n", event_get_signal(signal));
}
int main(int argc, char **argv) {
    // 初始化反应器
    struct event_base* pBase = event_base_new();

    // 创建信号事件
    struct event evt;
    event_new(&evt,pBase, SIGINT,EV_SIGNAL,signal_cb,event_self_cbarg());

    // 添加信号事件
    event_add(&evt, NULL);

    // 事件循环
    event_base_dispatch(pBase);

    // 不需要释放事件

    // 释放反应器
    event_base_free(pBase);

    return 0;
}

8.4 FIFO读事件

 

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <event2/event.h>

static void fifo_read(evutil_socket_t fd, short event, void *arg)
{
    char buf[255];
    int len;
    struct event *ev = (struct event*)arg;
    fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
        (int)fd, event, arg);

    // 读取管道
    len = read(fd, buf, sizeof(buf) - 1);

    if (len <= 0) {
        if (len == -1)
            perror("read");
        else if (len == 0)
            fprintf(stderr, "Connection closed\n");
        
        // 删除事件
        event_del(ev);

        // 退出事件循环
        event_base_loopbreak(event_get_base(ev));
        return;
    }

    // 打印管道
    buf[len] = '\0';
    fprintf(stdout, "Read: %s\n", buf);
}

int main(int argc, char **argv) {
    const char *fifo = argv[1];
    // 创建命名管道
    unlink(fifo);
    if (mkfifo(fifo, 0600) == -1) {
        perror("mkfifo");
        exit(1);
    }

    // 打开命名管道
    int fifo_fd = open(fifo, O_RDONLY | O_NONBLOCK, 0);
    if (fifo_fd == -1) {
        perror("open");
        exit(1);
    }

    // 初始化反应器 
    struct event_base* base = event_base_new();

    // 创建FIFO读事件
    struct event* evfifo = event_new(base, fifo_fd, EV_READ|EV_PERSIST, fifo_read,
                           event_self_cbarg());

    // 添加FIFO读事件
    event_add(evfifo, NULL);

    // 事件循环
    event_base_dispatch(base);

    // 释放反应器
    event_base_free(base);

    // 关闭管道
    close(fifo_fd);

    // 删除管道
    unlink(fifo);
    return (0);
}

9. 定时器宏定义

对定时器事件,libevent提供了几个以evtimer_开头的宏定义,简化代码。

 

// 创建定时器事件
#define evtimer_new(base,callback,arg) \
    event_new((base),-1,0,(callback),(arg))
// 赋值定时器事件
#define evtimer_assign(event,base,callback,arg) \
    event_assign((event),(base),-1,0,(callback),(arg))
// 添加定时器事件
#define evtimer_add(ev,tv) \
    event_add((ev),(tv))
// 删除定时器事件
#define evtimer_del(ev) \
    event_add(ev)
// 未决定时器事件
#define evtimer_pending(ev,what,tv_out) \
    event_pending((ev),(what),(tv_out))

使用宏定义代替

 

    // 创建定时事件
    struct event* pEvent = NULL;
    pEvent = event_new(pBase,-1,EV_TIMEOUT,onTime,&pEvent);

    // 添加定时事件
    event_add(pEvent, &tv);

代替

 

    // 创建定时事件
    struct event* pEvent = NULL;
    pEvent = evtimer_new(pBase,onTime,&pEvent);
  
    // 添加定时事件
    evtimer_add(pEvent, &tv);
  • 代码

 

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
#include <assert.h>

// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg)  {
    printf("Hello,World!\n");
    assert(NULL != arg);
    struct event** ppEvent = (struct event**)(arg);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 重新添加定时事件(定时事件触发后默认自动删除)
    evtimer_add(*ppEvent, &tv);
}

int main()  {
    // 初始化反应器
    struct event_base* pBase = event_base_new();

    // 创建定时事件
    struct event* pEvent = NULL;
    pEvent = evtimer_new(pBase,onTime,&pEvent);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 添加定时事件
    evtimer_add(pEvent, &tv);

    // 事件循环
    event_base_dispatch(pBase);

    // 释放事件
    event_free(pEvent);

    // 释放反应器
    event_base_free(pBase);
    return 0;
}

使用宏定义代替

 

    // 创建定时事件
    struct event evt;
    event_assign(&evt,pBase,-1,EV_TIMEOUT|EV_PERSIST,onTime,NULL);

    // 添加定时事件
    event_add(&evt, &tv);

代替

 

    // 创建定时事件
    struct event evt;
    evtimer_assign(&evt,pBase,onTime,&evt);

    // 添加定时事件
    evtimer_add(&evt, &tv);
  • 代码

 

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <event2/event.h>
#include <event2/event_struct.h> // 注意:可能会出现不兼容后期版本

// 定时事件回调函数
void onTime(evutil_socket_t fd, short event, void *arg)  {
    struct event* pEvent = (struct event*)(arg);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 重新添加定时事件(定时事件触发后默认自动删除)
    evtimer_add(pEvent, &tv);
}

int main()  {
    // 初始化反应器
    struct event_base* pBase = event_base_new();

    // 创建定时事件
    struct event evt;
    evtimer_assign(&evt,pBase,onTime,&evt);

    // 定时间隔
    struct timeval tv;
    evutil_timerclear(&tv);
    tv.tv_sec = 1;

    // 添加定时事件
    evtimer_add(&evt, &tv);

    // 事件循环
    event_base_dispatch(pBase);

    // 释放反应器
    event_base_free(pBase);
    return 0;
}

10. 信号宏定义

对信号事件,libevent提供了几个以evsignal_开头的宏定义,简化代码。

 

#define evsignal_new(base,signum,cb,arg) \
    event_new(base,signum,EV_SIGNAL|EV_PERSIST,cb,arg)
#define evsignal_assign(ev,base,signum,cb,arg) \
    event_assign(ev,base,signum,EV_SIGNAL|EV_PERSIST,cb,arg)
#define evsignal_add(ev,tv) \
    event_add((ev),(tv))
#define evsignal_del(ev) \
    event_del(ev)
#define evsignal_pending(ev,what,tv_out) \
    event_pending((ev),(what),(tv_out))
  • 示例

 

#include <stdio.h>
#include <event2/event.h>
#include <signal.h>

void signal_cb(evutil_socket_t fd, short event, void *arg){
        struct event *signal = (struct event*)arg;
        printf("signal_cb: got signal %d\n", event_get_signal(signal));
}
int main(int argc, char **argv) {
        struct event_base* base = event_base_new();
        struct event *signal_int = evsignal_new(base, SIGINT, signal_cb, event_self_cbarg());
        event_add(signal_int, NULL);
        event_base_dispatch(base);
        event_free(signal_int);
        event_base_free(base);
}

11. 废弃/老版的函数

头文件:event.h

No.函数功能
1event_init()初始化事件
2event_dispatch()事件分发
3event_set()初始化设置事件
  • 示例

 

#include <stdlib.h>  
#include <stdio.h>  
#include <sys/time.h>  
#include <event.h>  
 
// 定时事件回调函数   
void onTime(int sock, short event, void *arg)  {   
    printf("Hello,World!\n");  
    
    // 事件间隔
    struct timeval tv;   
    tv.tv_sec = 1;   
    tv.tv_usec = 0;   
     
    // 重新添加定时事件(定时事件触发后默认自动删除)   
    event_add((struct event*)arg, &tv);   
}   
      
int main()  {   
    // 初始化   
    event_init();   
      
    // 设置定时事件  
    struct event ev_time;    
    evtimer_set(&ev_time, onTime, &ev_time);   
      
    // 事件间隔
    struct timeval tv;   
    tv.tv_sec = 1;   
    tv.tv_usec = 0;   

    // 添加定时事件   
    event_add(&ev_time, &tv);   
      
    // 事件循环   
    event_dispatch();   
      
    return 0;   
}  

new event_baseevent_init()区别:event_init()是创建一个全局的new event_base

实现相同的功能,即能用系统函数,又能用第三方库,使用那种方式实现?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值