目录
bufferevent_cb函数
设置buffevent_cb的函数原型
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
注意event_cb函数指针和readcb是不一样的,event_cb多了一个short参数,和之前学习的event事件的回调函数类似
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx);
read、write的函数指针类型
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
bufferevent_socket_new函数
函数原型如下,在学习buffevent的时候我一直困惑,究竟是怎么记录客户端的套接字?通过断点调试终于解决了自己的疑惑,这个函数是这篇博客新增的介绍函数,在没有充分了解之前不随意去写这个函数,以免给人带来误会。
struct bufferevent *
bufferevent_socket_new(struct event_base *base,
evutil_socket_t fd,int options)
函数的实现如下,event_assign函数被调用,通过bufferevent成员变量event类型把客户端的套接字给绑定到了一起。下面给出了bufferevent的成员变量
struct bufferevent *
bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
int options)
{
struct bufferevent_private *bufev_p;
struct bufferevent *bufev;
#ifdef _WIN32
if (base && event_base_get_iocp_(base))
return bufferevent_async_new_(base, fd, options);
#endif
if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)
return NULL;
if (bufferevent_init_common_(bufev_p, base, &bufferevent_ops_socket,
options) < 0) {
mm_free(bufev_p);
return NULL;
}
bufev = &bufev_p->bev;
evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);
event_assign(&bufev->ev_read, bufev->ev_base, fd,
EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);
event_assign(&bufev->ev_write, bufev->ev_base, fd,
EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);
evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);
evbuffer_freeze(bufev->input, 0);
evbuffer_freeze(bufev->output, 1);
return bufev;
}
bufferevent里面含有的成员变量
struct bufferevent {
/** Event base for which this bufferevent was created. */
struct event_base *ev_base;
/** Pointer to a table of function pointers to set up how this
bufferevent behaves. */
const struct bufferevent_ops *be_ops;
/** A read event that triggers when a timeout has happened or a socket
is ready to read data. Only used by some subtypes of
bufferevent. */
struct event ev_read;
/** A write event that triggers when a timeout has happened or a socket
is ready to write data. Only used by some subtypes of
bufferevent. */
struct event ev_write;
/** An input buffer. Only the bufferevent is allowed to add data to
this buffer, though the user is allowed to drain it. */
struct evbuffer *input;
/** An input buffer. Only the bufferevent is allowed to drain data
from this buffer, though the user is allowed to add it. */
struct evbuffer *output;
struct event_watermark wm_read;
struct event_watermark wm_write;
bufferevent_data_cb readcb;
bufferevent_data_cb writecb;
/* This should be called 'eventcb', but renaming it would break
* backward compatibility */
bufferevent_event_cb errorcb;
void *cbarg;
struct timeval timeout_read;
struct timeval timeout_write;
/** Events that are currently enabled: currently EV_READ and EV_WRITE
are supported. */
short enabled;
};
bufferevent讲解
个人理解,封装了recv和send函数,并且设置了水位,有两种水位:低水位和高水位
设置水位
void bufferevent_setwatermark(struct bufferevent *bufev,
short events,size_t lowmark, size_t highmark);
函数原型如上,第一个参数由bufferevent_socket_new
创建,第二个是设置读还是写的水位,第三个是低水位的值、第四个高水位的值
(1)读的低水位
0就是默认值,收到了就读了,当设置了低水位(下限)的值,收到了这么多的大小才会去处理,没有到达低水位的字节数的话就一直不处理。
(2)读的高水位
0也是默认值,设置高水位(上界)超过这个值的话就要分批处理了
(3)写的水位一直没有测试成功
bufferevent创建的过程
①先通过bufferevent_socket_new创建bufferevent的对象bev
②bufferevent_enable设置bev的属性(EV_READ 或者EV_WRITE),EV_READ设置可读事件、EV_WRITE设置可写事件。如果不设置EV_READ的话,客户端(telnet)发来的数据,服务器是不接受客户端的数据。这个函数一定要设置,默认是没有这两个属性的!
③bufferevent_setwatermark设置水位,如果低水位和高水位都不想设置的话可以不使用这个函数
④bufferevent_set_timeouts函数可以设置超时时间,第一个参数是bev,第二个参数是读超时时间,第三个是写超时时间
int bufferevent_set_timeouts(struct bufferevent *bufev,
const struct timeval *timeout_read, const struct timeval *timeout_write);
⑤bufferevent_setcb函数设置bev的回调函数们,第一个是bev,第二个是读的回调函数、第三个书写的回调函数,第四个是处理异常的回调函数,比如超时,第五个传任意一个参数
⑥具体代码看下面的完整代码
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
bufferevent* bev = bufferevent_socket_new(base,sock,BEV_OPT_CLOSE_ON_FREE);
//把新连接的数据的操作添加到bufferevent中去
bufferevent_enable(bev,EV_READ | EV_WRITE);
//设置低水位,最少多少数据才读
//高水位是超过这些数据就要控制接收的数据了
bufferevent_setwatermark(bev,EV_READ,5,//低水位
10//高水位就是0,相当于没有设置,默认就是0
);
/*没有测试出来写水位
//设置读取水位,字节数量低于低水位的话数据不会被发送
bufferevent_setwatermark(bev,EV_WRITE,20,//低水位
0//高水位就是0,相当于没有设置,默认就是0
);
*/
//设置超时时间
timeval t1 = {3,0};
bufferevent_set_timeouts(bev,&t1,nullptr);//前面是读超时时间,后面是写超时时间
//设置回调函数,读的、写的、异常(超时等)回调函数
bufferevent_setcb(bev,read_cb,write_cb,event_cb,base);
echo加超时的小服务器
#include <iostream>
#include <event2/listener.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <sys/types.h>
#include <event2/bufferevent.h>
#include <signal.h>
#include <sys/socket.h>
#include <string.h>
using namespace std;
//异常、超时、连接断开的情况下
void event_cb(bufferevent* bev,short events,void* arg){
cout<<"event_cb"<<endl;
if(events & BEV_EVENT_TIMEOUT){
cout<<"time out "<<endl;
//重新让他可读
//bufferevent_enable(bev,EV_READ);
goto err;
}
else if(events & BEV_EVENT_ERROR){
goto err;
}
else {
cout<<"others "<<endl;
}
err:
bufferevent_free(bev);
}
//这里的写和读都是指的是服务器的读和写
void write_cb(bufferevent* bev,void* arg){
cout<<"有数据可写"<<endl;
}
void read_cb(bufferevent* bev,void* arg){
char data[1024] = { 0 };
int len = bufferevent_read(bev,data,sizeof(data)-1);
if(len <= 0)return ;
if(strstr(data,"quit") != nullptr){
cout<<"quit"<<endl;
bufferevent_free(bev);
return ;
}
cout<<"收到数据"<<data<<endl;
//发送数据,echo服务器
bufferevent_write(bev,data,strlen(data));
}
void listen_cb(evconnlistener* ev,int sock,sockaddr* sin,
int sinlen,void* arg){
cout<<"listen_cb"<<endl;
cout<<sock<<endl;
event_base* base = static_cast<event_base*>(arg);
//创建buffevent上下文
//BEV_OPT_CLOSE_ON_FREE清理buffevent清理socket
//把sock也注册到了bufferevent中去了
bufferevent* bev = bufferevent_socket_new(base,sock,BEV_OPT_CLOSE_ON_FREE);
//把新连接的数据的操作添加到bufferevent中去
bufferevent_enable(bev,EV_READ | EV_WRITE);
//设置低水位,最少多少数据才读
//高水位是超过这些数据就要控制接收的数据了
bufferevent_setwatermark(bev,EV_READ,5,//低水位
10//高水位就是0,相当于没有设置,默认就是0
);
/*没有测试出来写水位
//设置读取水位,字节数量低于低水位的话数据不会被发送
bufferevent_setwatermark(bev,EV_WRITE,20,//低水位
0//高水位就是0,相当于没有设置,默认就是0
);
*/
//设置超时时间
timeval t1 = {3,0};
bufferevent_set_timeouts(bev,&t1,nullptr);//前面是读超时时间,后面是写超时时间
//设置回调函数,读的、写的、异常(超时等)回调函数
bufferevent_setcb(bev,read_cb,write_cb,event_cb,base);
}
int main(int argc,char** argv){
int port = 9996;
if(argc > 1){
port = atoi(argv[1]);
}
//忽略管道信号
if(signal(SIGPIPE,SIG_IGN) == SIG_ERR){
return 1;
}
//创建event的上下文
event_base* base = event_base_new();
if(base){
cout<<"event_base_new successful!"<<endl;
}
//创建网络服务器
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9996);
inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
evconnlistener* ev =evconnlistener_new_bind(base,listen_cb,base,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
10,(sockaddr*)&addr,sizeof(addr));
if(base)
event_base_dispatch(base);
if(ev){
evconnlistener_free(ev);
}
if(base)
event_base_free(base);
return 0;
}