zoukankan      html  css  js  c++  java
  • Libevent库介绍

    1.libevent介绍

    简介

    基本的socket变成是阻塞/同步的,每个操作除非已经完成,出错,或者超时才会返回,这样对于每一个请求,要使用一个线程或者单独的进程去处理,系统资源没有办法支撑大量的请求。posix定义了可以使用异步的select系统调用,但是因为它采用了轮询的方式来判断某个fd是否变成active,效率不高。于是各系统就分别提出了基于异步的系统调用,例如Linux的epoll,由于在内核层面做了支持,所以可以用O(1)的效率查找到active的fd。基本上,libevent就是对这些高效IO的封装,提供统一的API,简化开发。

    原理简介

    libevent默认情况下是单线程的,可以配置成多线程,每个线程有且只有一个event_base,对应一个struct event_base结构体以及附于其上的事件管理器,用来调度托管给它的一系列event,可以和操作系统的进程管理类比。当一个事件发生后,event_base会在合适的时间,不一定是立即去调用绑定在这个事件上的函数,直到这个函数执行完,再去调度其他的事件。

    1 //创建一个event_base
    2 struct event_base *base = event_base_new();
    3 assert(base != NULL);
    event_base内部有一个循环,循环阻塞在epoll等系统调用上,直到有一个/一些时间发生,然后去处理这些事件。当然,这些事件要被绑定在这个event_base上,每个事件对应一个struct event,可以是监听一个fd或者信号量之类的,struct event使用event_new来创建和绑定,使用event_add来将event绑定到event_base中。
    1 // 创建并绑定一个event
    2 struct event* listen_event;
    3 
    4 //参数:event_base,监听的对象,需要监听的事件,事件发生后的回调函数,传给回调函数的参数
    5 listen_event = event_new(base, listener, EV_READ | EV_PERSIST, callback_func, (void*)base);
    6 //参数:event,超时时间,NULL表示无超时设置
    7 event_add(listen_event, NULL);

    注:libevent支持的事件及属性包括(使用bitfield实现)

    1. EV_TIMEOUT:超时;
    2. EV_READ:只要网络缓冲中还有数据,回调函数就会被触发;
    3. EV_WRITE:只要塞给网络缓冲的数据被写完,回调函数就会被触发;
    4. EV_SIGNAL:POSIX信号量;
    5. EV_PERSIST:不指定这个属性,回调函数被触发后事件会被删除;
    6. EV_ET:Edge-Trigger边缘触发(这个还不懂是什么意思)

    然后启动event_base的循环,开始处理事件。循环地启动使用event_base_dispatch,循环将一直持续,找到不再有需要关注的事件,或者是遇到event_loopbreak()/event_loopexit()函数。

    //启动循环,开始处理事件
    event_base_dispatch(base);

    接下来再来关注事件发生时的回调函数callback_func,callback_func的原型如下所示

    typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)

    传给callback_func的是一个监听的fd,监听的事件类型,以及event_new中最后一个参数。在上述程序中,是将event_base传给了callback_func,实际中更常用的是构造一个结构体,把需要传给回调函数的参数都放进来,然后传给event_new,event_new再传给回调函数。

    所以总结一下,对于一个服务器而言,流程大致如下:

    1. 获取待监听的内容的fd;
    2. 创建一个event_base;
    3. 创建一个event,指定待监听的fd,待监听事件的类型,以及事件放生时的回调函数及传给回调函数的参数;
    4. 将event添加到event_base的事件管理器中;
    5. 开启event_base的事件处理循环;
    6. (异步)当事件发生的时候,调用前面设置的回调函数。

    2.libevent 使用流程

    使用流程

    1. 创建一个事件处理框架
    2. 创建一个事件
    3. 事件添加到处理框架
    4. 开始事件循环
    5. 释放资源

    事件处理框架 - event_base

    1. 使用libevent函数之前需要分配一个或者多个event_base结构体. 每个event_base结构体有一个事件集合,可以检测以确定哪个事件是激活的.

      • 相当于epoll红黑树的树根
      • 底座
      • 抽象层,完成对event_base的封装
      • 每个event_base都有一种用于检测那种事件已经就绪的"方法",或者说后端
    2. 具体操作

      • 创建event_base
        • struct event_base* event_base_new(void);
        • 失败返回NULL
      • 释放event_base
        • event_base_free(struct event_base* base);
      • 循环监听base对应的事件, 等待条件满足
        • event_base_dispatch(struct event_base* base);
    3. 查看event_base封装的后端(当前系统中支持那些函数)

      • const char **event_get_supported_methods();
      • 返回一个字符串数组中
      • const char * event_base_get_method(const struct event_base *base);
      • 返回当前使用的IO转接
    4. event_base和fork

      • 子进程创建成功后,父进程可以继续使用event_base
      • 子进程中需要继续使用event_base需要重新进行初始化
        • int event_reinit(struct event_base * base)

    事件 - event

    1. 创建新事件
    
    #define  EV_TIMEOUT         0x01    // 废弃
    #define  EV_READ            0x02
    #define  EV_WRITE           0x04
    #define  EV_SIGNAL          0x08
    #define  EV_PERSIST         0x10    // 持续触发
    #define  EV_ET              0x20    // 边沿模式
    
    typedef void(*event_callback_fn)(evutil_sockt_t,short,void *);
    
    struct event *event_new(
        struct event_base *base,
        evutil_socket_t fd,     // 文件描述符-int
        shord what,
        event_callback_fn cb,   // 事件处理动作
        void *arg
    );
    
    1. 释放事件

      • void event_free(struct event *event);
    2. 设置未决事件(有资格但是没有被处理的事件)

      • 构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到event_base, 非未决事件 -> 未决事件.
      • 函数
        int event_add(
            struct event *ev, 
            const struct timeval *tv
        ); 
        
      • tv:
        • NULL: 事件被触发, 对应的回调被调用
        • tv = {0, 100}, 如果设置的时间,
          • 在改时间段内检测的事件没被触发, 时间到达之后, 回调函数还是会被调用
      • 函数调用成功返回0, 失败返回-1
    3. 设置非未决(还没有资格被处理的事件)

      • int event_del(struct event *ev);
      • 对已经初始化的事件调用 event_del()将使其成为非未决和非激活的。如果事件不是未决的或者激活的,调用将没有效果。成功时函数返回 0,失败时返回-1。

    事件循环

    1. 开始循环

      • 一旦有了一个已经注册了某些事件的 event_base, 就需要让 libevent 等待事件并且通知事件的发生。
      #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);
        • 正常退出返回0, 失败返回-1
      • event_base_dispatch(struct event_base* base)
        • 等同于没有设置标志的 event_base_loop()
        • 将一直运行,直到没有已经注册的事件了,或者调用 了event_base_loopbreak()或者 event_base_loopexit()为止。
    2. 停止循环

      • 返回值:成功0,失败-1
      struct timeval{
          long tv_sec;
          long tv_usec;
      };
      
      • 如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出
      int event_base_loopexit(
          struct event_base *base,
          const struct timeval *tv
      );
      
      • 让event_base 立即退出循环
      int event_base_loopbreak(struct event_base *base);

    3.实例演示

      1 /*
      2   This exmple program provides a trivial server program that listens for TCP
      3   connections on port 9995.  When they arrive, it writes a short message to
      4   each client connection, and closes each connection once it is flushed.
      5 
      6   Where possible, it exits cleanly in response to a SIGINT (ctrl-c).
      7 */
      8 
      9 
     10 #include <string.h>
     11 #include <errno.h>
     12 #include <stdio.h>
     13 #include <signal.h>
     14 #ifndef WIN32
     15 #include <netinet/in.h>
     16 # ifdef _XOPEN_SOURCE_EXTENDED
     17 #  include <arpa/inet.h>
     18 # endif
     19 #include <sys/socket.h>
     20 #endif
     21 
     22 #include <event2/bufferevent.h>
     23 #include <event2/buffer.h>
     24 #include <event2/listener.h>
     25 #include <event2/util.h>
     26 #include <event2/event.h>
     27 
     28 static const char MESSAGE[] = "Hello, World!
    ";
     29 
     30 static const int PORT = 9995;
     31 
     32 static void listener_cb(struct evconnlistener *, evutil_socket_t,
     33     struct sockaddr *, int socklen, void *);
     34 static void conn_writecb(struct bufferevent *, void *);
     35 static void conn_eventcb(struct bufferevent *, short, void *);
     36 static void signal_cb(evutil_socket_t, short, void *);
     37 
     38 int
     39 main(int argc, char **argv)
     40 {
     41     struct event_base *base;
     42     struct evconnlistener *listener;
     43     struct event *signal_event;
     44 
     45     struct sockaddr_in sin;
     46 #ifdef WIN32
     47     WSADATA wsa_data;
     48     WSAStartup(0x0201, &wsa_data);
     49 #endif
     50 
     51     base = event_base_new();  //创建base
     52     if (!base) {
     53         fprintf(stderr, "Could not initialize libevent!
    ");
     54         return 1;
     55     }
     56 
     57     memset(&sin, 0, sizeof(sin));
     58     sin.sin_family = AF_INET;
     59     sin.sin_port = htons(PORT);
     60 
     61     listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
     62         LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
     63         (struct sockaddr*)&sin,
     64         sizeof(sin));  //添加事件
     65 
     66     if (!listener) {
     67         fprintf(stderr, "Could not create a listener!
    ");
     68         return 1;
     69     }
     70 
     71     signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base); //添加信号事件
     72 
     73     if (!signal_event || event_add(signal_event, NULL)<0) {
     74         fprintf(stderr, "Could not create/add a signal event!
    ");
     75         return 1;
     76     }
     77 
     78     event_base_dispatch(base);
     79 
     80     evconnlistener_free(listener);
     81     event_free(signal_event);
     82     event_base_free(base);
     83 
     84     printf("done
    ");
     85     return 0;
     86 }
     87 
     88 static void
     89 listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
     90     struct sockaddr *sa, int socklen, void *user_data)
     91 {
     92     struct event_base *base = user_data;
     93     struct bufferevent *bev;
     94 
     95     bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
     96     if (!bev) {
     97         fprintf(stderr, "Error constructing bufferevent!");
     98         event_base_loopbreak(base);
     99         return;
    100     }
    101     bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
    102     bufferevent_enable(bev, EV_WRITE);
    103     bufferevent_disable(bev, EV_READ);
    104 
    105     bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
    106 }
    107 
    108 static void
    109 conn_writecb(struct bufferevent *bev, void *user_data)
    110 {
    111     struct evbuffer *output = bufferevent_get_output(bev);
    112     if (evbuffer_get_length(output) == 0) {
    113         printf("flushed answer
    ");
    114         bufferevent_free(bev);
    115     }
    116 }
    117 
    118 static void
    119 conn_eventcb(struct bufferevent *bev, short events, void *user_data)
    120 {
    121     if (events & BEV_EVENT_EOF) {
    122         printf("Connection closed.
    ");
    123     } else if (events & BEV_EVENT_ERROR) {
    124         printf("Got an error on the connection: %s
    ",
    125             strerror(errno));/*XXX win32*/
    126     }
    127     /* None of the other events can happen here, since we haven't enabled
    128      * timeouts */
    129     bufferevent_free(bev);
    130 }
    131 
    132 static void
    133 signal_cb(evutil_socket_t sig, short events, void *user_data)
    134 {
    135     struct event_base *base = user_data;
    136     struct timeval delay = { 2, 0 };
    137 
    138     printf("Caught an interrupt signal; exiting cleanly in two seconds.
    ");
    139 
    140     event_base_loopexit(base, &delay);
    141 }

    参考链接:https://www.jianshu.com/p/8ea60a8d3abb

         https://www.cnblogs.com/joker-wz/p/10735410.html

  • 相关阅读:
    java实验报告(实验五)
    java实验报告(实验三)
    java读书笔记二
    总结报告
    Android实践项目汇报(总结)-修改
    Android实践项目汇报(总结)
    Android实践项目汇报(四)
    Android实践项目汇报(三)
    Android实践项目汇报(二)
    Android实践项目汇报-改(一)
  • 原文地址:https://www.cnblogs.com/single-dont/p/12866970.html
Copyright © 2011-2022 走看看