一、bufferevent的基本概念
bufferevent 是 libevent 中的一个事件缓冲 IO,内部实现了基本 socket recv/send 操作 ,用户只需要调用 bufferevent 的 API 即可实现数据的读写。
(1)缓冲区:每个 bufferevent 都有一个读缓冲区(input)和写缓冲区(output),数据结构为 evbuffer ,它是一个数据缓冲区,用于缓存网络上发送或接收的数据。
(2)回调:
// 读取和写入回调
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
// 事件回调,其中what表示发生的事件,具体后面有说明
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx);
(3)水位:可以通过设置水位来调节回调函数的调用。
(3.1)读取低水位:输入缓冲区的数据量达到或超过此水位时,调用回调函数,即数据量达不到水位要求,则不会调用回调;
(3.2)读取高水位:输入缓冲区的数据量达到此水位时,bufferevent 将停止读取,直到输入缓冲区中的数据被读取,使得数据量低于此水位。注意用于限制一次读取的最大数据量。
(3.3)写入低水位:输出缓冲区中的数据量达到或低于此水位时,写入回调将被调用;
(3.4)写入高水位:不设置,直接写入即可,一般不用。
默认情况下,读取低水位和高水位都为 0,表示有数据就读取,且不会限定最大读取数量。写入的水位也是一样,表示可写即会全部写入。
(4)事件回调函数中多了一个 what 参数,表示发生的事件:
// 读取错误
#define BEV_EVENT_READING 0x01 /**< error encountered while reading */
// 写入错误
#define BEV_EVENT_WRITING 0x02 /**< error encountered while writing */
// 到达文件结尾
#define BEV_EVENT_EOF 0x10 /**< eof file reached */
// 不可恢复的错误
#define BEV_EVENT_ERROR 0x20 /**< unrecoverable error encountered */
// 到达直到的超时时间
#define BEV_EVENT_TIMEOUT 0x40 /**< user-specified timeout reached */
// 连接操作完成
#define BEV_EVENT_CONNECTED 0x80 /**< connect operation finished. */
二、bufferevent的基本接口
bufferevent 的接口都位于头文件 <event2/bufferevent.h> 中。
1、创建 bufferevent 上下文接口
struct bufferevent *bufferevent_socket_new(
struct event_base *base, evutil_socket_t fd, int options);
参数说明:
(1)base :libevent 上下文;
(2)fd :socket 描述符,bufferevent 的读写操作都基于此描述符;
(3)options :可选项,定义如下:
enum bufferevent_options {
// 释放bufferevent时,关闭底层socket传输
BEV_OPT_CLOSE_ON_FREE = (1<<0),
// 线程安全,即在bufferevent中使用lock,此时callback也会被加锁
BEV_OPT_THREADSAFE = (1<<1),
// 在事件循环中延迟回调
BEV_OPT_DEFER_CALLBACKS = (1<<2),
// 不对回调函数加锁,即便设置了BEV_OPT_THREADSAFE也不加锁
// 此选项需要与BEV_OPT_DEFER_CALLBACKS一起使用,未来可能会移除这一要求
BEV_OPT_UNLOCK_CALLBACKS = (1<<3)
};
一般使用 BEV_OPT_CLOSE_ON_FREE 选项,表示在 在 bufferevent_free 时也会关闭 socket 。
2、开启/关闭 bufferevent 操作
// 开启
int bufferevent_enable(struct bufferevent *bufev, short event);
// 关闭
int bufferevent_disable(struct bufferevent *bufev, short event);
event :指定使能事件,一般就是读写事件 EV_READ | EV_WRITE;
3、设置 bufferevent 的回调函数
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
包括 read、write、event 三个回调函数。
4、读取 bufferevent 缓冲区
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
参数说明:
(1)data :存储数据的缓冲区;
(2)size :存储数据的缓冲区长度。
5、写入 bufferevent 缓冲区
int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
返回值:成功返回 0,失败返回 -1。
6、设置 bufferevent 超时时间
int bufferevent_set_timeouts(struct bufferevent *bufev,
const struct timeval *timeout_read, const struct timeval *timeout_write);
参数说明:
(1)timeout_read :读超时,NULL 表示不超时;
(2)timeout_write :写超时,NULL 表示不超时。
返回值:成功返回 0,失败返回 -1。
可以使用如下方法判断是否为读写超时:
if ((what & BEV_EVENT_TIMEOUT) && (what & BEV_EVENT_READING)) { /* 读超时 */ }
if ((what & BEV_EVENT_TIMEOUT) && (what & BEV_EVENT_WRITING)) { /* 写超时 */ }
7、释放bufferevent
void bufferevent_free(struct bufferevent *bufev);
bufferevent_free 函数内部有引用计数,它会尽快的关闭,即在判断没有引用后才会闭关,不会立即关闭。如果设置了 BEV_OPT_CLOSE_ON_FREE 标志,在 bufferevent_free 时也会将 socket 关闭。
另外,需要注意,如果用 bufferevent_write 发送后立马调用 bufferevent_free 可能会导致部分数据没有发出去 ,所以不要过早关闭 bufferevent。
8、连接服务端 socket
一般用于客户端程序。
int bufferevent_socket_connect(struct bufferevent *bufev,
const struct sockaddr *addr, int socklen);
在调用此函数时,bufferevent 中的 socket fd 必须设置为 nonblock 。正常情况下,若连接成功,会引起 BEV_EVENT_CONNECTED 的事件回调。