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

服务器连接多客户端

一、epoll 核心函数详解

1. epoll_create/epoll_create1 - 创建 epoll 实例

c

#include <sys/epoll.h>
int epoll_create(int size); // Linux 2.6.8前需指定size(>=1),后续版本可忽略
int epoll_create1(int flags); // 推荐使用,支持flags参数

  • 功能:创建一个 epoll 实例,返回文件描述符(epfd),用于管理监视的文件描述符。
  • 参数
    • size:旧版本需指定预分配大小(无实际作用,可设为 1)。
    • flags
      • 0:与epoll_create功能相同。
      • EPOLL_CLOEXEC:使 epfd 在进程 fork 时自动关闭。
  • 返回值:成功返回 epfd(非负整数),失败返回-1并置errno
  • 示例

    c

    int epfd = epoll_create1(EPOLL_CLOEXEC); // 创建带自动关闭功能的epoll实例
    
2. epoll_ctl - 管理监视列表

c

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

  • 功能:向 epoll 实例中添加、修改或删除监视的文件描述符及其事件。
  • 参数
    • epfd:epoll 实例的文件描述符。
    • op:操作类型:
      • EPOLL_CTL_ADD:添加文件描述符fd到监视列表。
      • EPOLL_CTL_MOD:修改fd关联的事件。
      • EPOLL_CTL_DEL:从监视列表删除fdevent参数可置NULL)。
    • fd:需操作的文件描述符(如套接字)。
    • event:指向epoll_event结构体的指针,包含监视事件和用户数据:

      c

      struct epoll_event {uint32_t events;       // 监视事件(如EPOLLIN、EPOLLOUT、EPOLLET)epoll_data_t data;     // 用户数据(如fd、指针等)
      };
      typedef union epoll_data {void        *ptr;      // 自定义指针int          fd;       // 监视的文件描述符uint32_t     u32;      // 32位整数uint64_t     u64;      // 64位整数
      } epoll_data_t;
      
  • 返回值:成功返回0,失败返回-1并置errno
  • 示例

    c

    struct epoll_event event;
    event.events = EPOLLIN | EPOLLET; // 边缘触发模式 + 读事件
    event.data.fd = sockfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event); // 添加套接字到监视列表
    
3. epoll_wait - 等待事件触发

c

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

  • 功能:阻塞等待 epoll 实例中监视的文件描述符触发事件,返回激活的事件列表。
  • 参数
    • epfd:epoll 实例的文件描述符。
    • events:用于存储激活事件的结构体数组。
    • maxeventsevents数组的最大长度(需小于等于创建时的预分配值)。
    • timeout:超时时间(ms):
      • -1:永久阻塞,直到事件触发。
      • 0:非阻塞,立即返回。
      • >0:超时时间,到期后返回。
  • 返回值
    • >0:激活的事件数量(events中有效元素个数)。
    • 0:超时且无事件触发。
    • -1:失败并置errno
  • 示例

    c

    struct epoll_event events[1024];
    int n = epoll_wait(epfd, events, sizeof(events)/sizeof(events[0]), -1); // 永久阻塞等待事件
    for (int i = 0; i < n; i++) {int fd = events[i].data.fd;// 处理事件...
    }
    

二、epoll 工作原理

  1. 数据结构
    • 监视列表:使用红黑树存储所有监视的文件描述符,支持快速增删改查(时间复杂度 O (logN))。
    • 激活列表:使用链表或动态数组存储当前触发事件的文件描述符,避免遍历整个红黑树。
  2. 事件触发流程
    1. 通过epoll_ctl将文件描述符添加到红黑树(监视列表)。
    2. 内核监听文件描述符状态,当事件(如可读、可写)发生时,将其加入激活列表。
    3. epoll_wait返回激活列表中的事件,用户空间处理后,激活列表清空。

三、LT(水平触发)与 ET(边缘触发)模式对比

特性LT(水平触发)ET(边缘触发)
触发条件只要内核缓冲区有数据未读 / 未写,就持续触发事件。仅在状态变化时触发一次事件(如数据从无到有)。
读操作允许分多次读取,剩余数据会再次触发事件。必须一次性读完所有数据(需配合非阻塞 I/O),否则剩余数据不再触发。
写操作只要内核缓冲区有空间,就持续触发可写事件。仅在缓冲区由满变空时触发一次可写事件。
编程复杂度低,类似 select/poll,无需处理非阻塞。高,必须使用非阻塞 I/O,避免阻塞导致事件丢失。
适用场景简单场景、低速设备或不确定数据量的场景。高并发、高速数据传输场景(如 Web 服务器、实时通信)。

  • 示例配置

    c

    // LT模式(默认,无需指定EPOLLET)
    event.events = EPOLLIN; // ET模式(需显式指定EPOLLET)
    event.events = EPOLLIN | EPOLLET; 
    

