libevent的bufferevent
libevent的bufferevent是在event的基础上自己维护了一个buffer,这意味着你的程序不再需要自己管理一个buffer。而且在windows上的异步IO不是“select()-like"接口,而是IOCP API,在一个socket已经准备好读写时并不会通知你的程序去从内核复制到用户内存(或者从内核复制到内核),而是在将数据从内核已经复制到用户内存(或反之)再通知你的程序。bufferevent API的使用刚好能满足这种情况,或者说在不支持IOCP的linux上能够模拟出这种功能。看下bufferevent的基本用法:
/* echo server */
/* For sockaddr_in */
#include
/* For socket functions */
#include
/* For fcntl */
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 32
void readcb(struct bufferevent *bev, void *ctx)
{
char *line = NULL;
struct evbuffer *input, *output;
size_t n;
input = bufferevent_get_input(bev);
output = bufferevent_get_output(bev);
while ((line = evbuffer_readln(input, &n, EVBUFFER_EOL_LF)) != NULL)
{
evbuffer_add(output, line, strlen(line) + 1);
evbuffer_add(output, "\n", 1);
free(line);
}
char buf[MAXLINE];
if (evbuffer_get_length(input) >= MAXLINE)
{
while (evbuffer_get_length(input))
{
int n = evbuffer_remove(input, buf, sizeof(buf));
evbuffer_add(output, buf, n);
}
evbuffer_add(output, "\n", 1);
}
}
void errorcb(struct bufferevent *bev, short error, void *ctx)
{
if (error & BEV_EVENT_EOF) {
/* connection has been closed, do any clean up here */
/* ... */
} else if (error & BEV_EVENT_ERROR) {
/* check errno to see what error occurred */
/* ... */
} else if (error & BEV_EVENT_TIMEOUT) {
/* must be a timeout event handle, handle it */
/* ... */
}
bufferevent_free(bev);
}
void do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = (struct event_base *)arg;
struct sockaddr_in sin;
socklen_t slen = sizeof(sin);
int fd = accept(listener, (struct sockaddr *)&sin, &slen);
if (fd < 0)
{
/* EAGIN? */
perror("accept");
}
else
{
struct bufferevent *bev;
evutil_make_socket_nonblocking(fd);
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, readcb, NULL, errorcb, NULL);
bufferevent_setwatermark(bev, EV_READ, 0, 102400);
bufferevent_enable(bev, EV_READ | EV_WRITE);
}
}
int main(int argc, char *argv[])
{
struct sockaddr_in sin;
evutil_socket_t listener;
struct event_base *base;
struct event * listener_event;
base = event_base_new();
if (!base)
return -1; /* XXXerr */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(16998);
listener = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_socket_nonblocking(listener);
if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("bind failed:");
return -1;
}
if (listen(listener, 16) < 0)
{
perror("listen failed:");
return -1;
}
listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void *)base);
event_add(listener_event, NULL);
event_base_dispatch(base);
}
bufferevent相关文档比较浅显,如果要掌握相关函数的具体行为,要研究源码实现细节了。
1.bufferevent_socket_new
调用evbuffer_new初始化input,output(都是evbuffer类型)
调用event_assign初始化ev_read,ev_write(都是event类型),初始化读写事件回调分别为bufferevent_readcb, bufferevent_writecb
调evbuffer_add_cb给output添加了一个callback bufferevent_socket_outbuf_cb
结论:bufferevent主要是由读写事件(ev_read,ev_write),读写缓存(input,output)构成
2.bufferevent_enable
其实是调用了event_add将相应读写事件加入事件监听队列poll。正如文档所说,如果相应事件不置为true,bufferevent是不会读写数据的。
3.bufferevent_socket_outbuf_cb
bufferevent初始化output时,会给output添加这个回调函数,这个回调函数又会在条件允许的情况下把写事件添加入监听队列(event_add ev_write),一旦socket可写,events就会回调bufferevent_writecb,bufferevent_writecb会把写缓存里的(output)数据都发送出去。
4.evbuffer_add
看evbuffer的这个添加数据的操作,跟踪函数调用很快发现主要是做了两件事:一是在把数据拷贝到缓存,缓存不足时还要做扩容操作;二是拷贝完数据后,要遍历evbuffer的回调函数队列,根据条件应逐一调用符合条件的回调函数。结合bufferevent的output来看,在bufferevent初始化时会给output注册一个回调函数bufferevent_socket_outbuf_cb,所以任何对output的evbuffer_add操作最后都会触发bufferevent_writecb, write数据。文档说,默认情况下bufferevent写是enable的,读却不是,即开始就是可写出数据的。其实能否读写的本质是,bufferevent的ev_read,ev_write是否通过event_add添加进了events的监听队列中去poll。再看bufferevent_write的实现,其实就是对output调了evbuffer_add。
5.bufferevent_readcb,bufferevent_writecb
当bufferevent的socket变为可读写时分别调用的回调函数。这两个函数里read,write,并把数据写入或者写出buffer。还有一系列callback,称作用户定义回调,由函数bufferevent_setcb设置,其实是在bufferevent_readcb, bufferevent_writecb执行过程或者执行完成时回调的。比如IOCP的机制:当IO中可读写时,将数据从内核拷贝到用户buffer,并通知用户IO完成。也主要是由此处实现完成的。
转载于:https://www.cnblogs.com/persistentsnail/archive/2012/11/10/3294851.html