libevent学习——buffevent事件及低水位高水位设置

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;	
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值