libevent学习笔记【使用篇】——6. Bufferevents基本概念

来源:互联网 发布:网络招聘的优点包括 编辑:程序博客网 时间:2024/06/07 06:24

翻译自:http://www.wangafu.NET/~nickm/libevent-book/Ref6_bufferevent.html
原文:http://blog.csdn.net/windeal3203/article/details/52815520

大多数时候,应用程序除了响应请求外,还需要处理数据(及其缓存)。当我们想要写数据是,通常会有以下步骤

  • 决定要向连接中写入什么数据, 把这些数据放入缓存
  • 等待连接可写
  • 写入尽可能多的数据
  • 记住写入了多少数据,如果还有数据没写完。等待连接再次变为可写状态。

      这样的I/O缓冲方式很常见,因而libevent为此提供了一种通用机制。
      “bufferevent”由一个底层传输系统(比如socket),一个读缓冲区和一个写缓冲区组成。
      对于普通的events, 当底层传输系统可读或者可写时,调用回调方式; 而bufferevent提供了一种替代方式:它在已经写入、或者读出数据的时候才调用回调函数。
      libevent有多种bufferevent, 它们共享通用的接口。截止至本文撰写时,已经存在以下类型:

  • 基于socket的bufferevent:
    在底层流式socket上发送和接收数据,使用event_*接口作为其后端。

  • 异步IO的bufferevent:
    使用Windows IOCP接口来想底层流式socket发送和接收数据。(Windows only, 实验性的)
  • 过滤型的bufferevent:
    在数据传送到底层bufferevent对象之前,对到来和外出的数据进行前期处理的bufferevent,比如对数据进行压缩或者转换
  • 成对的bufferevent:

      注意:截止Libevent2.0.2-alpha版本,bufferevent接口还没有完全覆盖所有的bufferevent类型。换句话说,并不是下面介绍的每一个接口都能用于所有的bufferevent类型。Libevent开发者会在未来的版本中解决该问题。
      还要注意:bufferevent目前仅能工作在流式协议上,比如TCP。未来可能会支持数据报协议,比如UDP。
    本文所有的函数和类型都是在<event2/bufferevent.h>文件中声明。与evbuffers相关的函数在<event2/buffer.h>中声明,有关信息参考下一章。

1. bufferevent和evbuffers

  每一个bufferevent 都有一个输入缓冲区和输出缓冲区, 这些缓冲区(buffer)都是struct evbuffer 类型。 当你有数据要写入bufferevent, 你要先把数据填入output buffer,; 当bufferevent上有数据需要读取时,则可以从input buffer中抽取出来。
  evbuffer 接口支持很多操作,会在以后的章节中进行讨论。

2. 回调函数和水位数

每一个bufferevent都有两个数据相关的回到函数, 一个 读回调和一个写回调。 默认情况下,当有数据从底层传输读取时,读回调函数就会被调用; 当ouput buffer想底层传输写入足够多的数据时, 写回调函数就会被调用。
  通过调整bufferevent的读取和写入“水位线”(watermarks),可以改变这些函数的默认行为。
  每个bufferevent都有4个水位线:

  • 读 低水位:
    当bufferevent的输入缓冲区的数据量到达该水位线或者更高时,bufferevent的读回调函数就会被调用。该水位线默认为0,所以每一次读取操作都会导致读回调函数被调用。
  • 读 高水位:
    如果bufferevent的输入缓冲区的数据量到达该水位线时,那么bufferevent就会停止读取,直到输入缓冲区中足够多的数据被抽走,从而数据量再次低于该水位线。默认情况下该水位线是无限制的,所以从来不会因为输入缓冲区的大小而停止读取操作。
  • 写 低水位:
    当写操作使得输出缓冲区的数据量达到或者低于该水位线时,才调用写回调函数。默认情况下,该值为0,所以输出缓冲区被清空时才调用写回调函数。
  • 写 高水位:
    并非由bufferevent直接使用,对于bufferevent作为其他bufferevent底层传输系统的时候,该水位线才有特殊意义。所以可以参考后面的过滤型bufferevent。
      bufferevent还提供了error 或者event 的回调函数,用来通知应用程序关于非数据相关的事件。比如:关闭连接或者发生错误。 为此,定义了以下 event标志:

  • BEV_EVENT_READING:读操作期间发生了事件。具体哪个事件参见其他标志。

  • BEV_EVENT_WRITING:写操作期间发生了事件。具体哪个事件参见其他标志。
  • BEV_EVENT_ERROR:在bufferevent操作期间发生了错误,可以调用EVUTIL_SOCKET_ERROR函数来得到更多的错误信息。
  • BEV_EVENT_TIMEOUT: bufferevent上发生了超时
  • BEV_EVENT_EOF: bufferevent上遇到了EOF标志
  • BEV_EVENT_CONNECTED:在bufferevent上请求的连接已经完成

