Bufferevents:概念和基础

大部分情况下,程序除了响应事件之外,还需要做数据缓存。例如,当我们需要写数据时,通常的模式如下:
1.确定要给一个连接写数据,将数据写入缓冲区
2.等待连接可写
3.写尽可能多的数据
4.记下已经写入多少数据,如果数据还能写完,并等待连接再一次可写
这种IO模式很常见, Libevent 为此提供了一个通用的机制。bufferevent由一个底层传输,写入和读出缓冲区组成。用回调函数取代常规事件,当底层传输可读或可写的时候,当已经读取或写入足够的数据后,bufferevent 调用用户提供的回调函数。
bufferevent有以下几种,他们都公用一个接口:
socket-based bufferevents   从socket读取或写入数据。
asynchronous-IO bufferevents   用window的IOCP接口向底层的socket写入读取数据,仅在window上可用,
filtering bufferevents   在把数据传给底层的bufferevent对数据进行处理。
paired bufferevents   把数据传给另外一个bufferevent

一、Bufferevents and evbuffers

每个 bufferevent 都有一个input和output缓冲区,他们都是evbuffer类型。当有数据要写入bufferevent 时,把数据添加到output,要从bufferevent 读取数据时,从input读取。

二、回调和水位
每个bufferevent 都有两个数据相关的回调函数:一个写回调和读回调。当有数据从底层读出时,调用读回调函数。当有足够的空的输出缓冲区时调用写回调函数。你可以调整水位来覆盖这些回调函数的行为。
每个bufferevent 都有四个水位:
Read low-water mark       当input缓冲区的数据达到或高于此水位时,调用读回调函数。
Read high-water mark      如果input缓冲区的数据没有达到此水位则停止读数据,直到input中水位低于此。
Write low-water mark       当output缓冲区的是数据达到或低于该水位时,调用写回调函数
Write high-water mark      bufferevent 没有直接使用该水位
bufferevent 也有错误或者事件回调函数被调用,用于通知程序非面向数据的事件,比如:一个连接被关闭或出错。
BEV_EVENT_READING          bufferevent读操作时出错
BEV_EVENT_WRITING          bufferevent写操作时出错
BEV_EVENT_ERROR             bufferevent操作时出错
BEV_EVENT_TIMEOUT        bufferevent出现超时
BEV_EVENT_EOF                  bufferevent遇到文件结束符
BEV_EVENT_CONNECTED  结束对bufferevent的连接请求

三、延迟回调
默认情况下,当相应的条件成立时bufferevent的回调函数会立即被调用。当依赖关系变得复杂的时候这种立即调用会有麻烦。例如:有一个回调函数把数据写入evbuffer  A,另一个回调函数从evbuffer  A读取数据,他们都在栈上执行,有导致栈溢出的风险。
要解决此问题,你可以告知bufferevent 它的回调应该延迟。当条件成立时,延迟回调函数不是立即执行,而是进入event_loop()的调用队列,然后在定期的事件被调用。

四、 bufferevent的选项标记
BEV_OPT_CLOSE_ON_FREE
bufferevent 被释放后,关闭底层传输。这将关闭socket和底层bufferevent。
BEV_OPT_THREADSAFE
自动为bufferevent分配锁,这样可以从多个线程使用bufferevent
BEV_OPT_DEFER_CALLBACKS
当设置此标记,bufferevent 延迟所有的回调函数
BEV_OPT_UNLOCK_CALLBACKS
默认情况下,当bufferevent 设置为线程安全时,回调被调用的时候会进行锁定,设置此选项可以使调用函数被调用时解锁。

五、基于socket的bufferevent
最简单的bufferevent类型是基于socket的。基于socket的bufferevent使用了libevent底层的事件机制来发现网络socket何时可读/可写的,并且使用底层的网络调用来传送和接收数据。

创建基于socket的bufferevent
struct bufferevent *bufferevent_socket_new(
    struct event_base *base,
    evutil_socket_t fd,
    enum bufferevent_options options);
该函数创建一个基于socket的bufferevent。如果你想后面再设置文件描述符,fd可以为-1。函数成功则返回一个bufferevent ,释放返回NULL。
启动基于socket的bufferevent的连接
int bufferevent_socket_connect(struct bufferevent *bev,
    struct sockaddr *address, int addrlen);
address 和addrlen是调用标准connect函数的参数。如果bufferevent还没有socket,调用该函数则会分配一个非阻塞的socket。
如果bufferevent已经有socket,该函数通知Libevent ,socket还没连接,在成功连接之前不要进行读写操作。
在成功连接之前向output缓冲区写数据是没问题的。
成功连接则返回0,出错则返回-1.

