zoukankan      html  css  js  c++  java
  • libevent笔记3:evbuffer

    evbuffer

    之前提到bufferevent结构体提供两个缓存区用来为读写提供缓存,并自动进行IO操作。这两个缓存区是使用Libevent中的evbuffer实现的,同样,Libevent中也提供了相应的函数让我们能够直接操作evbuffer

    evbuffer的回调函数及evbuffer_cb_info结构体

    我们可以为一个evbuffer增加回调函数,回调函数会在evbuffer长度有变化时被调用。evbuffer的回调函数列表中有一个evbuffer_cb_info结构体,可以用它来判断是什么事件触发了回调函数,里面包含了三个关于缓存区长度的元素:

    • size_t orig_size: 表示长度变化前的缓存区长度;
    • size_t n_added: 表示增加的长度;
    • size_t n_deleted: 表示减少的长度;

    读写evbuffer

    除了使用bufferevent_write函数向缓存区读写数据外,也可以使用evbuffer提供的一些函数直接对缓存区进行读写操作。不过需要注意两点:

    • 标志EVBUFFER_FLAG_DRAINS_TO_FD会阻止一般的读操作,只允许数据进入网络;
    • 需要对evbuffer的头尾进行解冻(evbuffer_unfreeze)才能在头尾读写。不过从实验的结果来看,在调用对evbuffer尾增加数据的函数时,不需要额外进行冻结/解冻操作(函数listener_cb中),而在evbuffer头移除数据时需要解冻/冻结操作(函数evbuffer_cb中)。

    缓存区Demo

    #include <arpa/inet.h>
    #include <event2/listener.h>
    #include <event2/bufferevent.h>
    #include <event2/buffer.h>
    #include <event2/event.h>
    #include <event2/util.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <signal.h>
    //事件回调函数,处理bufferevent的事件
    static void conn_eventcb(struct bufferevent *bev, short events, void *ptr)
    {
       if(events & BEV_EVENT_EOF)
       {
          printf("client has closed the connection!
    ");
       }
       if(events & BEV_EVENT_ERROR)
       {
          printf("got an error on the connection: %s
    ", strerror(errno));
       }
       bufferevent_free(bev);
    }
    //缓存区发生变化时的回调函数
    static void evbuffer_cb(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *ptr)
    {
       if(info->n_added > 0)//当缓存区增加时
       {
          printf("The output buffer has added %ld bytes
    ", info->n_added);
          evbuffer_unfreeze(buffer, 1);
    
          evbuffer_clear_flags(buffer, EVBUFFER_FLAG_DRAINS_TO_FD);
          evbuffer_drain(buffer, 1);
    
          evbuffer_unfreeze(buffer, 1);   
       }
       if(info->n_deleted > 0)//当缓存区减少时
       {
          printf("The output buffer has deleted %ld bytes
    ", info->n_deleted);
       }
    }
    //设置evbuffer的回调函数
    static void evbuffer_set(struct bufferevent *bev)
    {
       struct evbuffer *output = bufferevent_get_output(bev);
    
       struct evbuffer_cb_entry *evbuffer_callback = evbuffer_add_cb(output, evbuffer_cb, NULL);
    }
    //监听回调函数
    static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    		struct sockaddr *sa, int socklen, void *ptr)
    {
       struct event_base *base = ptr;
       struct bufferevent *bev = NULL;
    
       bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    
       bufferevent_setcb(bev, NULL, NULL, conn_eventcb, NULL);
    
       bufferevent_enable(bev, EV_WRITE);
       bufferevent_disable(bev, EV_READ);
    
       evbuffer_set(bev);
       //打开一个文件,并基于文件创建一个file segment
       int file_segment = open("main.c", O_RDONLY);
       struct evbuffer_file_segment *file_seg = evbuffer_file_segment_new(file_segment, 0, -1, EVBUF_FS_CLOSE_ON_FREE);
       //将文件的的一段加入缓存区
       struct evbuffer *output = bufferevent_get_output(bev);
       evbuffer_add_file_segment(output, file_seg, 0, 10);
    }
    
    static void
    signal_cb(evutil_socket_t sig, short events, void *user_data)
    {
       struct event_base *base = user_data;
       struct timeval delay = { 2, 0 };
       printf("Caught an interrupt signal; exiting cleanly in two seconds.
    ");
       event_base_loopexit(base, &delay);
    }
    
    int main(int argc, char *argv[])
    {
       struct event_base *base = NULL;
       base = event_base_new();
    
       struct sockaddr_in sin;
       memset(&sin, 0, sizeof(sin));
       sin.sin_family = AF_INET;
       sin.sin_port = htons(9995);
    
       struct evconnlistener *listener;
    
       listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
    		   LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, (struct sockaddr *)&sin, sizeof(sin));
    
       struct event *signal_ev;
       signal_ev = evsignal_new(base, SIGINT, signal_cb, (void *)base);
    
       event_add(signal_ev, NULL);
       event_base_dispatch(base);
    
       evconnlistener_free(listener);
       event_free(signal_ev);
       event_base_free(base);
       return 0;
    }
    

    执行过程(信号事件等无关忽略):

    • 首先在main函数中创建一个监听器,监听9995端口;
    • 监听到客户端后创建对应的bufferevent,不设置其读写回调函数,而是设置其输出缓存区的回调函数;
    • 创建一个file_segment,为main.c文件的全部字符;
    • 将file_segment段的前10个字符放入输出缓存区的尾;
    • 因为有数据进入缓存区,回调函数被调用:输出新加入的数据长度,清楚EVBUFFER_FLAG_DRAINS_TO_FD标志,然后解冻缓存区的头部,将第一个数据抛弃,最后重新冻结;
    • 因为有数据被移除,回调函数被触发:输出移除的数据(这次移除由函数evbuffer_drain触发,所以只移除一个);
    • 数据写到底层socket,再次调用回调函数;

    运行结果:

    sunminming@sunminming:~/libevent/evbuffer$ ./main 
    The output buffer has added 10 bytes    //读进文件的前10个字符
    The output buffer has deleted 1 bytes   //抛弃第一个
    The output buffer has deleted 9 bytes   //正式输出剩下九个
    

    另一个终端使用nc命令:

    sunminming@sunminming:~$ nc 127.0.0.1 9995
    include <    //接收到的数据缺少第一个字符‘#’
    
  • 相关阅读:
    ASP.NET-FineUI开发实践-9(四)
    ASP.NET-FineUI开发实践-9(三)
    ASP.NET-FineUI开发实践-9(二)
    ASP.NET-FineUI开发实践-9
    ASP.NET-FineUI开发实践-8(二)
    ASP.NET-FineUI开发实践-8
    ASP.NET-FineUI开发实践-7
    ASP.NET-FineUI开发实践-6(三)
    ASP.NET-FineUI开发实践-6(二)
    ASP.NET-FineUI开发实践-6
  • 原文地址:https://www.cnblogs.com/sunminming/p/11946269.html
Copyright © 2011-2022 走看看