3. 延期回调

默认情况下, bufferevent的回调函数在响应的条件满足时会立即执行(evbuffer 回调函数也是如此, 后面会讲到)。 当依赖关系比较复杂时, 这种立即执行 的机制会导致一些问题。比如说, 假设 有一个回调函数 是用于在evbuffer A为空时向其填入数据, 而另一个回调函数则在evbuffer A为满时 从中取出数据进行处理。如果所有这些调用都发生在栈上的话,在依赖关系足够复杂的时候,有栈溢出的风险。
  为了解决这个问题,你可以告诉bufferevent(或evbuffer)它的回调函数应该被延迟执行。当延迟回调函数 的对应条件满足时, 延迟回调函数不会立即执行,而是加入到event_loop()调用队列中。然后在常规的event回调之后执行。 。

4. bufferevent 的选项标志

在穿件bufferevent时,可以通过一些选项来修改它的行为:

  • BEV_OPT_CLOSE_ON_FREE: 当释放bufferevent时,关闭底层的传输系统。 这将关闭底层套接字,释放底层bufferevent等。
  • BEV_OPT_THREADSAFE: 自动为bufferevent分配锁, 从而在多线程中可以安全使用。
  • BEV_OPT_DEFER_CALLBACKS: bufferevent会将其所有回调函数进行延迟调用设置。
  • BEV_OPT_UNLOCK_CALLBACKS: 默认情况下, 当设置bufferevent为线程安全的时候,任何用户提供的回调函数调用时都会锁住bufferevent的锁, 设置改标志可以在提供的回调函数被调用时不锁住bufferevent的锁。

5. 基于socket 的bufferevent

最简单的bufferevents就是基于socket类型的bufferevent。基于socket的bufferevent使用Libevent底层event机制探测底层网络socket何时准备好读和写,而且使用底层网络调用(比如readvwritevWSASendWSARecv)进行传送和接受数据。

5.1 创建 基于socket 的bufferevent

使用bufferevent_socket_new() 可以创建一个基于socket的bufferevent。

struct bufferevent *bufferevent_socket_new(    struct event_base *base,    evutil_socket_t fd,    enum bufferevent_options options);

base:表示event_base
options:bufferevent选项的位掩(BEV_OPT_CLOSE_ON_FREE等)。
fd:参数是一个可选的socket文件描述符。如果希望以后再设置socket文件描述符,可以将fd置为-1。

  Tip:要确保提供给bufferevent_socket_new的socket是非阻塞模式。Libevent提供了便于使用的evutil_make_socket_nonblocking来设置非阻塞模式。
  bufferevent_socket_new成功时返回一个bufferevent,失败时返回NULL

5.2 在基于socket的bufferevent上发送连接

如果bufferevent上的socket还没有建立连接, 我们可以发送一个新的连接:

int bufferevent_socket_connect(struct bufferevent *bev,    struct sockaddr *address, int addrlen);

address和addrlen参数类似于标准的connect函数。如果该bufferevent尚未设置socket,则调用该函数为该bufferevent会分配一个新的流类型的socket,并且置其为非阻塞的。
  如果bufferevent已经设置了一个socket,则调用数bufferevent_socket_connect会告知Libevent该socket尚未建立连接,在建立连接成功之前,不应该在其上进行读写操作。
  在建立连接成功之前,向输出缓冲区添加数据是可以的。
  该函数如果在建链成功时,返回0,如果发生错误,则返回-1.
Example

