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
只给一些接口的读权限,不给写权限,可以保证安全性。
整体使用流程
event_base_new
创建上下文- 初始化
sockaddr_in
结构体,用于evconnlistener_new_bind
绑定和监听 listen_cb
回调注册bufferevent_socket_new
用于等待建立一个连接bufferevent_setcb
向缓冲区注册回调函数,如果连接建立,会自动将文件描述符作为回调函数的参数传递到回调函数中- 利用注册好的回调函数,从缓冲区接受或者读取数据,以便做各种业务逻辑的开发
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;
}