libevent 是一款事件驱动的网络开发包,由于采用c语言开发 ,体积小,跨平台,速度极快,大量开源项目使用了libevent比如谷歌浏览器,和分布式告诉缓存系统memcached,libevnet支持kqueue,select,poll,epoll,iocp(非阻塞的工作模式),事件内部机制完全独立于公开的额事件api,libevnet 支持跨平台,可在linux,windows 等平台上编译,
优点:精简,跨平台,线程安全,专注于网络通信
下载--解压--安装
源码包安装:REANME
./config 检查安装环境 生成makefile
make 编译,生成.o可执行文件
sudo make install 将必要的资源拷贝到系统指定的目录
进入sample目录,运行demo验证 库安装使用的情况
编译使用库的.c时,需要加-levent选项
库名libevent.so /usr/lib
特点:基于“事件”异步通信模型--回调
创建 event_base(乐高的底座)
创建事件 event
将事件添加到 base上
循环监听事件满足
释放event_base
(1)创建 event_base(底座)
struct event _base *event_base_new(void);//创建函数;
struct event_base*base=evnet_base_new();
(2)创建事件
常规事件 event------>evnet_new();
bufferevent----------->bufferevnet_socket_new();
(3)将事件添加到base上
int event_add (struct *ev,const struct timeval *tv);
ev:event_new()函数返回的事件
TV:为null时,不会超时,意思是一直等到事件被触发,回调函数会被调用
为非0,等待期间检查事件没有被触发,时间到回调函数依然会被调用
(4)循环监听事件满足
int event_base_dispatch(struct event_base *base);
event_base_dispatch(base);成功返回0,失败返回-1;
只有event_new 中指定了EV_PERSIST才持续触发,否则只触发一次就跳出循环
通常这样:EV_WRITE|EV_PERSIST , EV_READ|EV_PERSIST
其他循环;
int event_base_loopexit(struct evnet_base *base,const struct timecal*tv);
在指定时间后停止循环
int event_base_loopbreak(struct evnet_base *base);立即停止循环
(5)释放event_base
void event_base_free(struct event_base*base);//释放函数;
event _base_free(base);
特征:基于事件的异步通信模型,所见皆事件,等某个条件到达时去执行某个函数(回调机制)-----回调
相关函数了解:
查看支持哪些多路io
const char ** evnet_get_supported_methods(void);
查看当前用的多路io
const char *event_base_get_method(const struct event_base *base);
查看fork()后子进程使用的event_base
int event_reinit(struct event_base *base);
成功0 失败-1;
使用该函数后,父进程创建的base在子进程生效。
#include
#include
#include
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int mian()
{
struct event_base *base=event_base_new();
/*
const char **buff;
buff=event_get_supported_methods();
for(int i=0;i<10;i++)
{
printf("buff[i]=%s\n",buff[i]);
}
*/
char *buf;
buf=event_base_get_method(base);
printf("buf=%s\n",buf);
return 0;
}
创建事件:struct eevent *event_new(struct evnet_base *base,evutil_socket_t fd,short what,evnet_callback_fn cb,void arg);
第一个参数base:底座
第二个参数:绑定到事件上的文件描述符
第三个参数:对该文件描述符监听什么事件,读,写,执行
第四个参数:指定回调函数
第五个参数:指定回调函数的参数
struct event *event_new(struct evnet_base *base,evutil_socket_t fd,short what,event_callbacj_fn cb,void arg);
第一个参数:event_base_new()的返回值 ,就是乐高的底座
第二个参数:绑定到event 上的文件描述符
第三个参数:对捆绑的文件描述符监听什么事情(读写异常r,w,e)
第四个参数:监听的事件满足条件,执行的回调函数
第五个参数:回调函数的参数
返回值:成功创建的ecent
what EV_READ EV_WORTE EV_PERSIST持续触发 结合event_base_dispatch()函数使用生效
回调函数类型
typedef void (*event_callback_fn)(evutil_socket_t fd,short,void*);
添加事件到base上
int event_add(event_new *ev,const struct timeval *tv);
ev:event_new()函数的返回值
tv:
为NULL 不会超时
为非零,等待时间,检查的时间没有被触发,时间到,函数依然会被调用
销毁事件
int event_del(struct event *ev);
//将事件从base上拿下来
ev:event_new()的返回值
销毁事件
int evnet_free(struct event*ev);
ev:event_new()的返回值
未决:创建的事务对象有资格被处理,但是还没有被处理
非未决:创建的事务对象没有资格被处理
默认创建的新事件是非未决状态的,因为事务还没有添加到base上
bufferevent
#inlcude
原理:bufferevent有两个缓冲区,借助消息队列实现,内部的数据只能读一次,先进先出
读:有数据->读回调函数被调用->使用bufferevent_read()读数据
写:使用bufferevent_write()->向缓冲区写数据->该缓冲区有数据自动写出->写完,回调函数被调用
创建
struct bufferevent *ev;
struct bufferevnet *bufferevent_socket_new(struct event_base *base,evutil_socket_t fd,enum bufferevent_options options);
base: event_base_new()函数的返回值
fd: 跟bufferevent绑定的文件描述符,类比event_new()
options: BEV_OPT_CLOSE_ON_FREE,只用这一个即可
返回:成功创建bufferevent事件对象
销毁
void bufferevent_free(struct bufferevent *ev);
ev//上面函数的返回值;
struct bufferevent *ev;
struct bufferevnet *bufferevent_socket_new(struct event_base *base,evutil_socket_t fd,enum bufferevent_options options);
base: event_base_new()函数的返回值
fd: 跟bufferevent绑定的文件描述符,类比event_new()
options: BEV_OPT_CLOSE_ON_FREE,只用这一个即可
返回:成功创建bufferevent事件对象
/
/
/
void bufferevnet_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevnet_event_cb evnetcb,
void *cbag);
bufev: bufferevnet_socket_new()函数的返回值
readcb:读缓冲对应的回调,自己封装,在其内部读数据,注意使用bufferevnet_read()读,而不是read()
writecb:不用它,传空即可
eventcb:可以传空
cbarg:回调函数用的参数
readcb对应的回调函数:
typedef void (*bufferevent_data_cb)(struct bufferevent*bev,void ctx);
如:
void read_cb(struct bufferevnet*bev,void arg)
{
bufferevent_read();//读数据,类似于read()
}
读数据从bufferevent输入缓冲区中移除数据
bufferevnet_read()函数原型
size_t bufferevent_read(struct bufferevent *bufev,void *data,size_t size);
通常在readcb中,替代read()
bufferevnet_write()函数原型
int bufferevent_write(struct bufferevent *bufev,const void *data,size_t size);
通常在buff erevent_read之后,替代write()
typedef void (*bufferevent_event_cb)(struct bufferevnet *bev,short events,void *ctx);
void event_cb(struct bufferevnet *bev,short events,void ctx)
{
.....
}
events:BEV_EVENT_CONNECTED
默认:新建的bufferevnet写缓冲是enable的,读缓冲是disable的
void bufferevnt _enable(struct bufferevnt *bufev,short evnets);
启用缓冲区
通常用发来启用bufferevent的read缓冲区
void bufferevent_disable(structevent *bufev,short events);
禁用缓冲区
events:EV_READ,EV_WRITE,EV_READ|EV_WRITE
short bufferevent_get_enable(struct bufferevnet *bufev);
获取缓冲区的近用状态,需要借助&来得到
客户端:socket();connect();
int bufferevnet_socket_connect(struct bufferevnet *bev,struct sockaddr*address,int addrlen);
bev:bufferevnet事件对象,封装了fd
address和addrlen就是connect()函数的第二第三个参数
服务器:socket(),bind(),listen();accept();
//创建监听服务器
struct evconnlistener*listener;
struct evconnlistener*evconnlistener_new_bind(
struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog,
const struct sockaddr *sa,
int socklen);
//释放监听服务器
返回值为空
evconnlistener_free(listener);
base:evnet_base
cb:回调函数,一旦被回调,说明在其内部应该与客户端完成读写操作,进行通信
ptr:表示回调函数的参数
flags:表示可以识别的标志
LEV_OPT_CLOSE_ON_FREE:释放bufferevent时关闭底层连接端口,这将关闭底层套接字,释放底层bufferevent等
LEV_OPT_REUSEABLE端口复用
backlog:listen()的第二个参数,传 -1,表示使用默认最大值
sa:服务器的ip+port,,服务器自己的地址结构
socklen:sa 的大小,,服务器自己的地址结构大小
返回值:成功创建的监听器
typedef void(*evconnlistener_cb)(struct evconnlistener*listener,
evutil_socket_t sock;
struct sockaddr*addr,
int len,
void *ptr
);
listener:evconnlisten_new_bind()的返回值
sock:用于与客户端通信的文件描述符 cfd
addr:客户端的地址结构
len:客户端地址结构的长度
ptr:外部ptr传进来的值
注:该回调函数不是由我们调用的,是框架自动调用,因此只需要直到参数含义
该回调函数完成与客户端的读写操作
里面使用bufferevnet_read() bufferevnet_write()去读写
#include
#include
#include
#include
#include
#include
#include
#include
#include
//读缓冲区
void read_cb(struct bufferevnet *bev,void arg)
{
char buf[1024]={0};
bufferevnet_read(bev,buf,sizeof(buf));
printf("client say:%s\n",buf);
char *p="我是服务器,已经受到了你写的数据";
//写数据给客户端
bufferevnet_write(bev,p,strlen(p)+1);
sleep(1);
}
//写缓冲区回调
void write_cb(struct bufferevnet *bev,void *arg)
{
printf("我是服务器,成功写数据给客户端,写缓冲区回调函数被回调\n");
}
//监听回调 这个函数被调用说明客户端已经连接上来了
void cb_listener(struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *addr,
int len,void ptr)
{
printf("connect new client\n");
struct event_base*base=(struct event_base*)ptr;
//添加事件
struct bufferevnet *bev;
bev=bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);
//给Bufferevent缓冲区设置回调
bufferevnet_setcb(bev,read_cb,write_cb,event_cb,NULL);
//启用bufferevent的读缓冲区,默认是disable的
bufferevent_enable (bev,EV_READ);
}
int main(int argc,const char*argv[])
{
//初始化服务器
struct socket_in serv;
memset(&serv,0,sizeof(serv));
serv.sin_family=AF_INET;//指定服务器的地址结构
serv.sin_port=htons(9876);//指定ipv4端口号
serv.sin_addr.s_addr=htonl(INDOOR_ANY);//指定ipv4IP地址
//创建event_base框架
struct event_base *base;
base=event_nase_new();
/*
struct bufferevent *bev;
bev=bufferevent_socket_new();
这个时间要在有链接之后才能读写数据,应该先监听有描述符了之后在进行读写
*/
struct evconnlistener*listener;
listener=evconnlistener_new_bind(base,cb_listener,base,LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
36,(struct sockaddr*)&serv,sizeof(serv));
event_base_diapatch(base);
evconnlistener_free(listener );
event_base_free(base);
return 0;
}