#include <event2/event.h>#include <event2/bufferevent.h>#include <sys/socket.h>#include <string.h>void eventcb(struct bufferevent *bev, short events, void *ptr){    if (events & BEV_EVENT_CONNECTED) {         /* We're connected to 127.0.0.1:8080.   Ordinarily we'd do            something here, like start reading or writing. */    } else if (events & BEV_EVENT_ERROR) {         /* An error occured while connecting. */    }}int main_loop(void){    struct event_base *base;    struct bufferevent *bev;    struct sockaddr_in sin;    base = event_base_new();    memset(&sin, 0, sizeof(sin));    sin.sin_family = AF_INET;    sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */    sin.sin_port = htons(8080); /* Port 8080 */    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);    bufferevent_setcb(bev, NULL, NULL, eventcb, NULL);    if (bufferevent_socket_connect(bev,        (struct sockaddr *)&sin, sizeof(sin)) < 0) {        /* Error starting connection */        bufferevent_free(bev);        return -1;    }    event_base_dispatch(base);    return 0;}

bufferevent_base_connect()函数是在Libevent-2.0.2-alpha版本引入的,在这之前,需要手动调用connect()函数,并且当连接建立的时候,bufferevent会将其作为写事件进行报告。
注意:如果使用bufferevent_socket_connect进行建链的话,会得到BEV_EVENT_CONNECTED事件。如果自己手动调用connect(),则会得到write事件。
如果在手动调用connect()的情况下,仍然想在建链成功的时候得到BEV_EVENT_CONNECTED事件,可以在connect()返回-1,并且errnoEAGAINEINPROGRESS之后,调用bufferevent_socket_connect(bev, NULL, 0)函数。

5.3 通过hostname发射连接

经常性的,你可能希望将解析主机名和建立连接操作合成一个单独的操作,这时可以使用下面的接口:

 int bufferevent_socket_connect_hostname(struct bufferevent *bev,    struct evdns_base *dns_base, int family, const char *hostname,    int port);int bufferevent_socket_get_dns_error(struct bufferevent *bev);

该函数解析DNS名字hostname,查找family类型的地址(family的类型可以是AF_INET, AF_INET6AF_UNSPEC)。如果解析主机名失败,会以error event调用回调函数。如果成功了,则会像 bufferevent_connect一样,接着进行建立连接。
dns_base参数是可选的。如果该参数为空,则Libevent会一直阻塞,等待主机名解析完成,一般情况下不会这么做。如果提供了该参数,则Libevent使用它进行异步的主机名解析。参考Chapter 9更多内容。
类似于bufferevent_socket_connect,该函数会告知Libevent,bufferevent上已存在的socket尚未建立连接,在解析完成,并且连接建立成功之前,不应该在其上进行读写操作。
如果发生了错误,有可能是DNS解析错误。可以通过调用函数bufferevent_socket_get_dns_error函数得到最近发生的错误信息。如果该函数返回的错误码为0,则表明没有检查到任何DNS错误。
Example: Trivial HTTP v0 client.

/* Don't actually copy this code: it is a poor way to implement an   HTTP client.  Have a look at evhttp instead.*/#include <event2/dns.h>#include <event2/bufferevent.h>#include <event2/buffer.h>#include <event2/util.h>#include <event2/event.h>#include <stdio.h>void readcb(struct bufferevent *bev, void *ptr){    char buf[1024];    int n;    struct evbuffer *input = bufferevent_get_input(bev);    while ((n = evbuffer_remove(input, buf, sizeof(buf))) > 0) {        fwrite(buf, 1, n, stdout);    }}void eventcb(struct bufferevent *bev, short events, void *ptr){    if (events & BEV_EVENT_CONNECTED) {         printf("Connect okay.\n");    } else if (events & (BEV_EVENT_ERROR|BEV_EVENT_EOF)) {         struct event_base *base = ptr;         if (events & BEV_EVENT_ERROR) {                 int err = bufferevent_socket_get_dns_error(bev);                 if (err)                         printf("DNS error: %s\n", evutil_gai_strerror(err));         }         printf("Closing\n");         bufferevent_free(bev);         event_base_loopexit(base, NULL);    }}int main(int argc, char **argv){    struct event_base *base;    struct evdns_base *dns_base;    struct bufferevent *bev;    if (argc != 3) {        printf("Trivial HTTP 0.x client\n"               "Syntax: %s [hostname] [resource]\n"               "Example: %s www.google.com /\n",argv[0],argv[0]);        return 1;    }    base = event_base_new();    dns_base = evdns_base_new(base, 1);    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);    bufferevent_setcb(bev, readcb, NULL, eventcb, base);    bufferevent_enable(bev, EV_READ|EV_WRITE);    evbuffer_add_printf(bufferevent_get_output(bev), "GET %s\r\n", argv[2]);    bufferevent_socket_connect_hostname(        bev, dns_base, AF_UNSPEC, argv[1], 80);    event_base_dispatch(base);    return 0;}

