概述:
有时候我们不只是相应一个事件,我们还需要缓存一些数据,基本的操作如下:
-
我们决定往一个连接输出一些数据,我们会首先把数据放到一个缓存中,
-
等待文件描述符可写。
-
往连接中写尽可能多的数据
-
记住我们写了多少,如果我们没有写完,我们会再次等待连接可写
bufferevent正是为了这种机制才提供的,一个eventbuffer在内部保存了类似于socket的传输介质,一个读的buffer,一个写的buffer,不同
与传统的event,当传输介质可读可写的时候会调用callback函数,bufferevent在有足够的数据可读可写的时候调用用户提供的callback。
有不同种类的bufferevent,但是他们有着共同的接口。
-
asynchronous-IO bufferevent
该接口使用windows iocp 完成端口
该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”的回调函数,来告诉关于非面向数据的事件。例如连接中断或者是发生了异常情况。
定义了一下的事件标志:
-
BEV_EVENT_READING //在一个读的操作的时候,事件发生了,往往伴随这其他的消息产生。
-
BEV_EVENT_WRITING //在一个写的操作的时候,事件发生了
-
BEV_EVENT_ERROR //有错误
-
BEV_EVENT_TIMEOUT //超时
-
BEV_EVENT_EOF //流末尾
-
BEV_EVENT_CONNECTED //有连接
使用
新建一个bufferevent的api
-
struct bufferevent *bufferevent_socket_new(
-
struct event_base *base,
-
evutil_socket_t fd,
-
enum bufferevent_options options)
fd变量表示要监控的底层文件描述符,如果为-1,可以之后设置,注意,应该保证此文件描述符是处于非阻塞模式,你可以调用
evutil_make_socket_nonblocking()使其处于非阻塞状态, options有下面几个值:
-
BEV_OPT_CLOSE_ON_FREE //当bufferevent被free了的时候,同时释放底层的介质(socket)
-
BEV_OPT_THREADSAFE //线程安全
-
BEV_OPT_DEFER_CALLBACKS //推迟调用,有些时候bufferevent会推迟调用,触发的事件
-
BEV_OPT_UNLOCK_CALLBACKS //在threadsafe模式下,在调用回调的时候会加锁,这个变量释放锁。
你如果想和服务器建立连接,你可以调用下面的函数:
-
int bufferevent_socket_connect(struct bufferevent *bev,
-
struct sockaddr *address, int addrlen)
这个函数相当于调用了connect函数,如果bev的fd为-1,这个函数会帮你申请一个socket,并且建立连接,如果fd有值,并且未处于连接状态,libevent会帮你建立连接。
还有一个函数可以直接连接制定的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)
family指向协议族,例如af_inet,af_inet6, dns_base是可选的,如果为NULL,libevent会等待域名解析结束。如果函数出错,你可以调用下面的函数。获取错误信息。
当我们需要释放一个bufferevent的时候,调用下面的api
-
void bufferevent_free(struct bufferevent *bev)
下面的函数是设置bufferevent的函数:
-
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) //cbarg_ptr可以作为你想要的数据,他会作为参数通过callback返回
还有控制读写的api:
-
void bufferevent_enable(struct bufferevent *bufev, short events);
-
void bufferevent_disable(struct bufferevent *bufev, short events);
-
-
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是设置水位的信息的:
-
void bufferevent_setwatermark(struct bufferevent *bufev, short events,
-
size_t lowmark, size_t highmark)
你可一通过这个api设置bufferevent的水位,例如events设置EV_READ, lowmark设置128,highmark 设置为0,表示不限制。这样的话只有读有128kb的时候回调函数才会被调用。
下面的函数是操纵底层的数据的函数:
-
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
-
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev)
你通过这两个函数或者input和output,你可一通过下面的函数读写:
-
int bufferevent_write(struct bufferevent *bufev,
-
const void *data, size_t size);
-
int bufferevent_write_buffer(struct bufferevent *bufev,
-
struct evbuffer *buf);
-
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
-
int bufferevent_read_buffer(struct bufferevent *bufev,
-
struct evbuffer *buf)
我们也可以设置超时:
-
void bufferevent_set_timeouts(struct bufferevent *bufev,
-
const struct timeval *timeout_read, const struct timeval *timeout_write)
设置fd的函数:
-
int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
-
evutil_socket_t bufferevent_getfd(struct bufferevent *bufev)
获取base
-
struct event_base *bufferevent_get_base(struct bufferevent *bev)
例子:
-
#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
-
}
阅读(1242) | 评论(0) | 转发(0) |