epoll是Linux下C++高性能网络编程的关键I/O多路复用机制,相比select和poll更高效,适用于大量并发连接。1. 使用epoll_create1创建实例;2. 通过epoll_ctl添加、修改或删除监控事件,支持EPOLLIN、EPOLLOUT、EPOLLET等事件类型;3. 调用epoll_wait等待事件并处理,配合非阻塞I/O实现高并发;4. 示例展示了基于epoll的简化TCP服务器流程,包括监听、accept新连接及读写处理,体现其事件驱动模型的核心优势。

在Linux下使用C++进行高性能网络编程时,epoll 是一个非常关键的I/O多路复用机制。相比传统的 select 和 poll,epoll 更加高效,尤其适用于处理大量并发连接。下面详细介绍如何在C++中使用 epoll。
1. 创建 epoll 实例
使用 epoll_create1() 函数创建一个 epoll 实例。该函数返回一个文件描述符,用于后续操作。
示例代码:
#include#include int epoll_fd = epoll_create1(0); if (epoll_fd == -1) { perror("epoll_create1 failed"); return -1; }
2. 添加、修改或删除监控事件
通过 epoll_ctl() 函数可以向 epoll 实例中添加、修改或删除要监听的文件描述符及其事件。
立即学习“C++免费学习笔记(深入)”;
常用事件类型包括:
- EPOLLIN:表示对应的文件描述符可读
- EPOLLOUT:表示对应的文件描述符可写
- EPOLLET:启用边缘触发模式(Edge Triggered),提高效率
- EPOLLONESHOT:事件只触发一次,需重新注册
示例:将某个 socket fd 加入 epoll 监听读事件(ET模式)
struct epoll_event event;
event.events = EPOLLIN | EPOLLET; // 监听可读 + 边缘触发
event.data.fd = sockfd; // 可以是fd,也可以是自定义指针
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &event) == -1) {
perror("epoll_ctl: ADD");
return -1;
}
3. 等待并处理事件
使用 epoll_wait() 阻塞等待事件发生。当有事件就绪时,它会填充用户提供的 events 数组。
参数说明:
- epoll_fd:epoll 实例的文件描述符
- events:存放就绪事件的数组
- max_events:最多返回的事件数
- timeout:超时时间(毫秒),-1 表示无限等待
示例:
const int MAX_EVENTS = 10;
struct epoll_event events[MAX_EVENTS];
while (true) {
int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (n == -1) {
perror("epoll_wait");
break;
}
for (int i = 0; i < n; ++i) {
if (events[i].events & EPOLLIN) {
handle_read(events[i].data.fd);
}
if (events[i].events & EPOLLOUT) {
handle_write(events[i].data.fd);
}
}
}
4. 完整流程示例(简化版TCP服务器)
以下是一个极简的使用 epoll 的 TCP 服务端框架:
#include#include #include #include #include #include #include int main() { int listen_sock = socket(AF_INET, SOCK_STREAM, 0); int flag = fcntl(listen_sock, F_GETFL, 0); fcntl(listen_sock, F_SETFL, flag | O_NONBLOCK); // 设置非阻塞 sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(8080); bind(listen_sock, (sockaddr*)&addr, sizeof(addr)); listen(listen_sock, SOMAXCONN); int epoll_fd = epoll_create1(0); epoll_event ev; ev.events = EPOLLIN | EPOLLET; ev.data.fd = listen_sock; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &ev); epoll_event events[10]; while (true) { int n = epoll_wait(epoll_fd, events, 10, -1); for (int i = 0; i < n; ++i) { if (events[i].data.fd == listen_sock) { // 新连接 while (true) { int client_fd = accept(listen_sock, nullptr, nullptr); if (client_fd == -1) break; fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL, 0) | O_NONBLOCK); epoll_event client_ev; client_ev.events = EPOLLIN | EPOLLET; client_ev.data.fd = client_fd; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_ev); } } else { // 处理客户端数据 char buf[1024]; int ret = read(events[i].data.fd, buf, sizeof(buf)); if (ret > 0) { write(events[i].data.fd, buf, ret); // 回显 } else { close(events[i].data.fd); epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, nullptr); } } } } close(listen_sock); close(epoll_fd); return 0; }
编译命令:
g++ -o server server.cpp
运行后可通过 telnet 或 curl 测试连接和回显功能。
基本上就这些。掌握 epoll 的核心在于理解其事件驱动模型,配合非阻塞 I/O 才能发挥最大性能。