6. 通用的bufferevent操作

本节介绍的bufferevent能够在不同的bufferevent实现上工作

6.1 释放bufferevent

void  bufferevent_free(struct  bufferevent *bev);

该函数释放buffereventbufferevent在内部具有引用计数,所以即使当释放bufferevent时,如果bufferevent还有未决的延迟回调,那该bufferevent在该回调完成之前也不会删除。
bufferevent_free函数会尽快释放bufferevent。然而,如果bufferevent的输出缓冲区中尚有残留数据要写,该函数也不会在释放bufferevent之前对缓冲区进行flush
如果设置了BEV_OPT_CLOSE_ON_FREE标志,并且该buffereventsocket或者使用了其他底层bufferevent作为其传输系统,则在释放该bufferevent时,会关闭对应的传输系统。
这些函数在Libevent 0.8.中引入。

6.2 回调函数、水位线、使能操作

typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);typedef void (*bufferevent_event_cb)(struct bufferevent *bev,    short events, void *ctx);void bufferevent_setcb(struct bufferevent *bufev,    bufferevent_data_cb readcb, bufferevent_data_cb writecb,    bufferevent_event_cb eventcb, void *cbarg);void bufferevent_getcb(struct bufferevent *bufev,    bufferevent_data_cb *readcb_ptr,    bufferevent_data_cb *writecb_ptr,    bufferevent_event_cb *eventcb_ptr,    void **cbarg_ptr);

  函数bufferevent_setcb() 可以改变bufferevent的回调函数。readcbwritecbeventcb函数 分别在 有足够数据可读、有足够数据可写、有event时间发生时被调用。 这些回调函数的第一个参数就是发生了events的bufferevent; 而最后一个参数则是由用户提供的bufferevent_setcbcbarg参数;用户可以通过event回调函数的events参数是event标志的位掩码:参考上面的“回调函数和水位线”一节
  我们可以通过向bufferevent_setcb() 传递NULL参数来禁用一些回调函数。需要注意的是,cbarg 参数是针对所有回调函数的。
  我们可以通过向bufferevent_getcb 传递指针来获取当前的回调函数。该函数会将*readcb_ptr设置为当前的读回调函数,*writecb_ptr设置为写回调函数,*eventcb_ptr设置为当前的event回调函数,并且*cbarg_ptr设置为当前回调函数的参数。对于被设置为NULL 的指针,则会忽略它。
  函数bufferevent_setcb() 在Libevent 1.4.4中引入. bufferevent_data_cbbufferevent_event_cb 则是在 Libevent 2.0.2-alpha中新引入的. The bufferevent_getcb() 则是在 2.1.1-alpha 中新加入的。

void bufferevent_enable(struct bufferevent *bufev, short events);void bufferevent_disable(struct bufferevent *bufev, short events);short bufferevent_get_enabled(struct bufferevent *bufev);

我们可以在bufferevent上enable或者disable事件 EV_READ, EV_WRITE, or EV_READ|EV_WRITE 。 当disable了读写操作,则bufferevent不会读取或写入数据。
当output buffer 为空时,没必要disable写动作,bufferevent会自动禁止掉写动作。
类似的,当输入缓冲区达到它的高水位线的时候,没必要禁止读操作:bufferevent会自动停止读操作,而且在有空间读取的时候,又重新开启读操作。
默认情况下,新创建的bufferevent会enable写操作,而禁止读操作。
可以通过bufferevent_get_enabled() 来获取当前bufferevent中有那些events被enable了。

bufferevent_get_enabled() 是在 2.0.3-alpha中引入的, 而其它函数在Libevent 0.8就被引进了。

void bufferevent_setwatermark(struct bufferevent *bufev, short events,    size_t lowmark, size_t highmark);

