文章目录
libevent 简介
libevent 库全部是基于 “事件” 的异步通信模型。
- 专注:网络应用开发开源库。
- 精简:相较于其他开源库,很小。
- 开源:源代码开放。
- 跨平台:windows、Linux、MacOs、unix …
- 库下载路径:http://libevent.org
libevent 库安装及测试
- 加压缩 : tar zxvf libevent-2.1.8-stable.tar.gz
- 进入解压缩后的目录。cd
- 执行 ./configure 命令。—— 检测当前安装环境是否满足。生成 makefile 文 件
- 执行 make 命令。编译 libevent 库,生成对应文件。
- 执行 sudo make install 命令。安装 libevent 库(本质:将指定配置文件,拷 贝存放到指定目录)。
- 验证:
1. cd 到 sample 目录,查看到 hello-world.c 文件
2. 编译 hello-world.c 生成可执行文件。
gcc hello-world.c -o hello -levent
-L 库路径。 -I 头文件目录位置。
3. ./hello 启动可执行程序
4. 使用 nc 借助 9995 端口测试。 nc 127.1 9995 ——> 看到服务器回发的 “hello,world!” 数据。
7. 生成的 libevent.so 动态库的存储目录位置为: /usr/local/lib
两种状态
未决和非未决态
- 未决:有资格被处理,但尚未被处理。
- 非未决:没有资格被处理。
libevent 框架
- 创建 event_base
#include <event2/event.h>
struct event_base *event_base_new(void);
// 例
struct event_base *base = NULL;
base = event_base_new();- 常规事件 event —> event_new()
带缓冲区事件 bufferevent —> bufferevent_socket_new();- 启动循环监听
int event_base_dispatch(struct event_base *base);
// 相当于在 该函数内部,封装了 while(1) { … }
但是,必须要借助 event_new() 中的 what 形参 指定了 EV_PERSIST 宏,才 while(1), 否则只执行一次。
// 该函数以后的代码,必须要该函数执行结束(跳出循环)才有执行机会。
// 例:
event_base_dispatch(base);- 释放 base
void event_base_free(struct event_base *base);
// 例:
event_base_free(base);
相关函数
查看 支持哪些 多路IO
const char **event_get_supported_methods(void);
void test()
{
//const char **event_get_supported_methods(void);
char** res = event_get_supported_methods();
for (int i = 0; i < 5; i++)
{
printf("%s\n", res[i]);
}
}
查看 当前用的 多路IO
const char * event_base_get_method(const struct event_base *base);
void test()
{
//const char * event_base_get_method(const struct event_base *base);
struct event_base* base = event_base_new();
const char* res = event_base_get_method(base);
printf("%s\n", res);
}
查看 fork后子进程使用的 event_base
int event_reinit(struct event_base *base);
成功: 0, 失败: -1
使用该函数后, 父创建的base才能在子进程中生效。
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:待监听的 文件描述符。
what:待添加的 监听事件。
EV_READ: 读事件
EV_WRITE: 写事件
EV_PERSIST: 持续触发。 结合 event_base_dispatch() 使用。用 “|” 连接
cb:回调函数。
arg:回调使用的 参数。
返回值:成功创建的 常规事件 对象。
回调函数类型:
typedef void (*event_callback_fn)(evutil_socket_t fd, short what, void *arg)
// 例:
void read_cb(evutil_socket_t fd, what, arg)
{
Read();
====数据处理====
write();
reutrn ;
}
添加事件到 base 上
int event_add(struct event *ev, const struct timeval *tv);
ev: event_new()函数返回的事件
tv: 超时时间(秒、微秒)如果为NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用。
将事件从 base上拿下
int event_del(struct event *ev);
ev:event_new() 函数返回的事件
释放事件
int event_free(struct event *ev);
成功: 0, 失败: -1
带缓冲区的事件 bufferevent
- bufferevent 特性
- 带有两个缓冲区的 event。一个读缓冲、一个写缓冲。
bufferevent 创建、释放
- 创建
// 对应 event_new(), 但,没有指定 回调函数。
struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);
base: event_base_new() 返回值。“积木底座”
fd:待监听的 文件描述符。
options:BEV_OPT_CLOSE_ON_FREE。关闭 socket时,释放 bufferevent
返回值:成功创建一个 bufferevent 对象。
- 释放
void bufferevent_free(struct bufferevent *bev);
bev: bufferevent_socket_new() 返回值。
- 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: 读回调 ——> 读缓冲
writecb: 写回调 ——> 写缓冲,可以传NULL
eventcb: 事件回调 ——> 通常传 NULL
cbarg:上述 回调函数使用的参数
- 读回调类型:
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
bev: bufferevent_socket_new() 返回值。
ctx: bufferevent_setcb() 函数的 最后一个参数 cbarg
// 例:
void read_cb(struct bufferevent *bev, void *ctx)
{
// 不能使用 read() 因为没有fd。 fd 被封装到 bev 对象内部。
bufferevent_read();
}
// 上述回调函数,在 bev 对象的读缓冲区中,有数据时,会被自动调用(回调)。
// bufferevent_read()函数原型。
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
- 写回调类型:
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx)
bev: bufferevent_socket_new() 返回值。
ctx: bufferevent_setcb() 函数的 最后一个参数 cbarg
// 例:
先调用 bufferevent_write(); 将数据写入到 bev 的 写缓冲中,写缓冲刷新之后,下述函数才会被回调。
void write_cb(struct bufferevent *bev, void *ctx)
{
// 打印,写操作完成!!!
}
// bufferevent_write()函数原型。
size_t bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
- 事件回调类型:
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
bev: bufferevent_socket_new() 返回值。
events:客户端建立连接时,使用 BEV_EVENT_CONNECTED 传参。
ctx: bufferevent_setcb() 函数的 最后一个参数 cbarg
// 例:
void event_cb(struct bufferevent *bev, short events, void *ctx)
{
// 事件满足时,回调的处理东西
}
启用缓冲区
-
默认创建的 bufferevent 对象具备r、w 两个缓冲区。
-
写缓冲,默认是 enable的。 可以直接写入数据。
-
读缓冲,默认是 disable的。不可以直接从中读数据。
打开读缓冲的权限。使用 bufferevent_enable 函数
void bufferevent_enable(struct bufferevent *bufev, short events);
bev: bufferevent_socket_new() 返回值。
events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
// 例:
bufferevent_enable(bufev, EV_READ);
其他操作:
- 禁用缓冲区:
void bufferevent_disable(struct bufferevent *bufev, short events);
查看缓冲区的使能状态:
short bufferevent_get_enabled(struct bufferevent *bufev);
客户端连接服务器
int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);
bev: bufferevent_socket_new() 返回值。
address: 服务器端的地址结构(IP+port) (connect()函数的参2)
addrlen: address的大小。 (connect()函数的参3)
创建监听客户端连接的监听器
// 创建服务器监听器 —— 监听客户端连接请求
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_new() 函数返回值 —— “积木底座”
cb:回调函数。
ptr:回调函数参数。
flags:标志位:
LEV_OPT_CLOSE_ON_FREE: 结束时,释放 bufferevent 对象。
LEV_OPT_REUSEABLE:设置 端口复用。 使用 “|” 设定两个值。
backlog:listen()的第二个参数。 传-1 ---> 128
sa:服务器地址结构(IP+port)
socklen: sa的大小。
返回值:成功创建的监听器。
evconnlistener_cb 回调函数的类型
typedef void (*evconnlistener_cb)(struct evconnlistener *listener,
evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr);
listener:evconnlistener_new_bind() 函数返回值。
sock: 通信套接字。cfd
addr: 传出。客户端地址结构
len: addr大小。
ptr:evconnlistener_new_bind() 函数的 ptr 参数。
// 例:
void listen_cb(struct evconnlistener *listener,
evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr)
{
// 该回调函数,当有客户端连接请求发生时,由系统自动调用并传参。
}
- 释放监听器
void evconnlistener_free(struct evconnlistener *lev);
libevent实现 TCP 服务器实现流程
具体实现demo请看GitHub
- 创建 base 。 struct event_base *base = event_base_new();
- 创建、初始化服务器地址结构 sockaddr_in srv_addr;
- 创建 服务器监听器 listener。 调用 evconnlistener_new_bind()
- 设置回调 listener_cb() 实现。将 base 传参到回调函数内。
- 创建 bufferevent 对象。bev = bufferevent_socket_new(base)
- 给 bufferevent 设置回调。 bufferevent_setcb(bev, read_cb, write_cb, NULL)
- 实现 read_cb 回调函数。
- bufferevent_read() 读客户端数据。
- 打印读到的数据。(小 – 大)
- bufferevnet_write() 写数据给客户端。- 设置回调完成后,开启读缓冲区权限。bufferevent_enable(EV_READ)
- 启动监听 base。 event_dispatch(base);
- 释放事件。listener、base。
libevent实现 TCP 客户端实现流程
创建 event_base。 创建 socket() --- fd
使用 bev = bufferevnet_socket_new(fd) 创建一个用跟服务器通信的 bufferevnet 事件对象
使用 bufferevnet_socket_connect(bev) 连接 服务器
使用 bufferevent_setcb() 给 bufferevnet对象的 read、write、event 设置回调
设置 bufferevnet对象的读缓冲区 enable
在 read_cb 回调函数内,接收、发送数据 bufferevent_read() / bufferevent_write()
启动循环监听 event_base_dispath(base)
释放资源。
简单使用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
void read_cb(struct bufferevent *bev, void *ctx)
{
char buf[4096] = {0};
int ret = bufferevent_read(bev, buf, sizeof(buf));
printf("sever read: %s\n", buf);
char *p = "this is a test for bufferevent server\n";
bufferevent_write(bev, p, strlen(p)+1);
}
void write_cb(struct bufferevent *bev, void *ctx)
{
printf(" ---- bufferevent_write finish!!!\n");
}
void listen_cb(struct evconnlistener *listener,
evutil_socket_t cfd, struct sockaddr *addr, int len, void *ptr)
{
struct event_base *base = (struct event_base *)ptr;
struct bufferevent *bev = NULL;
bev = bufferevent_socket_new(base, cfd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, read_cb, write_cb, NULL, NULL);
bufferevent_enable(bev, EV_READ);
}
int main(int argc, char *argv[])
{
struct event_base *base = NULL;
base = event_base_new();
struct sockaddr_in srv_addr;
bzero(&srv_addr, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(9999);
struct evconnlistener * listener = NULL;
listener = evconnlistener_new_bind(base, listen_cb, base, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
-1, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}