通过主机名进行连接
int bufferevent_socket_connect_hostname(struct bufferevent *bev,
    struct evdns_base *dns_base, int family, constchar *hostname,
    int port);
int bufferevent_socket_get_dns_error(struct bufferevent *bev);
该函数解析了hostname,查找family的地址。如果解析失败,它通过一个错误事件调用回调函数。
dns_base 是可选的,如果为NULL,Libevent 阻塞等待名字查找完成,这通常不是我们想要的。如果提供该参数,
Libevent通过它来异步的查找主机名。
六、通用的bufferevent操作
1、释放一个bufferevent
void bufferevent_free(struct bufferevent *bev);
该函数释放一个bufferevent。bufferevent是内部引用计数的,当你释放它时,如果bufferevent还要未决的延迟回调函数,它会等到回调函数执行万了才进行删除。
该函数尝试尽可能释放bufferevent ,如果有未决的数据要写入bufferevent ,bufferevent 被释放之前是不会被刷新的。
如果设置BEV_OPT_CLOSE_ON_FREE ,bufferevent 有一个socket,或者底层关联socket作为传输,释放bufferevent 就会关闭传送。
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一个或多个回调函数,当有足够的数据可读,可写或出现错误事件时分别调用回调函数readcb, writecb, 和eventcb。每个函数的第一个参数都是发生事件的bufferevent ,最后一个参数的值是用户调用bufferevent_callcb时传入的,你可以用它给回调函数传数据。事件回调函数的events 参数是事件标记的掩码。
用NULL替代回调函数就可以禁用该回调函数。所有的回调函数共享一个cbarg 值,所以改变其中的值会影响其他函数。
bufferevent_getcb()传入指针来获取目前设置的回调函数。
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就不会进行数据读写。
当output缓冲区为空时不用禁用写,bufferevent 自动停止写数据,当有数据要写时再重启。
你可以用bufferevent_get_enabled()获取bufferevent 目前哪个事件启用。

void bufferevent_setwatermark(struct bufferevent *bufev, short events,
    size_t lowmark, size_t highmark);
该函数调整bufferevent的读写水位。
七、操作bufferevent的数据
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);
这两个函数很强大,他们返回缓冲区,
int bufferevent_write(struct bufferevent *bufev,
    constvoid *data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev,
    struct evbuffer *buf);
这两个函数向bufferevent的output缓冲区写入数据。
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_read_buffer(struct bufferevent *bufev,
    struct evbuffer *buf);
从bufferevent的input 缓冲区读数据
八、读写超时
void bufferevent_set_timeouts(struct bufferevent *bufev,
    conststruct timeval *timeout_read, conststruct timeval *timeout_write);
尝试读操作并等待timeout_read秒之后将触发读超时。
尝试写操作并等待
timeout_write
秒之后将触发写超时。

如果bufferevent禁用了读操作或者input缓冲区已经满了,则读超时也就不可用了。同理,写超时也一样。
九、清空bufferevent
int bufferevent_flush(struct bufferevent *bufev,
    short iotype, enum bufferevent_flush_mode state);
冲洗bufferevent就是告知bufferevent强制从底层读写尽可能多的数据,忽略其他数据不可写入的限制。具体功能依赖与bufferevent的类型。
十、特定类型bufferevent的函数
int bufferevent_priority_set(struct bufferevent *bufev, int pri);
int bufferevent_get_priority(struct bufferevent *bufev);
bufferevent_priority_set用来调整bufferevent的优先级,只能用于基于socket的bufferevent。
int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
evutil_socket_t bufferevent_getfd(struct bufferevent *bufev);
这两个函数用于设置或返回基于文件的事件的文件描述符。只要基于socket的bufferevent支持setfd()、
struct event_base *bufferevent_get_base(struct bufferevent *bev);
该函数返回bufferevent的event_base

struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev);
该函数返回由于其他bufferevent 的底层传输的bufferevent 。
十一、bufferevent 手动加锁和解锁
void bufferevent_lock(struct bufferevent *bufev);
void bufferevent_unlock(struct bufferevent *bufev);
如果创建bufferevent时不用 BEV_OPT_THREADSAFE或者Libevent的不支持多线程,就不能给bufferevent加锁。
锁住bufferevent 自然也会把它不关联的evbuffers也加锁。这些函数是递归的,你可以给已经加锁的bufferevent 再加锁。















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值