Libevent book 笔记(8) Bufferevents concepts and basics


大多数情况下,一个应用不仅仅是响应事件,还需要对缓冲数据做某些操作。比如,当我们需要写数据时,模式通常是这样的:

  • 需要给一个连接写入数据,把这些数据放入缓冲区。
  • 等待该连接变得可写。
  • 写入尽可能多的数据。
  • 如果还有数据要写,记录写了多少数据,等待连接下一次变得可写入。

Libevent为这种常见的缓冲IO模式提供了一套通用机制,也就是“Bufferevent”,包括了底层的传输(比如socket),一个读缓冲,一个写缓冲。常规的event,是通过回调函数来处理底层传输的可读或者可写。而对于bufferevent,则是有足够的数据可读或者可写时调用用户提供的回调函数来处理。这里主要的区别不在于对回调的形容,而是常规event处理的是传输层,比方说socket的可读可写事件,bufferevent处理的是数据本身,或者说缓冲区里数据的数量引发的事件。

下面是目前为止所有的bufferevent的类型,它们共享一个公共的接口:

  • 基于socket的bufferevent

    通过底层的流socket来发送和接收数据,使用event_*接口作为后端

  • 异步IObufferevent

    使用Windows IOCP接口来发送和接收数据,底层也还是流socket。(只对Windows,实验性质)

  • 过滤bufferevent

    在把传入或者传出的数据传入底层的bufferevent对象前,预先进行一些处理的bufferevent,比方说压缩或者转换。

  • 成对bufferevent

    两个相互传输数据的bufferevent。

注意

  • 下面将要描述的接口并不一定都适用于所有的bufferevent类型。
  • 目前bufferevent只支持面向流的协议,如TCP,将来也许会支持面向数据报的协议,如UDP。
  • 本节所述的所有函数和类型都在event2/bufferevent.h里声明。
  • 与evbuffer相关的一些函数在 event2/buffer.h中声明。

Bufferevents and evbuffers

每一个bufferevent都有一个输入缓冲和一个输出缓冲,它们的类型是所谓的evbuffer结构。当把数据写入到一个bufferevent,就是把数据添加到输出缓冲中,当一个bufferevent有数据可读,则是从输入缓冲中获取,或者说排出。

evbuffer在后面章节讨论。

Callbacks and watermarks

每个bufferevent都有两个数据相关的回调:读回调和写回调。缺省情况下,当从底层传输中读到任何数据是都会调用读回调,当输出缓冲中的数据足够排空到底层传输时就会调用写回调。这些行为可以通过设置bufferevent的读或者写的水线来改变。

这里需要明确一下,写回调针对的是输出缓冲,而不是底层的传输,底层传输的情况由bufferevent处理,或者说,底层传输可写的话,bufferevent会自动将输出缓冲中的数据加入到底层传输的缓冲中,当bufferevent的输出缓冲中有足够的空间可以写入时,就会调用写回调。

bufferevent有四个水线:

  • Read low-water mark

    当一个读操作使得bufferevent的输入缓冲的数据达到或者高于这个线时,就会调用读回调。缺省为0,所以每一次读都会调用读回调。这里的读指的是从底层传输中获取数据,或者说一个read系统调用。

  • Read high-water mark

    如果bufferevent的输入缓冲达到了这条线,bufferevent就会停止读取,直到从输入缓冲中排出足够的数据,或者说应用从bufferevent的输入缓冲中取出了足够的数据,使得其中的数据低于这条线为止。缺省是无限的,因此也就不会因为输入缓冲的大小而停止读取数据。同样,这里的读取指的是从底层传输读取。

  • Write low-water mark

    当一个写操作使得输出缓冲的数据达到或者低于这条线时,就会调用写回调。缺省是0,因此,除非输出缓冲空了,否则是不会调用写回调的。同样,这里的写操作指的是向底层传输的写入。

  • Write high-water mark

    Libevent不直接使用这个水线,当一个bufferevent用来作为另一个Libevent的底层传输时,这条水线有特殊的含义,参见下面过滤bufferevent的说明。

bufferevent还有”error”或者”event”回调,用来告知应用一些与数据无关的事件,比方说连接关闭或者错误发生。有如下的事件标志定义:

  • BEV_EVENT_READING

    在对bufferevent进行读操作时发生的事件。需要查看别的事件标记来确定是什么事件。

  • BEV_EVENT_WRITING

    在对bufferevent进行写操作时发生的事件。需要查看别的事件标记来确定是什么事件。

  • BEV_EVENT_ERROR

    在操作bufferevent时发生的错误,调用EVUTIL_SOCKET_ERROR()来获知是什么错误。

  • BEV_EVENT_TIMEOUT

    bufferevent超时

  • BEV_EVENT_EOF

    在bufferevent得到一个文件结束标记。

  • BEV_EVENT_CONNECTED

    完成一个bufferevent上的连接请求。

