一、概念
Libevent 提供了高效的 TCP 网络编程接口,使开发者能够轻松构建高性能的 TCP 服务器和客户端。本指南将详细介绍如何使用 Libevent 进行 TCP 网络开发。
核心组件
-
事件基 (event_base) - 事件处理的核心结构
-
事件 (event) - 表示单个事件
-
缓冲区事件 (bufferevent) - 带缓冲的 I/O 事件
-
监听器 (evconnlistener) - TCP 连接监听器
-
缓冲区 (evbuffer) - 高效的数据缓冲区
常用 API
事件基
-
event_base_new()
- 创建新的事件基 -
event_base_free()
- 释放事件基 -
event_base_dispatch()
- 开始事件循环 -
event_base_loopexit()
- 退出事件循环
事件
-
event_new()
- 创建新事件 -
event_free()
- 释放事件 -
event_add()
- 添加事件到事件基 -
event_del()
- 从事件基中删除事件
缓冲区事件
-
bufferevent_socket_new()
- 创建新的缓冲区事件 -
bufferevent_setcb()
- 设置回调函数 -
bufferevent_enable()
- 启用事件 -
bufferevent_write()
- 写入数据 -
bufferevent_read()
- 读取数据
event与bufferevent
普通事件(event):基础事件类型,用于监听文件描述符的可读/可写状态、信号、定时器等单一事件触发。用户需手动管理I/O操作和缓冲区的数据读写。比如:监听socket的可读事件后,需自行调用read()读取数据并处理粘包/分包问题。
缓冲区事件(bufferevent):高级封装的事件类型,内置输入/输出缓冲区(evbuffer结构体),自动处理底层I/O的读写操作和数据缓冲。用户只需通过回调函数处理已就绪的数据。比如:收到数据时自动填充输入缓冲区,触发读回调;用户调用bufferevent_write()时,数据先写入输出缓冲区,由libevent自动发送。
特性 | 普通事件(event) | 缓冲区事件(bufferevent) |
---|---|---|
数据缓冲 | 需用户手动管理 | 内置输入/输出缓冲区,自动管理 |
水位控制 | 不支持 | 支持设置高/低水位标记 |
适用场景 | 信号、定时器、低层I/O控制 | 流式网络通信(如TCP服务) |
多线程支持 | 需额外同步机制 | 提供线程安全的缓冲区操作接口 |
二、TCP 服务器开发
1. 基本服务器框架
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <arpa/inet.h>
// 定义回调函数
void read_cb(struct bufferevent *bev, void *ctx);
void event_cb(struct bufferevent *bev, short events, void *ctx);
void accept_conn_cb(struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *address,
int socklen,
void *ctx);
int main() {
// 1. 创建事件基
struct event_base *base = event_base_new();
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
// 2. 配置服务器地址
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(8080); // 监听8080端口
sin.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有接口
// 3. 创建监听器
struct evconnlistener *listener = evconnlistener_new_bind(
base, accept_conn_cb, NULL,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
(struct sockaddr*)&sin, sizeof(sin));
if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}
// 4. 启动事件循环
event_base_dispatch(base);
// 5. 清理资源
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
2. 连接接受回调
void accept_conn_cb(struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *address,
int socklen,
void *ctx) {
struct event_base *base = evconnlistener_get_base(listener);
// 为新的客户端连接创建bufferevent
struct bufferevent *bev = bufferevent_socket_new(
base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
// 设置回调函数
bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
// 启用读写事件
bufferevent_enable(bev, EV_READ | EV_WRITE);
// 可选: 设置水位标记
bufferevent_setwatermark(bev, EV_READ, 0, 4096); // 最大读取4096字节
}
3. 数据读取回调
void read_cb(struct bufferevent *bev, void *ctx) {
struct evbuffer *input = bufferevent_get_input(bev);
size_t len = evbuffer_get_length(input);
// 分配缓冲区
unsigned char *buffer = malloc(len + 1);
// 从输入缓冲区读取数据
bufferevent_read(bev, buffer, len);
buffer[len] = '\0';
printf("Received %zu bytes: %s\n", len, buffer);
// 回显数据
bufferevent_write(bev, buffer, len);
free(buffer);
}
4. 事件处理回调
void event_cb(struct bufferevent *bev, short events, void *ctx)