bufferevent_setwatermark调整一个bufferevent的读水位线,或写水位线,或两者一起调整。如果在events参数中设置了EV_READ参数,则会调整读水位线,如果设置了EV_WRITE标志,则会调整写水位线。将高水位线标志置为0,表示“无限制”。
该函数在Libevent 1.4.4.中引入 。

Example

#include <event2/event.h>#include <event2/bufferevent.h>#include <event2/buffer.h>#include <event2/util.h>#include <stdlib.h>#include <errno.h>#include <string.h>struct info {    const char *name;    size_t total_drained;};void read_callback(struct bufferevent *bev, void *ctx){    struct info *inf = ctx;    struct evbuffer *input = bufferevent_get_input(bev);    size_t len = evbuffer_get_length(input);    if (len) {        inf->total_drained += len;        evbuffer_drain(input, len);        printf("Drained %lu bytes from %s\n",             (unsigned long) len, inf->name);    }}void event_callback(struct bufferevent *bev, short events, void *ctx){    struct info *inf = ctx;    struct evbuffer *input = bufferevent_get_input(bev);    int finished = 0;    if (events & BEV_EVENT_EOF) {        size_t len = evbuffer_get_length(input);        printf("Got a close from %s.  We drained %lu bytes from it, "            "and have %lu left.\n", inf->name,            (unsigned long)inf->total_drained, (unsigned long)len);        finished = 1;    }    if (events & BEV_EVENT_ERROR) {        printf("Got an error from %s: %s\n",            inf->name, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));        finished = 1;    }    if (finished) {        free(ctx);        bufferevent_free(bev);    }}struct bufferevent *setup_bufferevent(void){    struct bufferevent *b1 = NULL;    struct info *info1;    info1 = malloc(sizeof(struct info));    info1->name = "buffer 1";    info1->total_drained = 0;    /* ... Here we should set up the bufferevent and make sure it gets       connected... */    /* Trigger the read callback only whenever there is at least 128 bytes       of data in the buffer. */    bufferevent_setwatermark(b1, EV_READ, 128, 0);    bufferevent_setcb(b1, read_callback, NULL, event_callback, info1);    bufferevent_enable(b1, EV_READ); /* Start reading. */    return b1;}

7. bufferevent中的数据操作

如果不能操作读写的数据,则从网络中读写数据没有任何意义。bufferevent提供函数可以操作读写的数据。

struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);

这两个函数功能非常强大,它们分别反悔bufferevent的input bufferoutput buffer. 在evbuffer类型上所能进行的所有操作,可以参考下一章。
注意,应用程序只能从input buffer中移走(而不是添加)数据,而且只能向output buffer添加(而不是移走)数据。
如果bufferevent上的写操作因为数据太少而停滞(或者读操作因为数据太多而停滞),则向output buffer中添加数据(或者从input buffer中移走数据)可以自动重启写(读)操作。

int  bufferevent_write(struct  bufferevent *bufev,  const  void*data,  size_t  size);int  bufferevent_write_buffer(struct  bufferevent *bufev,  struct  evbuffer*buf);

这些函数向bufferevent的output buffer中添加数据。调用bufferevent_write函数添加data中的size个字节的数据到输出缓冲区的末尾。调用 bufferevent_write_buffer函数则将buf中所有数据都移动到output buffer的末尾。这些函数返回0表示成功,返回-1表示发生了错误。
这两个函数在Libevent 0.8就引入了。

size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);int bufferevent_read_buffer(struct bufferevent *bufev,    struct evbuffer *buf);

这两个函数从bufferevent的input buffer中移走数据。bufferevent_read函数从input buffer中移动size个字节到data中。它返回实际移动的字节数。bufferevent_read_buffer函数则移动输入缓冲区中的所有数据到buf中,该函数返回0表示成功,返回-1表示失败。
注意bufferevent_read函数中,data缓冲区必须有足够的空间保存size个字节。

bufferevent_read() 在 libevent 0.8中引入,bufferevent_read_buffer 在Libevent 2.0.1-alpha. 中才引入。

* Example*

