// 5. 将监听套接字添加到epoll监控
    struct epoll_event event{};
    event.events = EPOLLIN; // 监控可读事件
    event.data.fd = listen_sockfd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sockfd, &event) < 0) {
        perror("epoll_ctl add listen failed");
        return 1;
    }

把监听套接字加入 epoll 监控

这是 epoll 高并发服务器最核心的操作之一向 epoll 监控器注册要监听的 socket 和事件

整段代码的核心作用(大白话)

// 告诉 epoll 监控中心:
// 帮我盯着【监听套接字】,一旦有客户端来连接(可读事件),立刻通知我!

这一步 = 给服务器装上“雷达”,开始扫描客户端连接请求。


逐行深度拆解

1. 创建 epoll 事件结构体

struct epoll_event event{};

这是什么?

  • epoll_eventepoll 事件描述体
  • 作用:告诉 epoll 要监听哪个 socket、监听什么事件、事件触发后带什么数据回来

结构体内部长这样(必须看懂):

struct epoll_event {
    uint32_t events;  // 要监听的事件类型(如:可读、可写、异常)
    epoll_data_t data; // 事件触发时,带回的数据(通常存 socket 文件描述符)
};

2. 设置要监听的事件类型

event.events = EPOLLIN; // 监控可读事件

EPOLLIN 是什么?

全称:Event POLL IN —— 监听「可读事件」

  • 含义:只要对应的 socket 有数据可读,就触发通知
  • 监听套接字来说:
    EPOLLIN 事件触发 = 有客户端来连接了!

为什么监听套接字只需要 EPOLLIN?

  • 监听套接字不收发数据,只负责接收新连接
  • 客户端发起连接 → 内核把连接放入队列 → 监听套接字变为「可读」
  • epoll 检测到 EPOLLIN → 通知程序去调用 accept() 接收连接

其他常见事件(了解即可):

  • EPOLLOUT:可写事件(发送数据时用)
  • EPOLLET:边缘触发(高性能模式)
  • EPOLLERR:异常事件

3. 设置事件触发后带回的数据

event.data.fd = listen_sockfd;

作用:

把监听套接字的文件描述符存进事件结构体

  • 当 epoll 检测到事件触发时,会把这个 fd 原样返回
  • 程序拿到 fd 就知道:哦!是监听套接字有动静了,该去 accept 了!

为什么要存 fd?

epoll 会监控成百上千个 socket
必须带上标识,才能知道到底是哪个 socket 触发了事件


4. 核心函数:epoll_ctl(操控 epoll 监控列表)

epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sockfd, &event)

① 函数作用

对 epoll 监控列表进行增、删、改操作

  • 增:添加要监控的 socket
  • 删:取消监控
  • 改:修改监控的事件类型

② 四个参数逐行解释(重中之重)

参数 含义 解释
epoll_fd epoll 实例的文件描述符 你之前 epoll_create1 创建的监控器
EPOLL_CTL_ADD 操作类型:添加 把 socket 加入 epoll 监控列表
listen_sockfd 要监控的目标 socket 监听套接字(负责接收客户端连接)
&event 事件配置信息 告诉 epoll:监听 EPOLLIN 事件,触发后返回 listen_sockfd

③ 三种操作类型(必记)

  • EPOLL_CTL_ADD添加 新的 socket 到 epoll
  • EPOLL_CTL_DEL删除 不再监控的 socket
  • EPOLL_CTL_MOD修改 已添加 socket 的监听事件

5. 返回值判断

if (...) < 0) {
    perror("epoll_ctl add listen failed");
    return 1;
}
  • 成功:返回 0
  • 失败:返回 -1
  • 常见失败原因:
    • epoll_fd 非法
    • listen_sockfd 未绑定/未监听
    • 重复添加同一个 socket

完整原理(通俗到离谱)

把 epoll 当作 小区保安室

  1. struct epoll_event event = 一张监控登记表
  2. event.events = EPOLLIN = 登记要监控:有人来访
  3. event.data.fd = listen_sockfd = 监控目标:小区大门
  4. epoll_ctl(..., EPOLL_CTL_ADD, ...) = 把登记表交给保安
  5. 保安(epoll)开始盯着大门,有人来就立刻通知你

这行代码执行完后发生了什么?

  1. 内核把 listen_sockfd 加入 epoll 监控红黑树
  2. epoll 开始自动监控该套接字的 EPOLLIN 事件
  3. 一旦有客户端连接 → 内核立刻通知 epoll
  4. 程序调用 epoll_wait 就能拿到事件,去处理连接

极简总结(超好记)

  1. struct epoll_event:epoll 事件登记表
  2. EPOLLIN:监听「有客户端连接」事件
  3. event.data.fd:告诉 epoll,触发后带回监听套接字
  4. epoll_ctl(ADD):把监听套接字注册到 epoll 监控
  5. 核心意义:让 epoll 帮你盯着端口,有连接就通知,不用死等

一句话总结

这行代码 = 给 epoll 装上“连接雷达”,让服务器能高效感知所有客户端的连接请求!

下一步就是最关键的 epoll_wait() —— 等待客户端连接 / 数据到来

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