Deferred callbacks

缺省情况下,一个bufferevent回调当对应情况发生时就会马上执行,对应evbuffer回调也是如此。这种即时调用在依赖性比较复杂时可能会造成麻烦,比方说,假定一个回调在evbuffer A变空时向其写入数据,另一个回调在evbuffer A满的时候处理其中的数据,由于这些回调是在栈上发生的,当依赖性比较复杂时可能就会有栈溢出的风险。

为了解决这个问题,可以告知bufferevent或者evbuffer,其回调应该延时调用。当一个延时回调的条件满足时,并不会马上调用,而是在event_loop()中排队,如常规事件一样等待被调用。

Option flags for bufferevents

创建bufferevent时可以设置一些标记来改变其行为。可识别的标记有:

  • BEV_OPT_CLOSE_ON_FREE

    当bufferevent被释放时,关闭底层的传输。包括关闭底层的socket,释放底层的bufferevent,等等。

  • BEV_OPT_THREADSAFE

    自动为bufferevent分配锁,这样可以在多线程中安全的使用。

  • BEV_OPT_DEFER_CALLBACKS

    延时调用所有的回调,参见上面的解释。

  • BEV_OPT_UNLOCK_CALLBACKS

    缺省情况下,bufferevent是设置为线程安全的,当一个用户提供的回调调用时,bufferevent都会锁定。这个参数使得bufferevent在调用回调时释放其锁。

Working with socket-based bufferevents

基于socket的bufferevent是最易于使用的,其使用Libevent的底层传输机制来检测底层网络socket可以读或写的时机,并且使用底层网络调用(如readv、writev、WSASend、WSARecv)来发送和接收数据。

Creating a socket-based bufferevent

bufferevent_socket_new
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等)的bitmask,fd是一个socket的文件描述符,如果打算之后再设置这个文件描述符,可以将其设为-1。

注意确保把提供给bufferevent_socket_new的socket设置为非堵塞的。Libevent提供的evutil_make_socket_nonblocking可以很方便的用来实现这点。如果使用evconnlistener的话,相关的socket实际上是会缺省设置为非堵塞的。

成功返回一个bufferevent,失败返回NULL。注意是指针。

Launching connections on socket-based bufferevents

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

address和addrlen参数与标准的connect()一致。如果bufferevent还没有设置一个socket,这个函数会分配一个新的流socket,并将其置为非堵塞。这里只是说此函数会自动分配并设置非堵塞,并不表明所有使用bev的函数都会如此。

如果bufferevent确实已经有了一个socket,这个函数会告知Libevent该socket还没有连接,不应该在这个socket上读或写,直到连接操作成功。

在连接结束前向输出缓冲添加数据时可以的。

如果连接成功函数返回0,如果发生错误返回-1。

例子

#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); /*或者 inet_addr("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_socket_connect()时才会得到BEV_EVENT_CONNECTED事件,如果直接调用connect(),则连接会被报告为写。

如果想自己调用connect(),但又想在连接成功时得到BEV_EVENT_CONNECTED事件,则可以在调用connect()以errno为EAGAIN或者EINPROGRESS返回-1后,调用bufferevent_socket_connect(bev, NULL, 0)。当然要清楚,这时候connect所使用的socket必须是已经设置为非堵塞的了。不大想象得出这种用法的使用场景,列在这里以便万一需要的时候做个参考。

Launching connections by hostname

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

函数解析DNS名称hostname,查找类型为family的地址。允许的family类型是AF_INET, AF_INET6, AF_UNSPEC。如果名字解析失败,调用event回调,如果成功,则发起与bufferevent_connect一样的连接操作。

dns_base参数是可选的,如果为NULL,则Libevent在名字查找完成前堵塞,这通常并不是所想要的。如果提供了,则Libevent使用这个参数以异步方式查找主机名。具体可参见后面关于DNS的章节。

bufferevent_socket_connect()一样,这个函数在解析完成以及连接成功之前,都会告知Libevent,bufferevent已有的socket还没有连接,不应该在其上进行读或写操作。

bufferevent_socket_get_dns_error
int bufferevent_socket_get_dns_error(struct bufferevent *bev);