#include <event2/bufferevent.h>#include <event2/buffer.h>#include <ctype.h>voidread_callback_uppercase(struct bufferevent *bev, void *ctx){        /* This callback removes the data from bev's input buffer 128           bytes at a time, uppercases it, and starts sending it           back.           (Watch out!  In practice, you shouldn't use toupper to implement           a network protocol, unless you know for a fact that the current           locale is the one you want to be using.)         */        char tmp[128];        size_t n;        int i;        while (1) {                n = bufferevent_read(bev, tmp, sizeof(tmp));                if (n <= 0)                        break; /* No more data. */                for (i=0; i<n; ++i)                        tmp[i] = toupper(tmp[i]);                bufferevent_write(bev, tmp, n);        }}struct proxy_info {        struct bufferevent *other_bev;};voidread_callback_proxy(struct bufferevent *bev, void *ctx){        /* You might use a function like this if you're implementing           a simple proxy: it will take data from one connection (on           bev), and write it to another, copying as little as           possible. */        struct proxy_info *inf = ctx;        bufferevent_read_buffer(bev,            bufferevent_get_output(inf->other_bev));}struct count {        unsigned long last_fib[2];};voidwrite_callback_fibonacci(struct bufferevent *bev, void *ctx){        /* Here's a callback that adds some Fibonacci numbers to the           output buffer of bev.  It stops once we have added 1k of           data; once this data is drained, we'll add more. */        struct count *c = ctx;        struct evbuffer *tmp = evbuffer_new();        while (evbuffer_get_length(tmp) < 1024) {                 unsigned long next = c->last_fib[0] + c->last_fib[1];                 c->last_fib[0] = c->last_fib[1];                 c->last_fib[1] = next;                 evbuffer_add_printf(tmp, "%lu", next);        }        /* Now we add the whole contents of tmp to bev. */        bufferevent_write_buffer(bev, tmp);        /* We don't need tmp any longer. */        evbuffer_free(tmp);}

8. 读写 超时

同其他events一样,可以设置timeout时间,当bufferevent在timeout时间消逝后还没有成功的读或写任何数据,则可以触发某个超时事件。

void bufferevent_set_timeouts(struct bufferevent *bufev,    const struct timeval *timeout_read, const struct timeval *timeout_write);

timeout设置为NULL,意味着移除超时时间;然而在Libevent 2.1.2-alpha版本之前,这种方式并非在所有event类型上都有效。(对于较老版本的,取消超时时间的有效方法是,可以将超时时间设置为好几天,并且/或者使eventcb函数忽略BEV_TIMEOUT事件)。
当bufferevent试图读取数据时,等待了timeout_read秒还没有数据,则读超时事件就会触发。当bufferevent试图写数据时,至少等待了timeout_write秒,则写超时事件就会触发。
注意,只有在bufferevent读或写的时候,才会对超时时间进行计时。换句话说,如果bufferevent上禁止了读操作,或者当输入缓冲区满(达到高水位线)时,则读超时时间不会使能。类似的,如果写操作未被使能,或者没有数据可写,则写超时时间也会被禁止。

当读或写超时发生的时候,则bufferevent上相应的读写操作就会被禁止。相应的event回调函数就会以BEV_EVENT_TIMEOUT|BEV_EVENT_READINGBEV_EVENT_TIMEOUT|BEV_EVENT_WRITING进行调用。

9. 在bufferevent上进行flush

int bufferevent_flush(struct bufferevent *bufev,    short iotype, enum bufferevent_flush_mode state);

对一个bufferevent进行flush,可以强制bufferevent尽可能多的从底层传输系统上读取或者写入数据,而忽略其他可能阻止写入的限制条件。该函数的细节依赖于不同类型的bufferevent。
参数iotype 可以是EV_READ, EV_WRITE, EV_READ|EV_WRITE指明处理读操作、写操作,还是两者都处理. 参数state可以是BEV_NORMAL, BEV_FLUSH,BEV_FINISHED 之一. BEV_FINISHED 指明应该告诉另一端已经没有数据可以发送了;BEV_NORMALBEV_FLUSH 取决于bufferevent的类型。
bufferevent_flush函数返回-1表示失败,返回0表示没有任何数据被flush,返回1表示由数据被flush。
目前(Libevent2.0.5-beta),bufferevent_flush函数只在某些bufferevent类型上进行了实现,特别是基于socket的bufferevent并不支持该操作。

10. 特定类型的bufferevent函数

这些函数并非在所有bufferevent类型上都能使用

