libevent缓冲区实例

bufferevent

读、写、回调和读低水位、读取高水位、写入低水位

  • 读取低水位,假设设置为0,就意味着只要数据来了,就读,至于读多少,取决于client发送的包体有多大。再比如,如果规定字节是100个,只来了90个,那也不会回调,可能是数据包发错了,这是私有传输协议之间的规定。
  • 读取高水位,输入缓冲区中的数据量假设到了100个字节。bufferevent将停止读取,直到数据量低于100个,会阻塞。等于限定了一个条件,最大处理的最大数据量,一次发来1024个字节,如果每次只读取102.4就切成了十份。
  • 写入低水位,达到或低于此级别,写入回调就被调用,降低IO调度,保护硬盘写入速度。

bufferevent_socket_new(base,fd,flag)

BEV_OPT_CLOSE_ON_FREE释放时会关闭socket

BEV_OPT_THREADSAFE 线程安全,会自动分配锁

BEV_OPT_DEFER_CALLBACKS 延迟所有回调,防止有栈溢出,延迟回调不会立即调用,而是在event_loop()调用中被排队

BEV_OPT_UNLOCK_CALLBACKS 进入回调函数时不需要锁,防止再调用其他函数造成死锁

bufferevent_enable缓冲区的限制

EV_READ

EV_WRITE

只给一些接口的读权限,不给写权限,可以保证安全性。

整体使用流程

  1. event_base_new创建上下文
  2. 初始化sockaddr_in结构体,用于evconnlistener_new_bind绑定和监听
  3. listen_cb回调注册
  4. bufferevent_socket_new用于等待建立一个连接
  5. bufferevent_setcb向缓冲区注册回调函数,如果连接建立,会自动将文件描述符作为回调函数的参数传递到回调函数中
  6. 利用注册好的回调函数,从缓冲区接受或者读取数据,以便做各种业务逻辑的开发
struct event_base {
	const struct eventop *evsel;
}
struct eventop {
    int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
	int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
}

对于给定的evutil_socket_t fd进行事件的读写

int (*dispatch)(struct event_base *, struct timeval *);

事件循环的核心

会先清楚缓存时间,然后Socketpair用于从信号处理程序发送通知,其实说到本质,UDP也会从网卡发送一个signal通知到用户。

int
event_base_loop(struct event_base *base, int flags)
{
    const struct eventop *evsel = base->evsel;
    const struct eventop *evsel = base->evsel;
	struct timeval tv;
	struct timeval *tv_p;
	int res, done, retval = 0;
	EVBASE_ACQUIRE_LOCK(base, th_base_lock); // 加锁操作
    base->running_loop = 1;

	clear_time_cache(base);

	if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
		evsig_set_base_(base);

	done = 0;
    
}

eventop就是存储事件的核心

buferevent用例

 m_base = event_base_new();
    // 创建上下文

    m_ev = evconnlistener_new_bind(m_base,
		listen_cb,		//回调函数
		m_base,			//回调函数的参数arg
		LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
		10,				//listen back
		(sockaddr*)&m_sin,
		sizeof(m_sin)
		);

listen_cb监听到事件时,创建缓冲区上下文

回调函数的参数

struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
    evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
    const struct sockaddr *sa, int socklen);
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
  • 文件描述符
  • sockaddr*地址族
  • sizeof(sockaddr)
  • 需要传递的参数
void listen_cb(evconnlistener *ev,evutil_socket_t s,sockaddr*sin,int slen,void *arg)
{
	event_base *base = (event_base *)arg;
	//创建bufferevent上下文 BEV_OPT_CLOSE_ON_FREE清理bufferevent时关闭socket
	bufferevent *bev = bufferevent_socket_new(base,s,BEV_OPT_CLOSE_ON_FREE);

	//添加监控事件
	bufferevent_enable(bev,EV_READ|EV_WRITE);

	//设置回调函数
	bufferevent_setcb(bev,read_cb,write_cb,event_cb,base);

}

bufferevent_socket_new

创建socket缓冲区

bufferevent_enable(bev,EV_READ|EV_WRITE);设置缓冲区事件为读写事件

封装

master.hpp

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define PORT 5002
using namespace std;
class Cwork
{
    public:
    Cwork()
    {
        memset(&m_sin,0,sizeof(m_sin));
        m_sin.sin_family = AF_INET;
        m_sin.sin_port = htons(PORT);
    }
    ~Cwork()
    {
        event_base_free(m_base);
        evconnlistener_free(m_ev);
    }
    void init();	// 初始化创建上下文,设置回调函数
    void run();		// 开启事件轮询
   static void listen_cb(struct evconnlistener *m_ev, evutil_socket_t fd, struct sockaddr * serv_sock, int socklen, void *arg);
private:
    event_base* m_base;
    sockaddr_in m_sin;
    evconnlistener* m_ev;
    
};
// 事件和Work的解耦
class event_cb
{
    public:
    // 设置为static是因为普通的成员函数都有this指针,不满足回调函数的参数
    static void sread_cb(bufferevent *be,void *arg);
    static void swrite_cb(bufferevent *be,void *arg);
    static void events_cb(bufferevent *be,short events,void *arg); // 参数2 需要区分事件类型
};

master.cpp

类:work的实现

void Cwork::init()
{
    m_base = event_base_new();
    // 创建上下文

    m_ev = evconnlistener_new_bind(m_base,
		Cwork::listen_cb,		//回调函数
		m_base,					//回调函数的参数arg
		LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
		10,						//listen que
		(sockaddr*)&m_sin,
		sizeof(m_sin)
		);
}
void Cwork::run()
{
    event_base_dispatch(m_base);
}
// 通过此方法回调事件处理函数
void Cwork::listen_cb(struct evconnlistener *m_ev, evutil_socket_t fd, struct sockaddr * serv_sock, int socklen, void *arg)
{
    cout<<"listen "<<endl;
    event_base* base = (event_base*)arg;
    bufferevent *bev = bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);

    bufferevent_enable(bev,EV_READ|EV_WRITE);

   	 // 设置水位
    bufferevent_setwatermark(bev,EV_READ,
    5, // 低水位
    10  // 高水位 无限制
    ); 
    // 意思是,至少收到5个字节的数据才能激发回调, 一次发送超过10个字节的,回调会分两次来读取数据
    bufferevent_setcb(bev,event_cb::sread_cb,event_cb::swrite_cb,event_cb::events_cb,base);
}

类:event_cb的实现

void event_cb::events_cb(bufferevent *be,short events,void *arg)
{
    if(events & BEV_EVENT_TIMEOUT)
    {
        cout<<"timeout"<<endl;
        bufferevent_enable(be,EV_READ|EV_WRITE); // 超时可以重新将其纳入事件监听,如果可以,那就设置如果一个连接超时了三次
        // 这次区分一个链接的话,可以用个结构体将其包裹,附带独特的token或者flag
        // bufferevent_free(be);
    }
    else if(events & BEV_EVENT_ERROR)
    {
        cout<<"close()"<<endl;
        bufferevent_free(be); // 设置了关闭
    }
    cout<<"[E]"<<flush;	
}
void event_cb::swrite_cb(bufferevent *be,void *arg)
{
    cout<<"[W]"<<endl;
}
void event_cb::events_cb(bufferevent *be,short events,void *arg)
{
    cout<<"[E]"<<flush;	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值