如果有错误发生,可能是个DNS主机名查找错误。通过调用bufferevent_socket_get_dns_error()可以找到最近发生的错误。如果返回的错误码为0,则表示没有检测到DNS错误。

例子: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;
}

Generic bufferevent operations

Freeing a bufferevent

bufferevent_free
void bufferevent_free(struct bufferevent *bev);

释放一个bufferevent。Bufferevent内部是引用计数的,所以在释放一个buffevent的时候,如果有延时回调正处于挂起等待状态,则会等待所有这些回调完成之后才会删除这个bufferevent。

不过,bufferevent_free是只要有可能就会尽快释放bufferevent。如果有挂起的数据等待写入bufferevent,那么在bufferevent被释放前这些数据可能不会被写入到bufferevent中,就是说释放bufferevent可能会丢失一部分数据。

如果设置了BEV_OPT_CLOSE_ON_FREE标记,并且bufferevent由socket或者底层bufferevent来传输数据,当释放bufferevent时,传输会关闭。

Manipulating callbacks, watermarks, and enabled operations

回调函数原型
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
typedef void (*bufferevent_event_cb)(struct bufferevent *bev,
    short events, void *ctx);
bufferevent_setcb
void bufferevent_setcb(struct bufferevent *bufev,
    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
    bufferevent_event_cb eventcb, void *cbarg);

bufferevent_setcb用来设置或者改变指定bufferevent的回调函数。

  • bufev是指定的bufferevent,其余几个参数所代表的的事件都发生在这个bufferevent上。
  • readcb在有足够的数据可读时调用,writecb在有足够的数据可写时调用,eventcb在有事件发生时调用,这几个回调函数的相关问题可以参照前面的一些说明,这里说的只是大概情况,并不十分严格或者说确切。如果参数被置为NULL,则表示禁用相应的回调。
  • cbarg_ptr是用来给回调函数传递参数的,或者说这个参数会变成如上面回调函数签名里的ctx,传入回调函数,通常会在回调函数里对ctx做一个类型转换,然后再使用,或者说,对一个回调函数而言,ctx提供了一个泛型机制。所有设置的回调函数共享这个参数,所以要注意其设置。
  • bufferevent_event_cb的events参数是事件标记的一个bitmask,当然这是在回调发生时由Bufferevent自动传入的。
bufferevent_getcb
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_getcb用来查询bufferevent当前的回调设置。要注意的是其参数都是指针,这几个参数都是作为输入输出参数传入函数,被设为NULL的参数表示忽略相应的内容。所有的参数输入前只需要声明,但不需要分配空间,函数只是把bufev结构的域的值简单的赋给参数,返回后,因为函数指针和函数名是等价的,不会有什么使用上的区别或者说误解,就不多说了了,只是要稍微说下cbarg_ptr这个参数,作为输出参数后续使用时要先做转换,转换的对象是*cbarg_ptr,而不是cbarg_ptr,因为cbarg_ptr所指向的内存区存放是一个地址,我们要获取的是这个地址,而不是cbarg_ptr本身的地址,所以要对*cbarg_ptr进行操作,而不是对cbarg_ptr,至于为什么传入一个void **,而不是一个void *,这涉及到对c语言指针和函数参数传递机制的的理解,就不废话了。

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

可以设置或者禁止bufferevent上的EV_READ、EV_WRITE、或者EV_READ|EV_WRITE事件。当没有设置读或写事件是,bufferevent将不会去读或者写数据。这里的读或者写指的是从底层传输读取或者向底层传输写入。

当输出缓冲空时并不需要禁止写,bufferevent会自动停止,当有数据时会自动启动。

类似的,当输入缓冲达到高水位线时也不需要禁止读,bufferevent也会自动停止读,然后当有空间的时候再自动启动。

缺省情况下,新创建的bufferevent会设置可写,但不设置可读。

bufferevent_get_enabled()用来查看bufferevent当前启用了哪些事件。

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

用来调整一个bufferevent的读写水线。如果events设为EV_READ,则调整读水线,如果设置为EV_WRITE,则调整读水位线。但是除非打算给读写两类水线设为一样的值,否则没有理由设置events为EV_READ|EV_WRITE,虽然是允许的。

高水位线设为0表示无限。

例子:
#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;
}

Manipulating data in a bufferevent

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

这两个函数分别返回输入和输出缓冲,详细信息可以查看下一章的evbuffer类型。

注意应用对输入缓冲只能移除数据而不能添加数据,对输出缓冲只能添加而不能移除数据。

