当前位置: 首页 > news >正文

epoll_event的概念和使用案例

epoll_event 是 Linux 下 epoll I/O 多路复用机制的核心数据结构,用于描述文件描述符(File Descriptor, FD)上发生的事件及其关联的用户数据。通过 epoll,可以高效地监控多个文件描述符的状态变化(如可读、可写、错误等)。


epoll_event 结构定义

#include <sys/epoll.h>

struct epoll_event {
    uint32_t     events;  // 需要监听的事件类型(bitmask)
    epoll_data_t data;    // 用户数据,通常包含文件描述符
};

typedef union epoll_data {
    void* ptr;
    int fd;          // 通常关联的 FD
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;
  • events:表示关注的事件类型,常用值:

    • EPOLLIN:文件描述符可读(如 socket 接收到数据)。
    • EPOLLOUT:文件描述符可写(如 socket 可以发送数据)。
    • EPOLLERR:发生错误。
    • EPOLLHUP:对端关闭连接。
    • EPOLLET:设置为边缘触发(Edge-Triggered)模式(默认是水平触发 Level-Triggered)。
  • data:用户数据联合体,通常用 fd 字段保存关联的文件描述符。


使用步骤

  1. 创建 epoll 实例epoll_create1()
  2. 注册/修改事件epoll_ctl() 添加(EPOLL_CTL_ADD)、修改(EPOLL_CTL_MOD)或删除(EPOLL_CTL_DEL)事件。
  3. 等待事件epoll_wait() 阻塞等待事件发生。
  4. 处理事件:遍历就绪的事件并处理。

示例代码:TCP 服务器监控连接和数据

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>

#define MAX_EVENTS 10
#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int server_fd, client_fd, epoll_fd;
    struct sockaddr_in addr;
    struct epoll_event event, events[MAX_EVENTS];
    char buffer[BUFFER_SIZE];

    // 1. 创建 TCP 服务器 socket
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 5);

    // 2. 创建 epoll 实例
    epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    // 3. 注册服务器 socket 到 epoll,监听可读事件(新连接)
    event.events = EPOLLIN;
    event.data.fd = server_fd;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);

    printf("Server listening on port %d...\n", PORT);

    while (1) {
        // 4. 等待事件发生(阻塞调用)
        int n_ready = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (n_ready == -1) {
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }

        // 5. 处理所有就绪事件
        for (int i = 0; i < n_ready; i++) {
            int current_fd = events[i].data.fd;

            // 服务器 socket 可读:新连接到达
            if (current_fd == server_fd) {
                client_fd = accept(server_fd, NULL, NULL);
                if (client_fd == -1) {
                    perror("accept");
                    continue;
                }

                // 将新连接的客户端 socket 加入 epoll 监听
                event.events = EPOLLIN;
                event.data.fd = client_fd;
                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
                printf("New client connected: fd=%d\n", client_fd);
            } 
            // 客户端 socket 可读:接收数据
            else if (events[i].events & EPOLLIN) {
                ssize_t bytes_read = read(current_fd, buffer, BUFFER_SIZE);
                if (bytes_read <= 0) {
                    // 连接关闭或错误,移除监听并关闭 socket
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, current_fd, NULL);
                    close(current_fd);
                    printf("Client fd=%d disconnected.\n", current_fd);
                } else {
                    buffer[bytes_read] = '\0';
                    printf("Received from fd=%d: %s\n", current_fd, buffer);
                    // 回显数据(示例)
                    write(current_fd, buffer, bytes_read);
                }
            }
        }
    }

    close(server_fd);
    return 0;
}

关键解释

  1. 服务器初始化:创建 TCP 服务器 socket 并绑定端口。
  2. 注册服务器 socket:将服务器 socket 加入 epoll 监听列表,关注 EPOLLIN 事件(新连接到达)。
  3. 事件循环
    • epoll_wait() 返回所有就绪的事件。
    • 如果是服务器 socket 就绪,调用 accept() 接受新连接,并将新客户端 socket 加入 epoll
    • 如果是客户端 socket 可读,读取数据并处理;若读取失败(如连接关闭),则移除监听并关闭 socket。

触发模式

  • 水平触发(LT,默认):只要文件描述符处于就绪状态,epoll_wait() 会持续报告事件。
  • 边缘触发(ET):仅在状态变化时报告一次事件。需搭配非阻塞 IO,并循环读取数据直到 EAGAIN 错误。

设置 ET 模式示例:

event.events = EPOLLIN | EPOLLET;  // 边缘触发

通过 epoll_event,可以高效管理成千上万的并发连接,是高性能网络服务器的核心机制(如 Nginx、Redis)。

相关文章:

  • 如何保存爬虫获取商品评论的数据?
  • 【AI时代】基于AnythingLLM+ Ollama + DeepSeek 搭建本地知识库
  • GeoHD - 一种用于智慧城市热点探测的Python工具箱
  • redis缓存与Mysql数据一致性,要如何解决?
  • Unity贴图与模型相关知识
  • GTSAM 库详细介绍与使用指南
  • DeepSeek全链路开发指南:从零搭建智能问答系统到API无缝对接【内含知识库实战】
  • 微信小程序数据绑定与事件处理:打造动态交互体验
  • 【Altium Designer】差分对等长设置以及绕线
  • Linux基本指令(三)+ 权限
  • Unity中点乘和叉乘对于我们来说的作用是什么?
  • 【愚公系列】《鸿蒙原生应用开发从零基础到多实战》002-TypeScript 类型系统详解
  • Windows安装MySQL教程
  • 基于 QT6 工业非标自动化设备上位机软件开发与设计
  • 对Revit事务机制的一些推测
  • libxls库的编译以及基于Visual studio的配置
  • Qt开发中有关内存管理方面常见的问题分析与解决方案
  • 简讯:Rust 2024 edition and v1.85.0 已发布
  • 【Shell编程 / 9】脚本实战项目:从基础到进阶的自动化管理方案
  • uniapp修改picker-view样式
  • 蒋圣龙突遭伤病出战世预赛存疑,国足生死战后防线严重减员
  • 男子恶意遗弃幼子获刑,最高法发布涉未成年人家庭保护典型案例
  • 玉渊谭天丨卢拉谈美国降低对华关税:中国的行动捍卫了主权
  • 讲武谈兵|视距外的狙杀:从印巴空战谈谈超视距空战
  • 中欧金融工作组第二次会议在比利时布鲁塞尔举行
  • 白玉兰奖征片综述丨动画的IP生命力