四、select/poll/epoll 对比(面试重点)

特性selectpollepoll
数据结构位图(数组),最大连接数受限于FD_SETSIZE(默认 1024)。结构体数组,理论无连接数限制(仅受内存限制)。红黑树 + 激活列表,支持上万级连接(如 10,000+)。
内核遍历方式每次调用遍历所有监视的文件描述符(O (n))。每次调用遍历所有监视的文件描述符(O (n))。仅遍历激活列表中的文件描述符(O (1)~O (k),k 为激活数)。
用户态 / 内核态数据拷贝每次select需拷贝全量文件描述符到内核。每次poll需拷贝全量文件描述符到内核。仅在epoll_ctl时更新红黑树,epoll_wait无需拷贝。
触发模式仅支持 LT 模式。仅支持 LT 模式。支持 LT 和 ET 模式。
跨平台性支持(Linux/Windows/macOS)。支持(Linux/UNIX)。仅 Linux 支持。
适用场景低并发、跨平台场景(如嵌入式)。中等并发场景。高并发、Linux 服务器场景(如 Nginx、Redis)。

五、epoll 服务器示例代码关键点(C 语言)

c

// 1. 创建epoll实例并设置监听套接字为非阻塞
int epfd = epoll_create1(EPOLL_CLOEXEC);
set_nonblocking(listen_fd); // 自定义函数,通过fcntl设置O_NONBLOCK// 2. 注册监听套接字到epoll(ET模式)
struct epoll_event event = {.events = EPOLLIN | EPOLLET,.data = {.fd = listen_fd}
};
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event);// 3. 处理新连接(ET模式下需循环accept直到EAGAIN)
if (fd == listen_fd) {while (1) {int connfd = accept(listen_fd, &cliaddr, &addrlen);if (connfd == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) break; // 无新连接perror("accept");exit(EXIT_FAILURE);}set_nonblocking(connfd); // 客户端套接字也需设为非阻塞epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &event); // 注册读事件(ET模式)}
}// 4. 处理读事件(ET模式下需循环recv直到EAGAIN)
else {char buf[BUFFER_SIZE];while (1) {ssize_t n = recv(fd, buf, sizeof(buf), 0);if (n == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) break; // 数据读完perror("recv");close(fd);epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);break;} else if (n == 0) { // 客户端关闭连接close(fd);epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);break;}// 处理数据...}
}

六、总结

  • epoll 优势
    • 高并发:基于红黑树和激活列表,适合管理上万级连接。
    • 高性能:事件通知复杂度 O (1),减少 CPU 消耗。
    • 灵活性:支持 LT/ET 模式,适配不同场景。
  • 使用建议
    • 高并发服务器首选 epoll,配合 ET 模式和非阻塞 I/O。
    • 简单场景或跨平台需求使用 select/poll。
    • 注意设置文件描述符非阻塞,避免 ET 模式下的数据丢失。

相关文章:

  • 文章复现|(1)整合scRNA-seq 和空间转录组学揭示了子宫内膜癌中 MDK-NCL 依赖性免疫抑制环境
  • 数据结构中双栈的实现方法分享
  • PH热榜 | 2025-05-15
  • 解码生命语言:深度学习模型TranslationAI揭示RNA翻译新规则
  • Quic如何实现udp可靠传输
  • 缓存的相关内容
  • 该如何了解联排半孔的使用规范?
  • FPGA: UltraScale+ bitslip实现(ISERDESE3)
  • 2025年长三角+山东省赛+ 认证杯二阶段资料助攻说明
  • 快速搭建一个electron-vite项目
  • 套路化编程:C# winform ListView 自定义排序
  • 学习笔记:黑马程序员JavaWeb开发教程(2025.4.4)
  • MySQL DBA数据运维管理经验分享:新手入门快速提升效率的新工具与技巧
  • NuGet程序包还原失败
  • 智能包装机控制终端技术方案:基于EFISH-SCB-RK3588/SAIL-RK3588的全场景国产化替代解析
  • Docker 安装 MySQL8
  • 【iOS】源码阅读(四)——isa与类关联的原理
  • Vue 学习随笔系列二十三 -- el-date-picker 组件
  • Inventor 转 STP 全攻略:软件操作与迪威模型在线转换推荐
  • 从零开始创建一个 Next.js 项目并实现一个 TodoList 示例
  • “多规合一”改革7年成效如何?自然资源部总规划师亮成绩单
  • 李公明︱一周书记:当前科学观中的盲点、危机与……人类命运
  • 习近平复信中国丹麦商会负责人
  • “老中青少”四代同堂,季春艳携锡剧《玲珑女》冲击梅花奖
  • 陕西一村民被冒名贷款40余万续:名下已无贷款,将继续追责
  • 因操纵乙烯价格再遭诉讼,科莱恩等四家企业被陶氏索赔60亿