在网络编程中,处理大量并发连接是一个常见且关键的问题。传统的 select 和 poll 方法在面对成千上万的连接时效率低下。而 Linux 提供的 epoll 机制则能高效地解决这一问题。本文将带你从零开始,详细讲解 C++语言epoll函数使用 的全过程,即使是编程小白也能轻松上手!

epoll 是 Linux 内核为处理大批量文件描述符而改进的 poll,是 Linux 下高性能I/O多路复用的重要工具。与 select/poll 不同,epoll 使用事件驱动模型,只返回活跃的文件描述符,避免了遍历所有连接的开销。
epoll 主要包含三个系统调用:
epoll_create():创建一个 epoll 实例。epoll_ctl():向 epoll 实例中添加、修改或删除要监听的文件描述符。epoll_wait():等待事件发生,并返回就绪的事件列表。1. 水平触发(Level-Triggered, LT):默认模式。只要文件描述符处于就绪状态,每次调用 epoll_wait() 都会通知你。
2. 边缘触发(Edge-Triggered, ET):只有当文件描述符状态发生变化时(例如从不可读变为可读),才会通知一次。ET 模式效率更高,但要求程序必须一次性读完所有数据,否则可能丢失事件。
下面是一个基于 C++ 的简单 TCP 服务器,使用 epoll 实现高并发连接处理。这个例子展示了如何使用 epoll示例代码 构建一个基础但完整的网络服务。
#include <iostream>#include <sys/socket.h>#include <netinet/in.h>#include <unistd.h>#include <cstring>#include <sys/epoll.h>#include <fcntl.h>#define MAX_EVENTS 10#define PORT 8888// 设置非阻塞void set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK);}int main() { int server_fd, new_socket; struct sockaddr_in address; int addrlen = sizeof(address); // 创建 socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 允许端口重用 int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定并监听 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } if (listen(server_fd, 10) < 0) { perror("listen"); close(server_fd); exit(EXIT_FAILURE); } // 创建 epoll 实例 int epoll_fd = epoll_create1(0); if (epoll_fd == -1) { perror("epoll_create1"); close(server_fd); exit(EXIT_FAILURE); } struct epoll_event ev, events[MAX_EVENTS]; ev.events = EPOLLIN; ev.data.fd = server_fd; // 将 server_fd 加入 epoll 监听 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == -1) { perror("epoll_ctl: server_fd"); close(server_fd); close(epoll_fd); exit(EXIT_FAILURE); } std::cout << "Server listening on port " << PORT << std::endl; while (true) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); break; } for (int i = 0; i < nfds; i++) { if (events[i].data.fd == server_fd) { // 接受新连接 new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen); if (new_socket == -1) { perror("accept"); continue; } set_nonblocking(new_socket); ev.events = EPOLLIN | EPOLLET; // 使用边缘触发 ev.data.fd = new_socket; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_socket, &ev) == -1) { perror("epoll_ctl: new_socket"); close(new_socket); } std::cout << "New connection accepted: " << new_socket << std::endl; } else { // 处理客户端数据 char buffer[1024] = {0}; int valread = read(events[i].data.fd, buffer, 1024); if (valread > 0) { std::cout << "Received: " << buffer << std::endl; send(events[i].data.fd, buffer, strlen(buffer), 0); } else if (valread == 0) { // 客户端断开连接 std::cout << "Client disconnected: " << events[i].data.fd << std::endl; close(events[i].data.fd); epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, nullptr); } else { if (errno != EAGAIN) { perror("read error"); close(events[i].data.fd); epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, nullptr); } } } } } close(server_fd); close(epoll_fd); return 0;}1. 非阻塞 I/O:在 ET 模式下,必须将 socket 设置为非阻塞,否则可能因未读完数据而卡住。
2. 事件注册:使用 EPOLLIN | EPOLLET 表示监听可读事件并启用边缘触发。
3. 错误处理:当 read 返回 -1 且 errno == EAGAIN 时,表示数据已读完,这是 ET 模式的正常行为。
通过本教程,你应该已经掌握了 C++网络编程 中 epoll 的基本使用方法。epoll 是构建高性能服务器的核心技术之一,广泛应用于 Nginx、Redis 等知名项目中。希望这篇 epoll函数使用 教程能为你打下坚实的基础!
继续练习,尝试扩展这个服务器,比如支持 HTTP 协议、多线程处理等,你将更深入理解高性能网络编程的奥秘。
本文由主机测评网于2025-12-07发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/2025124314.html