Chinaunix首页 | 论坛 | 博客
  • 博客访问: 264940
  • 博文数量: 107
  • 博客积分: 305
  • 博客等级: 二等列兵
  • 技术积分: 417
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-22 09:42
文章分类

全部博文(107)

文章存档

2014年(3)

2013年(41)

2012年(34)

2011年(28)

2008年(1)

分类: LINUX

2013-10-24 13:42:42

原文地址:libevent bufferevents介绍 作者:anqiu1987

概述:

    有时候我们不只是相应一个事件,我们还需要缓存一些数据,基本的操作如下:

  •     我们决定往一个连接输出一些数据,我们会首先把数据放到一个缓存中,
  •     等待文件描述符可写。
  •     往连接中写尽可能多的数据
  •     记住我们写了多少,如果我们没有写完,我们会再次等待连接可写
     bufferevent正是为了这种机制才提供的,一个eventbuffer在内部保存了类似于socket的传输介质,一个读的buffer,一个写的buffer,不同
与传统的event,当传输介质可读可写的时候会调用callback函数,bufferevent在有足够的数据可读可写的时候调用用户提供的callback。
    有不同种类的bufferevent,但是他们有着共同的接口。
  •     基于socket的bufferevent
  •     asynchronous-IO  bufferevent
                  该接口使用windows iocp 完成端口
  •    filtering bufferevent
             该bufferevent对进入的和出去的数据执行一些操作,例如压缩或者解释。
  •     paired bufferevent
             互相通信的一对bufferevent

      注意:暂时bufferevent只是支持面向流的协议例如tcp。

回调函数与水位

       每一个bufferevent都有两个回调函数,一个读的,一个写的。一般来说,当有数据可以读的时候 就会调用读的回调函数,当有足够的数据从从buffer中输出到了底层的介质(例如socket)的时候写的回调函数就是被调用,    你可以自己定义对于读和写的水位(watermarks)。
        read low-mark water
              当bufferevent的input buffer达到或者高于这个水位的时候,回调函数就会被调用,默认是0,所以当有数据可读的时候就会被调用。
       read  high-mark water        
             当bufferevent的input buffer达到这个水位的时候,bufferevent就会停止读取底层数据,知道里面的数据被取走,默认是没有限制的。 
       write   low-mark water
               当bufferevent的output达到或者低于这个水位的时候,回调函数会被调用,默认是0,所以只有output数据为空的时候,回调函书才被调用
      write   high-mark  water
             暂时不用。

      
    


          bufferevent也有一个叫做“error”或者是“event”的回调函数,来告诉关于非面向数据的事件。例如连接中断或者是发生了异常情况。
定义了一下的事件标志:

点击(此处)折叠或打开

  1. BEV_EVENT_READING            //在一个读的操作的时候,事件发生了,往往伴随这其他的消息产生。
  2. BEV_EVENT_WRITING             //在一个写的操作的时候,事件发生了
  3. BEV_EVENT_ERROR               //有错误
  4. BEV_EVENT_TIMEOUT              //超时
  5. BEV_EVENT_EOF                  //流末尾
  6. BEV_EVENT_CONNECTED            //有连接


使用

    新建一个bufferevent的api   

点击(此处)折叠或打开

  1. struct bufferevent *bufferevent_socket_new(
  2.     struct event_base *base,
  3.     evutil_socket_t fd,
  4.     enum bufferevent_options options)
    fd变量表示要监控的底层文件描述符,如果为-1,可以之后设置,注意,应该保证此文件描述符是处于非阻塞模式,你可以调用
  evutil_make_socket_nonblocking()使其处于非阻塞状态, options有下面几个值:

点击(此处)折叠或打开

  1. BEV_OPT_CLOSE_ON_FREE               //当bufferevent被free了的时候,同时释放底层的介质(socket)
  2. BEV_OPT_THREADSAFE                  //线程安全
  3. BEV_OPT_DEFER_CALLBACKS             //推迟调用,有些时候bufferevent会推迟调用,触发的事件
  4. BEV_OPT_UNLOCK_CALLBACKS            //在threadsafe模式下,在调用回调的时候会加锁,这个变量释放锁。
   你如果想和服务器建立连接,你可以调用下面的函数:
  

点击(此处)折叠或打开

  1. int bufferevent_socket_connect(struct bufferevent *bev,
  2.     struct sockaddr *address, int addrlen)
    这个函数相当于调用了connect函数,如果bev的fd为-1,这个函数会帮你申请一个socket,并且建立连接,如果fd有值,并且未处于连接状态,libevent会帮你建立连接。
    还有一个函数可以直接连接制定的hostname的服务器:
  

点击(此处)折叠或打开

  1. int bufferevent_socket_connect_hostname(struct bufferevent *bev,
  2.     struct evdns_base *dns_base, int family, const char *hostname,
  3.     int port);
  4. int bufferevent_socket_get_dns_error(struct bufferevent *bev)
        family指向协议族,例如af_inet,af_inet6, dns_base是可选的,如果为NULL,libevent会等待域名解析结束。如果函数出错,你可以调用下面的函数。获取错误信息。
       当我们需要释放一个bufferevent的时候,调用下面的api
      

点击(此处)折叠或打开

  1. void bufferevent_free(struct bufferevent *bev)
      下面的函数是设置bufferevent的函数:
     

