• libevent (三) 事件注册与循环监听


    事件注册与循环监听

    在libevent中为了监听某种事件的发生,设置事件触发后的回调函数,也就是说对该事件注册到当前的IO模型中。

    事件注册

    事件初始化

    使用`event_new`函数来对事件进行初始化。

    typedef void (*event_callback_fn)(evutil_socket_t, short, void *);/* 回调函数 */
    
    struct event *event_new(struct event_base *base, evutil_socket_t fd,
        short what, event_callback_fn cb,
        void *arg);
    
    void event_free(struct event *event);
    /*
     * base:event_base类型,event_base_new的返回值
     * fd:监听的fd,listen的fd
     * what:事件的类型及属性
     * cb:绑定的回调函数
     * arg:给回调函数的参数
     */

    其中,事件类型及属性如下:

    #define EV_TIMEOUT 0x01 /*定时事件*/
    #define EV_READ 0x02 /*I/O事件*/
    #define EV_WRITE 0x04 /*I/O事件*/
    #define EV_SIGNAL 0x08 /*信号*/
    #define EV_PERSIST 0x10 /*永久事件 */
    #define EV_ET 0x20 /*边沿触发*/

    此外,还有一个函数`event_assgin`,它多了一个event参数:

    int event_assign(struct event *event, struct event_base *base,
        evutil_socket_t fd, short what,
        void (*callback)(evutil_socket_t, short, void *), void *arg);

    注册事件

    虽然已经初始化了事件,但是该事件并不会被触发,原因在于我们并没有激活该事件。

    `event_add`函数提供了激活事件的功能。

    int event_add(struct event *ev, const struct timeval *tv);

    如果是一个(non-pending)未注册`ev`,调用`event_add`函数会注册该事件(变为pending状态)。如果是一个(pending)注册过的`ev`,调用该函数会在tv时间后重新注册该事件。成功返回0,失败返回-1。

    例子

    #include <event2/event.h>
    
    void cb_func(evutil_socket_t fd, short what, void *arg)
    {
            const char *data = arg;
            printf("Got an event on socket %d:%s%s%s%s [%s]",
                (int) fd,
                (what&EV_TIMEOUT) ? " timeout" : "",
                (what&EV_READ)    ? " read" : "",
                (what&EV_WRITE)   ? " write" : "",
                (what&EV_SIGNAL)  ? " signal" : "",
                data);
    }
    
    void main_loop(evutil_socket_t fd1, evutil_socket_t fd2)
    {
            struct event *ev1, *ev2;
            struct timeval five_seconds = {5,0};
            struct event_base *base = event_base_new();
    
            /* The caller has already set up fd1, fd2 somehow, and make them
               nonblocking. */
          /* 初始化事件 */
            ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func,(char*)"Reading event");
            ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func,(char*)"Writing event");
            /* 注册事件 */
            event_add(ev1, &five_seconds);
            event_add(ev2, NULL);
            /* 循环监听 */
            event_base_dispatch(base);
    }

    查找正在运行的事件

    struct event *event_base_get_running_event(struct event_base *base);

    设置事件执行一次

    该函数和`event_base`类似,但是不支持`EV_SIGNAL or EV_PERSIST`。
    int event_base_once(struct event_base *, evutil_socket_t, short,void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);

    激活事件

    如果想自己激活某个事件,那么可以执行下面的函数:
    void event_active(struct event *ev, int what, short ncalls);
    /*
     * 参数ev为要激活的事件 
     * what可以为EV_READ, EV_WRITE, and EV_TIMEOUT
     * ncalls为激活次数
     */

    其他函数

    #define evtimer_new(base, callback, arg) 
        event_new((base), -1, 0, (callback), (arg))
    #define evtimer_add(ev, tv) 
        event_add((ev),(tv))
    #define evtimer_del(ev) 
        event_del(ev)
    #define evtimer_pending(ev, tv_out) 
        event_pending((ev), EV_TIMEOUT, (tv_out))

    循环监听

    当已经拥有注册了IO复用方法的`event_base`后,可以通过`event_loop`来监听并接受IO事件。

    打开event loop

    //@param: flags
    //等待IO事件
    #define EVLOOP_ONCE 0x01
    //非阻塞,直接返回
    #define EVLOOP_NONBLOCK 0x02 
    //其他的线程中添加事件
    #define EVLOOP_NO_EXIT_ON_EMPTY 0x04
    
    int event_base_loop(struct event_base *base, int flags);

    `event_loop`会检查所有IO复用方法的状态,一旦启动就会激活并使用这些IO复用方法。

    如果不使用flag,可以条用下面这个函数,功能等同于`event_base_loop`,调用该函数会一直阻塞在这里,等待时间的触发。
    int event_base_dispatch(struct event_base *base);

    关闭event loop

    //延迟tv时间后,停止event loop
    int event_base_loopexit(struct event_base *base, const struct timeval *tv);
    //立刻停止,等同于tv = NULL
    int event_base_loopbreak(struct event_base *base);
    
    //获取退出方式
    int event_base_got_exit(struct event_base *base);
    int event_base_got_break(struct event_base *base);

    debug

    这个函数可以把`event_base`中信息和状态写入文件中。
    void event_base_dump_events(struct event_base *base, FILE *f);

    例子

    #include <sys/types.h>
    #include <event2/event-config.h>
    #include <sys/stat.h>
    #ifndef WIN32
    #include <sys/queue.h>
    #include <unistd.h>
    #endif
    #include <time.h>
    #ifdef _EVENT_HAVE_SYS_TIME_H
    #include <sys/time.h>
    #endif
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    
    #include <event2/event.h>
    #include <event2/event_struct.h>
    #include <event2/util.h>
    
    
    struct timeval lasttime;
    
    int event_is_persistenet;
    
    static void
    timeout_cb(evutil_socket_t fd, short event, void *arg)
    {
        struct timeval newtime, difference;
        struct event *timeout = arg;
        double elapsed;
    
        evutil_gettimeofday(&newtime, NULL);
        evutil_timersub(&newtime, &lasttime, &difference);
        elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6);
    
        printf("timeout_cb called at %d: %.3f seconds elapsed.
    ",
        (int)newtime.tv_sec, elapsed);
        lasttime = newtime;
    
        if (! event_is_persistent) {
            struct timeval tv;
            evutil_timerclear(&tv);
            tv.tv_sec = 2;
            event_add(timeout, &tv);
        }
    }
    
    int main(int argc, char **argv)
    {
        struct event timeout;
        struct timeval tv;
        struct event_base *base;
        int flags;
    
        if (argc == 2 && !strcmp(argv[1], "-p")) {
            event_is_persistent = 1;
            flags = EV_PERSIST;
        } else {
            event_is_persistent = 0;
            flags = 0;
        }
    
        /* Initalize the event library */
        base = event_base_new();
    
        /* Initalize one event */
        event_assign(&timeout, base, -1, flags, timeout_cb, (void*)&timeout);
    
        evutil_timerclear(&tv);
        tv.tv_sec = 2;
        event_add(&timeout, &tv);
    
        evutil_gettimeofday(&lasttime, NULL);
    
        event_base_dispatch(base);
    
        return (0);
    }    
  • 相关阅读:
    CSDNReader(android客户端)发布!!
    linux下的C语言快速学习—从1+1开始。
    linux下的C语言快速学习—进程和文件
    ListView动态加载数据分页(使用Handler+线程和AsyncTask两种方法)
    CSDN阅读器(android版)开发总结
    算法实现将一个输入的数字颠倒(输入12345>54321)
    linux下的C语言快速学习—计算机体系结构基础简单了解
    实现一个字符串查找子串的函数
    .net4.0面向对象学习笔记—数据类型
    装饰器模式
  • 原文地址:https://www.cnblogs.com/coder2012/p/4267533.html
Copyright © 2020-2023  润新知