现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

libevent框架学习: event_base 重中之重

2018-02-18 22:02 工业·编程 ⁄ 共 4620字 ⁄ 字号 暂无评论

我们可以把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 依次执行即可

给我留言

留言无头像?