int  bufferevent_priority_set(struct  bufferevent *bufev,  int  pri);int  bufferevent_get_priority(struct  bufferevent *bufev);

该函数将实现bufev的events的优先级调整为pri,关于优先级更多的信息,可以参考event_priority_set函数。
返回0表示成功,返回-1表示失败,该函数只能工作在基于socket的bufferevent上。

函数bufferevent_priority_set() 在 Libevent 1.0 中引入; bufferevent_get_priority() 直到 2.1.2-alpha.才出现。

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

该函数设置或者返回一个基于fdevent的文件描述符。只有基于socketbufferevent支持setfd操作。这些函数返回-1表示失败,setfd返回0表示成功。
函数bufferevent_setfd()Libevent 1.4.4 中引入。 bufferevent_getfd() 在 Libevent 2.0.2-alpha 中引入。

struct event_base *bufferevent_get_base(struct bufferevent *bev);

该函数返回buffereventevent_base. 该函数在2.0.9-rc中引入 .

struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev);

如果bufferevent作为其他bufferevent的底层传输系统的话,则该函数返回该底层bufferevent。参考过滤型bufferevent,获得关于这种情况的更多信息。
这个函数在Libevent 2.0.2-alpha.中引进。

11. 对bufferevent手动加锁或者解锁

类似于evbuffers,有时希望保证在bufferevent上的一系列操作是原子性的。Libevent提供了可以手动加锁和解锁bufferevent的函数。

void bufferevent_lock(struct bufferevent *bufev);void bufferevent_unlock(struct bufferevent *bufev);

注意,如果一个bufferevent在创建时没有指定BEV_OPT_THREADSAFE 标志,或者Libevent的线程支持功能没有激活,则加锁一个bufferevent没有作用。
通过该函数对bufferevent进行加锁的同时,也会加锁evbuffers。这些函数都是递归的:对一个已经加锁的bufferevent再次加锁是安全的。当然,对于每次锁定都必须进行一次解锁。

12. 过时的bufferevent函数

在Libevent1.4和Libevent2.0之间,bufferevent的后台代码经历了大量的修改。在老接口中,访问·bufferevent·结构的内部是很正常的,而且,还会经常使用依赖于这种访问方式的宏。
让问题变得更加复杂的是,老的代码中,有时会使用以evbuffer为前缀的bufferevent函数。
下表是一个Libevent2.0之前版本的函数概述

Current name Old name bufferevent_data_cb evbuffercb bufferevent_event_cb everrorcb BEV_EVENT_READING EVBUFFER_READ BEV_EVENT_WRITE EVBUFFER_WRITE BEV_EVENT_EOF EVBUFFER_EOF BEV_EVENT_ERROR EVBUFFER_ERROR BEV_EVENT_TIMEOUT EVBUFFER_TIMEOUT bufferevent_get_input(b) EVBUFFER_INPUT(b) bufferevent_get_output(b) EVBUFFER_OUTPUT(b)

老版本的函数定义在event.h中,而不是event2/bufferevent.h中。
如果仍然需要访问bufferevent内部结构中的普通部分,可以包含event2/bufferevent_struct.h。我们不建议这样做:bufferevent结构的内容在不同的Libevent版本中经常会发生改变。如果包含了event2/bufferevent_compat.h文件,可以使用本节介绍的宏和名字。
老版本的代码中,创建bufferevent结构的接口有所不同:

struct bufferevent  *bufferevent_new(evutil_socket_t  fd,    evbuffercb  readcb,  evbuffercb  writecb,  everrorcb  errorcb,  void  *cbarg);int  bufferevent_base_set(struct  event_base  *base,  struct bufferevent  *bufev);

bufferevent_new()函数仅能创建基于socketbufferevent,而且还是在不推荐使用的“当前”event_base上。可以通过调用bufferevent_base_set来调整一个socket buffereventevent_base

老版本的代码设置超时的秒数,而不是设置结构体timeval

void  bufferevent_settimeout(struct  bufferevent  *bufev,                    int  timeout_read, int  timeout_write);

最后注意,在Libevent2.0之前的版本中,底层的evbuffer实现是非常低效的,因此使用bufferevent构建高性能的程序是不太可能的。

0 0
原创粉丝点击