【基于“事件”的异步通信模型】依赖回调机制
官方网站:libevent官方网站 相关资源:libevent相关资源
百度网盘链接:https://pan.baidu.com/s/17LeA3aGxgU7QUUFBt7zfpw 提取码:0ae7
1.libevent库安装
1.1 libevent库优点
- 事件驱动,高性能
- 开源,精简;
- 轻量级,专注于网络;
- 跨平台,支持Windows、Linux、Mac Os等;
- 支持多种I/O多路复用技术,epoll、poll、dev/poll、select和kqueue等;
- 支持I/O,定时器和信号等事件
1.2源码包安装步骤
- ./configure 检查安装环境,生成makefile;
- make 生成可执行文件;
- sudo make install 将必要的资源cp置系统指定目录
- 进入 sample 目录,运行demo验证库安装使用情况。
- 编译使用库时,需要加 -levent 选项。
??运行ln -s /usr/local/lib/libevent-1.4.so.2 /usr/lib/libevent-1.4.so.2命令(这是为了防止在系统默认路径下 找不到库文件,也可以使用gcc中的-L参数来指定库文件的位置所在)
2.libevent框架
- 创建event_base
struct event_base *event_base_new(void);
struct event_base *base =event_base_new();
- 创建事件
event_new();
bufferevent_socket_new();
- 将事件添加到 base上
int event_add(struct event *ev, const struct timeval *tv);
- 循环监听事件满足
int event_base_dispatch(struct event_base *base);
event_base_dispatch(base);
- 释放evemt_base
event_base_free(base);
2.1创建事件
struct event *ev;
struct event *event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb; void *arg);
参数:
??base: event_base_new()返回值
??fd: 绑定到 event 上的文件描述符
??what:对应的事件(r、w、e)
????EV_READ 一次读事件
????EV_WRTIE 一次写事件
????EV_PERSIST 持续触发。 结合 event_base_dispatch 函数使用,生效。
??cb:一旦事件满足监听条件,回调的函数
??typedef void (*event_callback_fn)(evutil_socket_t fd, short, void *)
??arg: 回调的函数的参数
返回值:成功创建的 event
2.2添加事件到 event_base
int event_add(struct event *ev, const struct timeval *tv);
参数:
??ev: event_new() 的返回值。
??tv:NULL(不会超时)
2.3释放事件
int event_free(struct event *ev);
??ev: event_new() 的返回值。
从event_base上删除事件【了解】
int event_del(struct event *ev);
??ev: event_new() 的返回值。
3.未决和非未决
未决事件:有资格被处理,但尚未被处理
非未决事件:没有资格被处理 )
4.带缓冲区的Bufferevent
4.1Bufferevent结构体
#include <event2/bufferevent.h>
bufferevent有两个缓冲区,队列实现,数据只能读一次
??读缓冲:读取了任意量的数据之后会调用读回调函数,使用bufferevent_read()函数从读缓冲区读取数据。
??写缓冲:bufferevent_write()函数向写缓冲中写入数据,写缓冲一旦有数据就会自动刷新,将数据发送给对端,写完后调用写回调函数。
4.2Bufferevent事件创建、释放
4.2.1创建基于套接字的 bufferevent
struct bufferevent *bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_optionsoptions);
参数:
??base:event _base
??fd:封装到bufferevent的文件描述符
???options:BEV_OPT_CLOSE_ON_FREE:释放 bufferevent 时关闭底层传输端口。这将关闭底层套接字,释放底层 bufferevent 等。
返回:
????成功:函数返回一个 bufferevent
??? ?失败:返回 NULL
4.2.2. 释放bufferevent操作
void bufferevent_free(struct bufferevent *bev);
4.3给读写缓冲区设置回调
bufferevent_setcb()函数修改 bufferevent 的一个或者多个回调
void bufferevent_setcb(
struct bufferevent *bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevent_event_cb eventcb,
void *cbarg);
参数:
????? bufev: bufferevent_socket_new() 返回值
?????readcb: 设置bufferevent 读缓冲,对应回调 read_cb{ bufferevent_read() 读数据 }
???writecb: 设置bufferevent 写缓冲,对应回调 write_cb { } ,给调用者,发送写成功通知。可以 NULL
????eventcb: 设置事件回调。也可传NULL
????cbarg:上述回调函数使用的参数。
-
readcb对应回调函数【从bufferevent的输入缓冲区移除数据】 typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
读数据:从bufferevent输入缓冲区中移除数据(队列) size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
-
writecb对应回调函数【向bufferevent的输出缓冲区添加数据】 int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
bufferevent_write()将内存中从 data 处开 始的 size 字节数据添加到输出缓冲区的末尾 int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
bufferevent_write_buffer()移除 buf 的所有内 容,将其放置到输出缓冲区的末尾 -
eventcb对应回调函数 typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
????BEV_EVENT_READING:读取操作时发生某事件,具体是哪种事件请看其他标志。
????BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志。
???BEV_EVENT_ERROR : 操 作 时 发 生 错 误 。 关 于 错 误 的 更 多 信 息 , 请 调 用
????EVUTIL_SOCKET_ERROR()。
????BEV_EVENT_TIMEOUT:发生超时.
????BEV_EVENT_EOF:遇到文件结束指示。
????BEV_EVENT_CONNECTED:请求的连接过程已经完成。
4.4禁用、启用缓冲区
void bufferevent_enable(struct bufferevent *bufev, short events) ;
void bufferevent_disable(struct bufferevent *bufev, short events) ;
short bufferevent_get_enabled(struct bufferevent *bufev);
events:EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE
默认:write 缓冲是 enable、read 缓冲是 disable,调用函数开启读缓冲
4.5客户端连接服务器
客户端:
int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);
bev:bufferevent事件对象(封装了fd)
address 和 addrlen 参数跟标准调用 connect()的参数相同
【注】:如果使用 bufferevent_socket_connect() 发起连接,将只会收到 BEV_EVENT_CONNECTED 事件。如果自己调用 connect(),则连接上将被报告为写入事件。
4.6服务器创建监听器
#include <event2/listener.h>
evconnlistener_new*()函数分配和返回一个新的连接监听器对象
struct evconnlistener * evconnlistener_new(
struct event_base *base
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog ,
evutil_socket_t fd);
evconnlistener_new_bind(
struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog ,
const struct sockaddr *sa,
int socklen);
参数:
???cb:监听回调函数,一旦回调,内部与客户端连接完成,进行数据通信
???ptr:传递给回调函数
???flags:“可识别的标志”
??????LEV_OPT_CLOSE_ON_FREE :如果设置了这个选项,释放连接监听器会关闭底层套接字
??????LEV_OPT_REUSEABLE :端口复用
???backlog:任何 时刻网络栈允许处于还未接受状态的最大未决连接数。-1:表示使用默认最大值
???sa:服务器的地址结构
???socklen:地址结构大小
返回:成功创建的监听器
释放监听器
void evconnlistener_free(struct evconnlistener *lev);
回调函数类型
typedef void (*evconnlistener_cb)(
struct evconnlistener *listener,
evutil_socket_t sock,
struct sockaddr *addr,
int len,
void * ptr);
参数:
? listener:接收连接的连接监听器
? sock:用于通信的文件描述符
? addr:客户端地址结构
? len:客户端地址结构的长度
? ptr:调用 evconnlistener_new() 时用户提供的指针
【注】:该回调函数由框架自动调用
5.libevent实现TCP服务器通信
5.1服务端实现TCP连接
-
创建event_base -
创建bufferevent事件对象,调用bufferevent_socket_new()函数 -
使用bufferevent_setcb() 函数给 bufferevent的 read、write、event 设置回调函数 -
当监听的事件满足时,read_cb会被调用,在其内部 bufferevent_read()读取数据 -
使用 evconnlistener_new_bind 创建监听服务器, 设置其回调函数,当有客户端成功连接时,这个回调函数会被调用 -
在函数内部封装 listner_cb() 函数。完成与客户端通信 -
启动循环event_base_dispatch() -
设置读写缓冲的使能状态 -
释放连接
5.2客户端实现TCP连接
-
创建event_base -
创建一个用于跟服务器通信的bufferevent事件对象,调用bufferevent_socket_new()函数 -
与服务器建立连接,调用bufferevent_socket_connect()函数 -
使用bufferevent_setcb() 函数给 bufferevent的 read、write、event 设置回调函数 -
设置读写缓冲的使能状态 -
接收、发送数据,bufferevent_read()、bufferevent_write() -
启动循环监听event_base_dispatch() -
释放连接
5.3服务端程序
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
using namespace std;
void read_cb(struct bufferevent *bev, void *data)
{
char buf[1024]={0};
bufferevent_read(bev,buf,sizeof(buf));
cout<<"client:"<<buf<<endl;
char *p="I am server,I have received your data";
bufferevent_write(bev,p,strlen(p)+1);
sleep(1);
}
void write_cb(struct bufferevent *bev, void *data)
{
cout<<"成功将数据写给客户端"<<endl;
}
void event_cb(struct bufferevent *bev, short events, void *data)
{
if (events & BEV_EVENT_EOF)
{
cout<<"connection closed"<<endl;
}
else if(events & BEV_EVENT_ERROR)
{
cout<<"some other error"<<endl;
}
bufferevent_free(bev);
cout<<"buffevent 资源已经被释放"<<endl;
}
void cb_listener(
struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *addr,
int len, void *ptr)
{
cout<<"connect new client"<<endl;
struct event_base*base=(struct event_base*)ptr;
struct bufferevent *bev;
bev=bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev,read_cb,write_cb,event_cb,NULL);
bufferevent_enable(bev, EV_READ);
return ;
}
int main (int argc,const char*argv[])
{
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(9876);
server.sin_addr.s_addr = htonl(INADDR_ANY);
struct event_base *base;
base=event_base_new();
struct evconnlistener *listener;
listener=evconnlistener_new_bind(
base,cb_listener,base,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
-1,
(struct sockaddr *)&server,
sizeof(server));
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
5.4客户端程序
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/bufferevent.h>
#include <event2/event.h>
#include <arpa/inet.h>
using namespace std;
void read_cb(struct bufferevent *bev, void *arg)
{
char buf[1024] = {0};
bufferevent_read(bev, buf, sizeof(buf));
cout<<"fwq say:"<<buf<<endl;
bufferevent_write(bev, buf, strlen(buf)+1);
sleep(1);
}
void write_cb(struct bufferevent *bev, void *arg)
{
cout<<"----------我是客户端的写回调函数"<<endl;
}
void event_cb(struct bufferevent *bev, short events, void *arg)
{
if (events & BEV_EVENT_EOF)
{
cout<<"connection closed"<<endl;
}
else if(events & BEV_EVENT_ERROR)
{
cout<<"some other error"<<endl;
}
else if(events & BEV_EVENT_CONNECTED)
{
cout<<"已经连接服务器..."<<endl;
return;
}
bufferevent_free(bev);
}
void read_terminal(evutil_socket_t fd, short what, void *arg)
{
char buf[1024] = {0};
int len = read(fd, buf, sizeof(buf));
struct bufferevent* bev = (struct bufferevent*)arg;
bufferevent_write(bev, buf, len+1);
}
int main(int argc, const char* argv[])
{
struct event_base* base = NULL;
base = event_base_new();
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct bufferevent* bev = NULL;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9876);
inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));
bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
struct event* ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST,
read_terminal, bev);
event_add(ev, NULL);
event_base_dispatch(base);
event_free(ev);
event_base_free(base);
return 0;
}
5.5运行结果
服务端: ) 客户端: )
|