1、带缓冲区事件函数使用流程
安装: Linux c libevent库安装(简单使用)
原理: bufferevent 有两个缓冲区:也是队列实现,读走没,先进先出 读缓冲:有数据-->读回调函数被调用-->使用bufferevent_read()函数读-->读数据 写缓冲:使用bufferevent_write()-->向写缓冲中写数据(写缓冲一旦有数据就会将数据发给对端,然后在调用设置的写回调函数,所以写回调有点鸡肋)-->该缓冲区有数据自动写出 ->写完,回调函数被调用
1.1、服务端基本流程
1、创建底座(创建框架)
struct event_base*event_base_new(void);
2、创建监听器
监听器中封装了(socket,bind,listen,accept)四个函数
struct evconnlistener*evconnlistener_new_bind(
struct event_base* base,
evconnlistener_cb cb,
void* ptr
unsigned flags,
int backlog,
const struct sockaddr* sa,
int socklen
);
// 回调函数类型:
typedefvoid(*evconnlistener_cb)(
struct evconnlistener* listener,
evutil_socket_t sock,
struct sockaddr* addr,
int len,
void* ptr
)
listener: evconnlistener_new_bind函数的返回值
sock:用于通信的文件描述符
addr:客户端的ip+短裤
len: addr的len
ptr:外部ptr传递进来值
// 该回调函数,不由我们调用,是框架自动调用,因此,只需知晓参数函数即可
参数:
base:event_base_new函数返回值(底座)
cb:回调函数一旦被回调,说明在其内部应该与客户端建立连接完成
ptr:回调函数参数
flags:
LEV_OPT_CLOSE_ON_FREE 关闭的是服务器端的监听socket
LEV_OPT_REUSEABLE 设置端口复用
backlog:listen函数参数2,-1表示最大值
sa:服务器地址结构
socklen:服务器地址结构大小
返回值:
成功:创建监听器
3、创建事件
struct bufferevent*bufferevent_socket_new(struct event_base* base,evutil_socket_t fd,enum bufferevent_options options);
参数:
base:event_base
fd:封装到bufferevent内的fd
options:BEV_OPT_CLOSE_ON_FREE // 释放底层套接字(默认填)
返回值:
成功创建buffervent事件对象
4、给事件设置回调函数(在常规事件中,是由一个函数完成的,这里需要这两步才能完成事件创建)
voidbufferevent_setcb(struct bufferevent* bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevent_event_cb eventcb,
void* cbarg);
读写回调函数类型:
typedefvoid(*bufferevent_data_cb)(struct bufferevent* bev,void* ctx);
事件回调函数类型:
typedefvoid(*bufferevent_event_cb)(struct bufferevent* bev,short events,void* ctx);
参数:
bufev:bufferevent_socket_new()函数的返回值
readcb:读缓冲对应的回调(当)
writecb:写缓冲设置回调
evencb:NULL(设置事件回调)
cbarg :回调函数用的参数
5、启动缓冲区(默认:write是开的,read缓冲是关闭的)
voidbufferevent_enable(struct bufferevent* bufev,short events);
参数:
bufev:bufferevent_socket_new返回值
events:
EV_READ 打开读
EV_WRITE 打开写
EV_READ|EV_WRITE 打开读写
6、循环监听底座上面的事件
intevent_base_dispatch(struct event_base* base);
7、销毁监听器
voidevconnlistener_free(struct evconnlistener* listener)
8、销毁底座
voidevent_base_free(struct event_base* base);
2、代码
2.1、server.c
#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>
#define PORT 9999
// 读缓冲区有数据,回调函数自动被触发
voidread_cb(struct bufferevent* bev,void* arg)
{
char buf[1024]={0};
// 相当于read,因为文件描述符被封装到了bev中,使用使用bufferevent_read
bufferevent_read(bev,buf,sizeof(buf));
printf("client say:%s\n",buf);
char* p ="服务器:已经收到数据!!!";
// 写给客户端
// 向bufferevent写缓冲区中写数据,写缓冲一旦有数据就会将数据发给对端,然后调写回调函数
bufferevent_write(bev,p,strlen(p)+1);
sleep(1);
}
voidwrite_cb(struct bufferevent* bev,void* arg)
{
printf("成功写数据给客户端、写缓冲区回调函数被回调\n");
}
// 事件回调
voidevent_cb(struct bufferevent* bev,short events,void* arg)
{
if(events & BEV_EVENT_EOF)
{
printf("connection closed \n");
}
elseif(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
bufferevent_free(bev);
printf("bufferevent 资源已经被释放---\n");
}
// 监听器回调函数 (表示有客户端连接请求)
voidcb_listener(struct evconnlistener* listener,
evutil_socket_t sock,
struct sockaddr* addr,
int len,
void* ptr)
{
printf("connect new client\n");
struct event_base* base =(struct event_base*)ptr;
// 添加新事件
struct bufferevent* bev;
bev = bufferevent_socket_new(base,sock,BEV_OPT_CLOSE_ON_FREE);
// 给bufferevent缓冲区设置回调函数
bufferevent_setcb(bev,read_cb,write_cb,event_cb,NULL);
// 默认读缓冲是关闭的
bufferevent_enable(bev,EV_READ);
}
intmain(int argc,const char* argv[])
{
struct sockaddr_in ser_addr;
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(PORT);
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 创建底座
struct event_base* base = event_base_new();
// 创建监听器(套接字,绑定接受请求连接)
struct evconnlistener* listener;
listener = evconnlistener_new_bind(base,
cb_listener,
base,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
36,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
// 启动循环监听
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return0;
}
2.2、client.c
客户端流程:
1、创建socket
2、创建带缓冲区事件
3、建立连接
int bufferevent_socket_connect(struct bufferevent* bev,struct sockaddr* address,int addrlen);
参数:
bev:bufferevent 事件对象(封装了文件描述符)
address、len(服务器地址结构和长度):等同于connect参数2,3
4、设置事件回调函数(read、write、event)
5、循环监听
5、释放
#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>
voidread_cb(struct bufferevent *bev, void *arg)
{
char buf[1024]={0};
bufferevent_read(bev, buf,sizeof(buf));
printf("fwq say:%s\n", buf);
bufferevent_write(bev, buf,strlen(buf)+1);
sleep(1);
}
voidwrite_cb(struct bufferevent *bev, void *arg)
{
printf("----------我是客户端的写回调函数,没卵用\n");
}
voidevent_cb(struct bufferevent *bev, short events, void *arg)
{
if(events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
elseif(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
elseif(events & BEV_EVENT_CONNECTED)
{
printf("已经连接服务器...\\(^o^)/...\n");
return;
}
// 释放资源
bufferevent_free(bev);
}
// 客户端与用户交互,从终端读取数据写给服务器
voidread_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);
}
intmain(int argc, const char* argv[])
{
struct event_base* base =NULL;
base = event_base_new();
int fd = socket(AF_INET, SOCK_STREAM,0);
// 通信的fd放到bufferevent中
struct bufferevent* bev =NULL;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
// init server info
struct sockaddr_in serv;
memset(&serv,0,sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9999);
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);
// 设置读回调生效
// bufferevent_enable(bev, EV_READ);
// 创建事件
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);
return0;
}