上一篇我们分享了: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 <event2/event.h>
#include <event2/listener.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* 客户端连接处理回调函数 */
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] = '';
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:%dn", 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("监听错误: %sn", 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("服务器启动,监听端口: %dn", 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 采用异步回调模式,避免阻塞式编程
3990