libevent作为一款强大的事件驱动库,提供了bufferevent机制,极大地简化了异步网络编程,高效处理了数据收发。
本文我们一起来学习一下bufferevent。
一、bufferevent简介
bufferevent是libevent提供的高级抽象层,用于处理带缓冲的I/O操作。
它将底层的事件处理(如socket读写)与数据缓冲管理相结合,提供了统一且简洁的接口。
通过bufferevent,开发者无需手动管理底层的I/O事件,只需关注数据的读写逻辑。

核心特性
二、bufferevent实战
下面是一个基于bufferevent的简易TCP客户端示例,展示了如何使用bufferevent进行数据收发。

#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
// 读取回调函数
void read_cb(struct bufferevent *bev, void *ctx) {
char buffer[1024];
int n;
// 从输入缓冲区读取数据
while ((n = bufferevent_read(bev, buffer, sizeof(buffer)-1)) > 0) {
buffer[n] = ' ';
printf("收到服务器消息: %s", buffer);
}
}
// 事件回调函数
void event_cb(struct bufferevent *bev, short events, void *ctx) {
if (events & BEV_EVENT_CONNECTED) {
printf("已连接到服务器n");
// 连接成功后发送数据
bufferevent_write(bev, "Hello, server!n", strlen("Hello, server!n"));
} elseif (events & BEV_EVENT_ERROR) {
printf("发生错误: %sn", strerror(errno));
} elseif (events & (BEV_EVENT_EOF | BEV_EVENT_TIMEOUT)) {
printf("连接关闭或超时n");
}
// 发生错误时释放资源
if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF | BEV_EVENT_TIMEOUT)) {
bufferevent_free(bev);
}
}
int main(int argc, char **argv) {
struct event_base *base;
struct bufferevent *bev;
struct sockaddr_in server_addr;
// 初始化事件基
base = event_base_new();
if (!base) {
fprintf(stderr, "创建事件基失败n");
return1;
}
// 配置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
evutil_inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
// 创建bufferevent
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "创建bufferevent失败n");
event_base_free(base);
return1;
}
// 设置回调函数
bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
bufferevent_enable(bev, EV_READ | EV_WRITE);
// 连接服务器
if (bufferevent_socket_connect(bev,
(struct sockaddr *)&server_addr,
sizeof(server_addr)) < 0) {
fprintf(stderr, "连接失败n");
bufferevent_free(bev);
event_base_free(base);
return1;
}
// 进入事件循环
event_base_dispatch(base);
// 清理资源
event_base_free(base);
return0;
}
编译:
gcc -o bufferevent_test bufferevent_test.c -I/path/to/libev
ent/include -L/path/to/libevent/lib -levent -Wl,-rpath=/path/to/libevent/lib
需要先安装libevent,服务端参考:嵌入式开发必备:开源事件驱动库 libevent
运行:

三、bufferevent核心机制
1、核心机制
(1)缓冲区操作核心
输入缓冲区:使用 evbuffer 管理接收到的数据,支持水位控制和回调
输出缓冲区:使用 evbuffer 管理待发送的数据,支持高效的批量写入
(2)事件触发条件
读回调:当输入缓冲区数据量 >= 读低水位 或 发生错误 / EOF
写回调:当输出缓冲区数据量 <= 写低水位 或 发生错误
(3)挂起机制
读挂起:当输入缓冲区数据量 >= 读高水位 或 手动挂起
写挂起:当输出缓冲区数据量 >= 写高水位 或 底层写入阻塞
(4)线程安全
使用递归锁保护共享数据结构
提供 bufferevent_lock()/bufferevent_unlock() 接口
延迟回调机制避免在锁内执行用户代码
2、源码
bufferevent的源码主要分布在以下文件中:
libevent/
├── bufferevent.c # 基础bufferevent实现
├── bufferevent_filter.c # 过滤器实现
├── bufferevent_ssl.c # SSL/TLS支持
├── evbuffer.c # 缓冲区实现
├── evbuffer_list.c # 链表式缓冲区实现
bufferevent 采用了 策略模式 设计,通过 struct bufferevent_ops 实现多态行为:

不同类型的 bufferevent(如 socket、filter、SSL)共享同一套接口,通过 be_ops 指针调用各自的实现函数。
(1)bufferevent的数据结构定义如下:

(2)水位控制结构:

读水位:当输入缓冲区数据超过 high 时停止读取,低于 low 时恢复
写水位:当输出缓冲区数据低于 low 时触发 writecb,超过 high 时暂停写入
(3)数据读取事件触发流程
bufferevent_socket_new -> bufferevent_readcb -> bufferevent_trigger_nolock_ -> bufferevent_run_readcb_ -> bufev->readcb

(4)数据写入触发流程

1289