如果bufferevent由于输出缓冲的数据过少而停止了向传输写入,那么就会自动启动向输出缓冲的数据添加。如果输入缓冲的数据过多而使得从传输读取数据停止了,bufferevent会等到从输入缓冲移除一定量的数据之后再自动启动读取。

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

这两个函数都是把数据添加到bufev的输出缓冲中。

bufferevent_write把内存中位于data的size字节的数据添加到bufev的输出缓冲的末端。

bufferevent_write_buffer移除buf的所有数据然后放到bufev的输出缓冲的末端。

两个函数都是成功返回0,失败返回-1。

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

这两个函数都是从bufev的输入缓冲中移除数据。

bufferevent_read从输入缓冲移除最多size字节的数据,然后存储到data所指的内存中,返回实际移除的数据。

bufferevent_read_buffer把输入缓冲的所有内容排空,并把它们放到buf中,成功返回0,失败返回-1。

要注意对于bufferevent_read,data指向的内存块必须要有足够大的空间能够存放size字节的数据。

例子
#include <event2/bufferevent.h>
#include <event2/buffer.h>

#include <ctype.h>

void
read_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;
};
void
read_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];
};

void
write_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);
}

Read- and write timeouts

与其他事件一样,可以设置bufferevent读或写的超时时间,如果超时了,就可以调用超时事件回调。

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

把超时设为NULL本来是要移除超时设置,但是在Libevent 2.1.2-alpha之前对所有事件这都不起效。之后大概也行可以了,但是也不好说。

当bufferevent在要读的时候如果等待了至少timeout_read 秒,就会触发读超时。如果在要写的时候等待了至少timeout_write ,则会触发写超时。

注意只有当bufferevent打算开始读或者写的时候才开始计时。换句话说,如果bufferevent禁止读,或者当输入缓冲满了(达到高水线),那么读超时就不会启用。类似的,如果写被禁止,或者没有数据可写,则写超时也不会启用。简单的说,就是bufferevent要开始读或写了,才会开始计时,如果根本就没有或者打算进行这个操作,那么就不会计时。

但读或者写超时发生时,在bufferevent上相应的读或写操作会被禁止,然后调用事件回调函数,这时候其调用参数标记为BEV_EVENT_TIMEOUT|BEV_EVENT_READING或者BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING。

Initiating a flush on a bufferevent

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

刷新一个bufferevent表示bufferevent要强制地使底层传输读入或者发送尽可能多的数据,并忽略其它任何的限制。注意指的是在底层传输接受或者发送数据,而不是从bufferevent的输入缓冲把数据读取到应用,或者把应用的数据写入到输出缓冲,当然,底层数据变化会引发bufferevent的可读可写事件。

iotype参数应该是EV_READ、EV_WRITE或者EV_READ|EV_WRITE。state参数可以是BEV_NORMAL、BEV_FLUSH、BEV_FINISHED之一,BEV_FINISHED表示另一端应该告知没有更多数据发送了,BEV_NORMAL和BEV_FLUSH的区别取决于bufferevent的类型。

返回-1表示失败,0表示没有数据被刷新,1表示刷新了一些数据。

到Libevent 2.0.5-beta,bufferevent_flush只应用于某些bufferevent类型,特别的是,到目前版本为止,基于socket的bufferevent也还是不支持的。

Type-specific bufferevent functions

下面的函数并不是所有的bufferevent类型都支持。

bufferevent_priority_set

bufferevent_get_priority

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

bufferevent_priority_set调整的是bufev对应的events的优先权。成功返回0,失败返回-1。

bufferevent_get_priority获取的是bufev对应的events的优先权,不存在失败的情况,至少会返回一个event_base的缺省优先权。

仅适用于基于socket的bufferevent。

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

设置或者返回基于文件描述符事件的文件描述符。只有基于socket的bufferevent支持setfd。失败都返回-1,setfd成功返回0。

bufferevent_get_base
struct event_base *bufferevent_get_base(struct bufferevent *bev);

就不废话l了。

bufferevent_get_underlying
struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev);

返回作为底层传输的另一个bufferevent,当然只适用于filtering bufferevents。

Manually locking and unlocking a bufferevent

有时候需要确保对bufferevent包括evbuffer的操作的原子性。

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

注意创建bufferevent时如果没有指定BEV_OPT_THREADSAFE标记,或者Libevent的线程支持没有激活,锁定一个bufferevent是没有效果的。

锁定一个bufferevent也会同时锁定其关联的evbuffer。这些函数是递归的,锁定一个已经锁定的bufferevent是安全的。当然,每次锁定都必须对应一次解锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值