libevent库bufferevent事件
带缓冲区的事件 bufferevent
#include <event2/bufferevent.h>
read/write 两个缓冲. 借助 队列.
创建、销毁bufferevent:
struct bufferevent *ev;
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 :释放 bufferevent 时关闭底层传输端口。这将 关闭底层套接字,释放底层
bufferevent
返回: 成功创建的 bufferevent事件对象。
void bufferevent_socket_free(struct bufferevent *ev);
给bufferevent设置回调:
对比event: event_new( fd, callback ); event_add() -- 挂到 event_base 上。
bufferevent_socket_new(fd) bufferevent_setcb( callback )
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
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
void event_cb(struct bufferevent *bev, short events, void *ctx)
{
。。。。。
}
events: BEV_EVENT_CONNECTED
cbarg: 上述回调函数使用的 参数。
read 回调函数类型:
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void*ctx);
void read_cb(struct bufferevent *bev, void *cbarg )
{
.....
bufferevent_read(); --- read();
}
bufferevent_read()函数的原型:
size_t bufferevent_read(struct bufferevent *bev, void *buf, size_t bufsize);
write 回调函数类型:
int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
启动、关闭 bufferevent的 缓冲区:
void bufferevent_enable(struct bufferevent *bufev, short events); 启动
events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
默认、write 缓冲是 enable、read 缓冲是 disable
bufferevent_enable(evev, EV_READ); -- 开启读缓冲。
libevent库实现C/S模型通信
客户端流程:
流程与基于socket套接字的流程一样,但是需要将套接字换成bufferevent事件,事实上,可以将buffevent事件看成
socket套接字与事件和事件处理器(回调函数)封装在一起的产物。
1. 创建bufferevent事件 :struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);
2. 连接服务器: bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr serv, size_t len);
3. 给bufferevent事件设置回调函数(注意与event事件的区别,event事件创建时就把回调函数封装进事件了)
void bufferevent_setcb(struct bufferevent * bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevent_event_cb eventcb,
void *cbarg );
4. 设置读回调生效。默认、write 缓冲是 enable、read 缓冲是 disable(注意!!!)
服务器流程:
1. 创建event_base
struct event_base* base = event_base_new();
2. 使用 evconnlistener_new_bind 创建监听服务器,
设置其回调函数,当有客户端成功连接时,这个回调函数listner_cb会被调用。
----实现socket();bind();listen();accept()的功能;
struct evconnlistener * listner
struct evconnlistener *evconnlistener_new_bind (
struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog,
const struct sockaddr *sa,
int socklen);
base: event_base
cb: 回调函数。 一旦被回调,说明在其内部应该与客户端完成(内核调用), 数据读写操作,进行通信。
typedef void (*evconnlistener_cb)(struct evconnlistener *listener, evutil_socket_t sock, struct sockaddr *addr, int len, void * ptr);
listener 参数是接收连接的连接监听器 。
sock 参数是 新接收的套接字。 (用于与客户端通信的cfd, 原来由accept返回值返回)
addr 和 len 是接收连接的地址和地址长度。(客户端的地址和地址长度,原来是accept参数返回)
ptr 是调 用 evconnlistener_new() 时用户提供的指针。
ptr: 回调函数的参数
flags: LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE
LEV_OPT_REUSEABLE:设置这个标志会让 libevent 标记套接字是可重用的,这样一旦关闭,可
以立即打开其他套接字,在相同端口进行监听
backlog: listen() 2参。 -1 表最大值
sa:服务器自己的地址结构体
socklen:服务器自己的地址结构体大小。
返回值:成功创建的监听器。
3. 封装 listner_cb() 在函数内部。
2.1 创建bufferevent事件对象。bufferevent_socket_new();
参数需要与客户端通信的cfd,listner_cb回调后才能得到cfd,所以要在回调函数里创建事件
2.2 使用bufferevent_setcb() 函数给 bufferevent的 read、write、event 设置回调函数。
2.3 当监听的 事件满足时,read_cb会被调用, 在其内部 bufferevent_read();读
4. 设置读缓冲、写缓冲的 使能状态 enable、disable
5. 启动循环 event_base_dispath();
6. 释放连接。
client.c
#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>
void read_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);
}
void write_cb(struct bufferevent *bev, void *arg)
{
printf("----------我是客户端的写回调函数,没卵用\n");
}
void event_cb(struct bufferevent *bev, short events, void *arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
else if(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
else if(events & BEV_EVENT_CONNECTED)
{
printf("已经连接服务器...\\(^o^)/...\n");
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;
}
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>
void read_cb(struct bufferevent *bev, void *arg)
{
char buf[1024] = {0};
bufferevent_read(bev, buf, sizeof(buf));
printf("client say: %s\n", buf);
char *p = "我是服务器, 已经成功收到你发送的数据!";
bufferevent_write(bev, p, strlen(p)+1);
sleep(1);
}
void write_cb(struct bufferevent *bev, void *arg)
{
printf("I'm服务器, 成功写数据给客户端,写缓冲区回调函数被回调...\n");
}
void event_cb(struct bufferevent *bev, short events, void *arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
else if(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
bufferevent_free(bev);
printf("buffevent 资源已经被释放...\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 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);
}
int main(int argc, const char* argv[])
{
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9876);
serv.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,
36, (struct sockaddr*)&serv, sizeof(serv));
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}