bufferevent 是在libevent 的reactor的模式里存在的Proactor模式,在event的基础上维护了自己的一个buffer ,我们不再关心数据的读写实际的操作,bufferevent自己实现了缓冲的绑定,当有读数据就绪时,就调用buffer_read 读走数据,或者调用buffer_write 写入给定数据,当出现错误,buffevent有自己的错误处理机制,达到了异步I/O
struct bufferevent { struct event_base *ev_base; struct event ev_read; struct event ev_write; struct evbuffer *input;//接收数据缓冲 struct evbuffer *output;//发送数据缓冲 struct event_watermark wm_read; struct event_watermark wm_write; evbuffercb readcb;//读回调函数指针 evbuffercb writecb;//写回调函数指针 everrorcb errorcb;//错误回调函数指针 void *cbarg; int timeout_read; /* in seconds */ int timeout_write; /* in seconds */ short enabled; /* events that are currently enabled */ };
可以看出buffevent 内置了两个event (ev_read,ev_write),当有数据被读入input时,readcb被调用,当ouuput 完成时,writecb被调用,出现网络I/O错误时,errorcb 被调用
buffevent 的使用流程:
- 设置sock为非阻塞
evutil_make_socket_nonblocking(fd);
- 使用bufferevent_socket_new创建一个structbufferevent *bev,关联该sockfd,托管给event_base
struct bufferevent * bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options)bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
- 设置读/写的回调函数
void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg) eg. bufferevent_setcb(bev, readcb, NULL, errorcb, NULL);
- 设置事件类型
int bufferevent_enable(struct bufferevent *bufev, short event) eg. bufferevent_enable(bev, EV_READ|EV_WRITE);
- 进入bufferevent_setcb回调函数:在readcb里面从input中读取数据,处理完毕后填充到output中;
-
writecb对于服务端程序,只需要readcb就可以了,可以置为NULL;
errorcb用于处理一些错误信息。
例子:简单的服务器
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <event.h> #include<event2/buffer.h> #include<event2/bufferevent.h> void errorcb(struct bufferevent *bev, short error, void *ctx); int Listener() { int listener=socket(AF_INET,SOCK_STREAM,0); if(listener==-1) { return -1; } struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8000); saddr.sin_addr.s_addr=inet_addr("127.0.0.1"); int res=bind(listener,(struct sockaddr *)&saddr,sizeof(saddr)); if(res==-1) { return -1; } listen(listener,5); return listener; } void readcb(struct bufferevent* bf,void *ct) { char buff[1024]; int len=bufferevent_read(bf,buff,1023); printf("%s\n",buff); char *p = "Hi Client, Server received data succeed"; bufferevent_write(bf, p, strlen(p)+1); } void writecd(struct bufferevent * bev,void *ctx) { printf("succed\n"); } void do_accept(int fd,short ev,void*arg) { struct event_base * base=(struct event_base*)arg; struct sockaddr_in caddr; socklen_t len = sizeof(caddr); int c=accept(fd,(struct sockaddr*)&caddr,&len); evutil_make_socket_nonblocking(c); if(c<0) { perror("accept error"); } else if(c>FD_SETSIZE) { close(c); } else { //使用bufferevent_socket_new创建一个struct bufferevent *bev,关联该sockfd,托管给event_base ////BEV_OPT_CLOSE_ON_FREE表示释放bufferevent时关闭底层传输端口。这将关闭底层套接字,释放底层bufferevent等。 struct bufferevent *bf=bufferevent_socket_new(base,c,BEV_OPT_CLOSE_ON_FREE); //设置读写对应的回调函数 bufferevent_setcb(bf,readcb,writecd,errorcb,NULL); //启用读写事件,其实是调用了event_add将相应读写事件加入事件监听队列poll,如果相应事件不置为true,bufferevent 是不会读写数据的。 bufferevent_enable(bf,EV_READ|EV_WRITE); } } void errorcb(struct bufferevent *bev, short error, void *ctx) { if (error & BEV_EVENT_EOF) { /* connection has been closed, do any clean up here */ printf("connection closed\n"); } else if (error & BEV_EVENT_ERROR) { /* check errno to see what error occurred */ printf("some other error\n"); } else if (error & BEV_EVENT_TIMEOUT) { /* must be a timeout event handle, handle it */ printf("Timed out\n"); } bufferevent_free(bev); } int main() { int listener=Listener(); assert(listener!=-1); struct event_base * base =event_base_new(); assert(base!=NULL); struct event * listen_ev=event_new(base,listener,EV_READ|EV_PERSIST,do_accept,(void*)base); event_add(listen_ev,NULL); event_base_dispatch(base); event_base_free(base); }
来源:https://www.cnblogs.com/lc-bk/p/12389943.html