上一篇我們分享了:libevhtp——一款專為嵌入式系統設計的開源HTTP庫!,libevhtp依賴于libevent。這篇文章我們一起來了解libevent。
一、libevent 簡介
libevent 是一個開源的、輕量級的跨平臺事件驅動庫:
Github倉庫地址:https://github.com/libevent/libevent
libevent 的源碼結構清晰,主要分為以下幾個核心模塊:
libevent/├── event.c
# 事件核心邏輯├── epoll.c
# Linux epoll 后端實現├── kqueue.c
# BSD kqueue 后端實現├── select.c
# 通用 select 后端實現├── bufferevent.c
# 緩沖區事件處理├── evhttp.c
# HTTP 協議處理├── evdns.c
# DNS 解析模塊├── evthread.c
# 線程支持├── evutil.c
# 工具函數
它封裝了多種操作系統的 I/O 多路復用機制(如 epoll、kqueue、select 等),提供了統一的事件處理接口,極大簡化了異步網絡編程的復雜度。
1. 核心特性
- 跨平臺支持:原生支持 Linux、Windows、BSD 等多種操作系統,甚至在嵌入式系統上也能運行
- 高性能 I/O 模型:自動適配底層最優的 I/O 多路復用機制(epoll/kqueue 優先)
- 輕量級設計:核心代碼簡潔高效,資源占用低,非常適合嵌入式場景
- 豐富的功能組件:包含 HTTP 服務器、DNS 解析、SSL 支持等實用模塊
- 線程安全:提供完善的線程安全機制,支持多線程環境下的事件處理
2. 優缺點分析
二、libevent實戰
1. 源碼獲取與編譯
# 克隆源碼倉庫
git clone https://github.com/libevent/libevent.git
cd libevent
# 創建編譯目錄
mkdir build && cd build
# 使用 CMake 配置(嵌入式平臺需指定交叉編譯工具鏈)
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/libevent
# 編譯
make -j16
# 安裝到指定目錄
sudo make install
2. 簡單的 TCP 服務器
下面是一個基于 libevent 的簡單 TCP 服務器示例,實現了接收客戶端連接并回顯數據的功能:
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 客戶端連接處理回調函數 */
void client_read_cb(struct bufferevent *bev, void *ctx) {
char buffer[1024];
int len;
/* 從緩沖區讀取數據 */
len = bufferevent_read(bev, buffer, sizeof(buffer) - 1);
if (len <= 0) {
printf("客戶端連接關閉\n");
bufferevent_free(bev);
return;
}
buffer[len] = '\0';
printf("收到客戶端數據: %s", buffer);
/* 將數據回顯給客戶端 */
bufferevent_write(bev, buffer, len);
}
/* 客戶端連接錯誤處理回調 */
void client_event_cb(struct bufferevent *bev, short events, void *ctx) {
if (events & BEV_EVENT_EOF) {
printf("客戶端斷開連接\n");
} elseif (events & BEV_EVENT_ERROR) {
printf("客戶端發生錯誤\n");
} elseif (events & BEV_EVENT_CONNECTED) {
printf("客戶端連接成功\n");
}
bufferevent_free(bev);
}
/* 新客戶端連接處理回調 */
void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t sock,
struct sockaddr *addr, int len, void *ctx) {
struct event_base *base = evconnlistener_get_base(listener);
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
char client_ip[INET_ADDRSTRLEN];
/* 轉換客戶端IP地址格式 */
inet_ntop(AF_INET, &(sin->sin_addr), client_ip, INET_ADDRSTRLEN);
printf("新客戶端連接: %s:%d\n", client_ip, ntohs(sin->sin_port));
/* 創建緩沖區事件 */
struct bufferevent *bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
printf("創建緩沖區事件失敗\n");
return;
}
/* 設置回調函數 */
bufferevent_setcb(bev, client_read_cb, NULL, client_event_cb, NULL);
/* 啟用讀寫事件 */
bufferevent_enable(bev, EV_READ | EV_WRITE);
}
/* 錯誤處理回調 */
void accept_error_cb(struct evconnlistener *listener, void *ctx) {
struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
printf("監聽錯誤: %s\n", evutil_socket_error_to_string(err));
event_base_loopexit(base, NULL);
}
int main(int argc, char **argv) {
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in sin;
int port = 9999;
/* 初始化事件基 */
base = event_base_new();
if (!base) {
printf("創建事件基失敗\n");
return1;
}
/* 配置監聽地址 */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(port);
/* 創建監聽套接字 */
listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
-1, (struct sockaddr *)&sin, sizeof(sin));
if (!listener) {
printf("創建監聽失敗\n");
event_base_free(base);
return1;
}
/* 設置監聽錯誤回調 */
evconnlistener_set_error_cb(listener, accept_error_cb);
printf("服務器啟動,監聽端口: %d\n", port);
/* 進入事件循環 */
event_base_dispatch(base);
/* 清理資源 */
evconnlistener_free(listener);
event_base_free(base);
return0;
}
編譯命令:
gcc -o tcp_server tcp_server.c -I/path/to/libevent/include -L/path/to/libevent/lib -levent -Wl,-rpath=/path/to/libevent/lib
Telnet協議是TCP/IP協議族中的一員,是Internet遠程登陸服務的標準協議和主要方式。telnet命令可以用來確定遠程服務的狀態,比如確定遠程服務器的某個端口是否能訪問。
重要部分:
- 事件基 (event_base):整個事件處理的核心,管理事件循環和事件分發
- 連接監聽器 (evconnlistener):封裝了 TCP 服務器的監聽邏輯,簡化連接處理
- 緩沖區事件 (bufferevent):提供了帶緩沖區的事件處理,自動管理數據讀寫
- 回調函數機制:libevent 采用異步回調模式,避免阻塞式編程