Epoll 是 Linux 系统中高性能的 I/O 事件通知机制,通过它可以实现对大量文件描述符的高效监控,适用于构建高并发的网络服务器。
在Linux中,epoll是一种高效的I/O多路复用机制,用于监视多个文件描述符(通常是套接字)的I/O事件。它相对于传统的 select 和 poll 具有更高的效率和扩展性,因此在处理大规模并发连接时被广泛应用。
poll模型的效率不如epoll,一般在fd数量比较多,但某段时间内,就绪事件fd数量较少的情况下,epoll才会体现出它的优势,也就是说socket连接数量较大时而活跃连接较少时epoll模型更高效。
在 Linux 下,epoll 提供了一些常用的函数来实现 I/O 多路复用,以下是几个常用的 epoll 函数及其功能介绍:
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll_event结构体如下:
struct epoll_event {
uint32_t events; // 事件类型,需要检测的fd事件,取值与poll函数一样
epoll_data_t data; // 用户自定义数据
};
typedef union epoll_data {
void *ptr; // 指向用户数据的指针
int fd; // 文件描述符
uint32_t u32;
uint64_t u64;
} epoll_data_t;
events:用来描述文件描述符上注册的事件类型,可以是以下的一个或多个值的组合:
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_EVENTS 10
#define PORT 8888
int main() {
int server_fd, client_fd, epoll_fd, nfds, n;
struct epoll_event event, events[MAX_EVENTS];
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
// 创建 socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 设置 server 地址信息
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
// 绑定 server 地址
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(server_fd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
// 创建 epoll 实例
if ((epoll_fd = epoll_create1(0)) == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
// 将 server_fd 添加到 epoll 实例中
event.events = EPOLLIN;
event.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {
perror("epoll_ctl: server_fd");
exit(EXIT_FAILURE);
}
while (1) {
nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == server_fd) { // 有新连接
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
// 将新的 client_fd 添加到 epoll 实例中
event.events = EPOLLIN;
event.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) {
perror("epoll_ctl: client_fd");
exit(EXIT_FAILURE);
}
} else { // 有数据可读
char buffer[1024];
int bytes_recv = recv(events[n].data.fd, buffer, sizeof(buffer), 0);
if (bytes_recv <= 0) { // 客户端断开连接
close(events[n].data.fd);
} else {
// 回显收到的消息
send(events[n].data.fd, buffer, bytes_recv, 0);
}
}
}
}
close(server_fd);
return 0;
}
参考链接:
- https://blog.csdn.net/m0_67392182/article/details/124487784
- https://www.zhihu.com/tardis/bd/art/159135478?source_id=1001
- https://blog.51cto.com/morris131/6216788
- https://www.cnblogs.com/xuewangkai/p/11158576.html