libevent的bufferevent

it2022-07-01  94

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


最新回复(0)