上一篇我們分享了:嵌入式開發必備:開源事件驅動庫 libevent
libevent作為一款強大的事件驅動庫,提供了bufferevent
機制,極大地簡化了異步網絡編程,高效處理了數據收發。
本文我們一起來學習一下bufferevent。
一、bufferevent簡介
bufferevent
是libevent提供的高級抽象層,用于處理帶緩沖的I/O操作。
它將底層的事件處理(如socket讀寫)與數據緩沖管理相結合,提供了統一且簡潔的接口。
通過bufferevent
,開發者無需手動管理底層的I/O事件,只需關注數據的讀寫邏輯。
核心特性
- 自動緩沖區管理:內置輸入/輸出緩沖區,自動處理數據的讀寫和存儲
- 事件驅動:基于libevent的事件機制,支持異步非阻塞操作
- 超時控制:可設置讀寫超時,防止長時間阻塞
- SSL/TLS支持:原生支持安全通信,無需額外配置
- 可擴展性:支持自定義過濾器,實現數據轉換和處理
- 線程安全:提供線程安全接口,適用于多線程環境
二、bufferevent實戰
下面是一個基于bufferevent的簡易TCP客戶端示例,展示了如何使用bufferevent進行數據收發。
#include
#include
#include
#include
#include
#include
#include
// 讀取回調函數
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] = '\0';
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("發生錯誤: %s\n", 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)數據寫入觸發流程
bufferevent_socket_new -> bufferevent_writecb -> bufferevent_trigger_nolock_ -> bufferevent_run_writecb_ -> bufev->writecb