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