整体架构设计
- 事件循环:采用一个主循环来不断监听和处理各种I/O事件。在循环内,使用同步多路复用技术(如select、poll或epoll)来等待事件的发生。
- 事件注册与管理:提供接口用于注册、删除和修改感兴趣的I/O事件。这些事件信息将被传递给多路复用函数。
- 事件处理回调:为每种类型的I/O事件定义相应的回调函数,当事件发生时,调用对应的回调函数进行处理。
关键数据结构
- 事件结构体:
typedef struct {
int fd; // 文件描述符
short events; // 感兴趣的事件类型(如POLLIN、POLLOUT等)
void (*callback)(int fd, short events, void *arg); // 事件发生时的回调函数
void *arg; // 传递给回调函数的参数
} Event;
- 事件集合结构体:
typedef struct {
Event *events;
int capacity;
int count;
} EventSet;
- 初始化事件集合函数:
void initEventSet(EventSet *set, int initialCapacity) {
set->events = (Event *)malloc(initialCapacity * sizeof(Event));
set->capacity = initialCapacity;
set->count = 0;
}
- 添加事件到集合函数:
void addEvent(EventSet *set, int fd, short events, void (*callback)(int fd, short events, void *arg), void *arg) {
if (set->count >= set->capacity) {
set->capacity *= 2;
set->events = (Event *)realloc(set->events, set->capacity * sizeof(Event));
}
set->events[set->count].fd = fd;
set->events[set->count].events = events;
set->events[set->count].callback = callback;
set->events[set->count].arg = arg;
set->count++;
}
- 删除事件从集合函数:
void removeEvent(EventSet *set, int fd) {
for (int i = 0; i < set->count; i++) {
if (set->events[i].fd == fd) {
set->events[i] = set->events[set->count - 1];
set->count--;
break;
}
}
}
关键函数实现
- 基于poll的事件循环:
void eventLoop(EventSet *set) {
struct pollfd *pollfds = (struct pollfd *)malloc(set->count * sizeof(struct pollfd));
for (int i = 0; i < set->count; i++) {
pollfds[i].fd = set->events[i].fd;
pollfds[i].events = set->events[i].events;
pollfds[i].revents = 0;
}
while (1) {
int nfds = poll(pollfds, set->count, -1);
if (nfds == -1) {
perror("poll");
break;
} else if (nfds > 0) {
for (int i = 0; i < set->count; i++) {
if (pollfds[i].revents) {
set->events[i].callback(pollfds[i].fd, pollfds[i].revents, set->events[i].arg);
}
}
}
}
free(pollfds);
}
- 示例回调函数:
void tcpConnectionCallback(int fd, short events, void *arg) {
if (events & POLLIN) {
// 处理TCP连接上的读事件
char buffer[1024];
ssize_t n = recv(fd, buffer, sizeof(buffer), 0);
if (n == -1) {
perror("recv");
} else if (n == 0) {
// 连接关闭
close(fd);
} else {
buffer[n] = '\0';
printf("Received: %s\n", buffer);
}
}
}
性能优化
- 减少系统调用次数:尽量批量处理事件,避免频繁调用多路复用函数。
- 高效的数据结构:使用合适的数据结构(如哈希表)来快速查找和管理事件,减少遍历时间。
- 非阻塞I/O:对于I/O操作,采用非阻塞模式,避免I/O操作时阻塞事件循环。
资源管理
- 内存管理:及时释放不再使用的内存,如在删除事件时释放相关资源。
- 文件描述符管理:确保在不再使用文件描述符时关闭它们,避免文件描述符泄漏。
错误处理
- 系统调用错误:在每次系统调用(如poll、recv、close等)后检查返回值,及时处理错误并记录日志。
- 内存分配错误:在内存分配(如malloc、realloc)失败时,及时处理错误,如输出错误信息并终止程序或进行合理的降级处理。