Linux系统编程:libevent

原文链接: https://www.jianshu.com/p/511cd8d73599

 

 

1.简介

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

2. 下载安装

2.1 自动安装

yum install libevent-devel

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

4. 功能

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

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

 

Linux系统编程:libevent_第1张图片

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. 参数 说明
1 NULL 失败
2 NULL 反应器指针

7.1.2 释放反应器

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

7.1.3 分发事件

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

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. 参数 说明
1 base 反应器指针
2 fd 文件描述符
3 what 事件类型
4 cb 事件回调函数
5 arg 事件回调函数参数

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

  • 事件类型
No. 事件类型 说明
1 EV_TIMEOUT 超时事件 0x01
2 EV_READ 读事件 0x02
3 EV_WRITE 写事件 0x03
4 EV_SIGNAL 信号事件 0x04
5 EV_PERSIST 持久配置 0x05
6 EV_ET 读写的边沿触发事件 0x06
  • event_callback_fn
typedef void (*event_callback_fn)(evutil_socket_t,short,void*);
  • 返回值
No. 参数 说明
1 NULL 失败
2 NULL 事件指针

7.2.2 释放事件

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

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 失败
2 0 成功

7.2.4 添加事件

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

7.2.5 删除事件

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

7.2.6 未决事件

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

8. 示例

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

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

8.1 读取控制台数据

  1. 堆上创建读取事件
#include   
#include    
#include 
#include   

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   
#include    
#include 
#include   
#include  // 注意:可能会出现不兼容后期版本

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;   
}  
  • 两种方式进行比较

     

    Linux系统编程:libevent_第2张图片

8.2 定时器事件

  1. 堆上创建定时器事件
#include 
#include 
#include 
#include 

// 定时事件回调函数
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 
#include 
#include 
#include 
#include  // 注意:可能会出现不兼容后期版本

// 定时事件回调函数
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;
}

二者比较

Linux系统编程:libevent_第3张图片

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

Linux系统编程:libevent_第4张图片

堆栈上创建定时器事件

#include 
#include 
#include 
#include 

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;
}

非堆栈上创建定时器事件

 

Linux系统编程:libevent_第5张图片

#include 
#include 
#include 
#include 
#include  // 注意:可能会出现不兼容后期版本

// 定时事件回调函数
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 
#include 
#include 

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 
#include 
#include 
#include  // 注意:可能会出现不兼容后期版本

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

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 
#include 
#include 
#include 
#include 

// 定时事件回调函数
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 
#include 
#include 
#include 
#include  // 注意:可能会出现不兼容后期版本

// 定时事件回调函数
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 
#include 
#include 

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. 函数 功能
1 event_init() 初始化事件
2 event_dispatch() 事件分发
3 event_set() 初始化设置事件
4 event_add() 添加事件
5 event_del() 删除事件
  • 示例
#include   
#include   
#include   
#include   
 
// 定时事件回调函数   
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

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

你可能感兴趣的:(Linux)