点击(此处)折叠或打开

  1. typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
  2. typedef void (*bufferevent_event_cb)(struct bufferevent *bev,
  3.     short events, void *ctx);

  4. void bufferevent_setcb(struct bufferevent *bufev,
  5.     bufferevent_data_cb readcb, bufferevent_data_cb writecb,
  6.     bufferevent_event_cb eventcb, void *cbarg);

  7. void bufferevent_getcb(struct bufferevent *bufev,
  8.     bufferevent_data_cb *readcb_ptr,
  9.     bufferevent_data_cb *writecb_ptr,
  10.     bufferevent_event_cb *eventcb_ptr,
  11.     void **cbarg_ptr)                                                 //cbarg_ptr可以作为你想要的数据,他会作为参数通过callback返回
 
  还有控制读写的api

点击(此处)折叠或打开

  1. void bufferevent_enable(struct bufferevent *bufev, short events);
  2. void bufferevent_disable(struct bufferevent *bufev, short events);

  3. short bufferevent_get_enabled(struct bufferevent *bufev)

    你可一禁止或者开启事件EV_READ,EV_WRITE或者EV_READ|EV_WRITE,,当读和写是被禁止的时候,bufferevent将不会试着读或者写数据,当outputbuffer为空的时候,你没必要,禁止写,因为bufferevent会自动停止写,同时当inputbuffer达到high-water的时候你也每必要禁止读,他也会主动的停止读,知道有空间为止。
    bufferevent默认的是读是禁止的,写是开启的。你可以调用bufferevent_get_enabled()来看那些事件是开启的。


下面的api是设置水位的信息的

点击(此处)折叠或打开

  1. void bufferevent_setwatermark(struct bufferevent *bufev, short events,
  2.     size_t lowmark, size_t highmark)


      你可一通过这个api设置bufferevent的水位,例如events设置EV_READ, lowmark设置128,highmark 设置为0,表示不限制。这样的话只有读有128kb的时候回调函数才会被调用。
   

    下面的函数是操纵底层的数据的函数:

 

点击(此处)折叠或打开

  1. struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
  2. struct evbuffer *bufferevent_get_output(struct bufferevent *bufev)

   你通过这两个函数或者input和output,你可一通过下面的函数读写:

点击(此处)折叠或打开

  1. int bufferevent_write(struct bufferevent *bufev,
  2.     const void *data, size_t size);
  3. int bufferevent_write_buffer(struct bufferevent *bufev,
  4.     struct evbuffer *buf);
  5. size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
  6. int bufferevent_read_buffer(struct bufferevent *bufev,
  7.     struct evbuffer *buf)

  我们也可以设置超时:
  

点击(此处)折叠或打开

  1. void bufferevent_set_timeouts(struct bufferevent *bufev,
  2.     const struct timeval *timeout_read, const struct timeval *timeout_write)


设置fd的函数:
   

点击(此处)折叠或打开

  1. int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
  2. evutil_socket_t bufferevent_getfd(struct bufferevent *bufev)

获取base
  

点击(此处)折叠或打开

  1. struct event_base *bufferevent_get_base(struct bufferevent *bev)


例子:

 

点击(此处)折叠或打开

  1. #include <event2/event.h>
  2. #include <event2/bufferevent.h>
  3. #include <event2/buffer.h>
  4. #include <event2/util.h>

  5. #include <stdlib.h>
  6. #include <errno.h>
  7. #include <string.h>

  8. struct info {
  9.     const char *name;
  10.     size_t total_drained;
  11. };

  12. void read_callback(struct bufferevent *bev, void *ctx)
  13. {
  14.     struct info *inf = ctx;
  15.     struct evbuffer *input = bufferevent_get_input(bev);
  16.     size_t len = evbuffer_get_length(input);
  17.     if (len) {
  18.         inf->total_drained += len;
  19.         evbuffer_drain(input, len);
  20.         printf("Drained %lu bytes from %s\n",
  21.              (unsigned long) len, inf->name);
  22.     }
  23. }

  24. void event_callback(struct bufferevent *bev, short events, void *ctx)
  25. {
  26.     struct info *inf = ctx;
  27.     struct evbuffer *input = bufferevent_get_input(bev);
  28.     int finished = 0;

  29.     if (events & BEV_EVENT_EOF) {
  30.         size_t len = evbuffer_get_length(input);
  31.         printf("Got a close from %s. We drained %lu bytes from it, "
  32.             "and have %lu left.\n", inf->name,
  33.             (unsigned long)inf->total_drained, (unsigned long)len);
  34.         finished = 1;
  35.     }
  36.     if (events & BEV_EVENT_ERROR) {
  37.         printf("Got an error from %s: %s\n",
  38.             inf->name, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
  39.         finished = 1;
  40.     }
  41.     if (finished) {
  42.         free(ctx);
  43.         bufferevent_free(bev);
  44.     }
  45. }

  46. struct bufferevent *setup_bufferevent(void)
  47. {
  48.     struct bufferevent *b1 = NULL;
  49.     struct info *info1;

  50.     info1 = malloc(sizeof(struct info));
  51.     info1->name = "buffer 1";
  52.     info1->total_drained = 0;

  53.     /* ... Here we should set up the bufferevent and make sure it gets
  54.        connected... */

  55.     /* Trigger the read callback only whenever there is at least 128 bytes
  56.        of data in the buffer. */
  57.     bufferevent_setwatermark(b1, EV_READ, 128, 0);

  58.     bufferevent_setcb(b1, read_callback, NULL, event_callback, info1);

  59.     bufferevent_enable(b1, EV_READ); /* Start reading. */
  60.     return b1
  61. }




阅读(1231) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~