
| # epoll模型核心要点 |
| |
| ## 1. epoll核心概念 |
| ### 1.1 高效IO多路复用 |
| - 监视列表与激活列表分离 |
| - 内核使用红黑树存储描述符 |
| - 边缘触发模式(EPOLLET)支持 |
| |
| ### 1.2 事件触发机制 |
| - **水平触发(LT)**: |
| - 默认模式,类似select/poll |
| - 数据未读完持续触发事件 |
| - **边缘触发(ET)**: |
| - 需手动设置EPOLLET标志 |
| - 数据到达仅触发一次事件 |
| - 必须搭配非阻塞IO使用 |
| |
| ## 2. 关键操作函数 |
| ### 2.1 epoll_create1 |
| ```c |
| int epfd = epoll_create1(EPOLL_CLOEXEC); |
- 创建epoll实例
- EPOLL_CLOEXEC标志自动关闭文件描述符
2.2 epoll_ctl
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
- 操作类型:
- EPOLL_CTL_ADD 添加描述符
- EPOLL_CTL_MOD 修改事件
- EPOLL_CTL_DEL 删除描述符
- 事件结构体:
| struct epoll_event { |
| uint32_t events; // EPOLLIN/EPOLLOUT/EPOLLERR |
| epoll_data_t data; // 携带用户数据 |
| }; |
2.3 epoll_wait
int num = epoll_wait(epfd, events, maxevents, timeout);
- 阻塞等待事件触发
- 参数:
- timeout=-1永久阻塞
- timeout=0非阻塞
- 返回激活事件数量
3. 服务器实现流程
3.1 初始化阶段
| // 创建TCP套接字 |
| int sfd = socket(AF_INET, SOCK_STREAM, 0); |
| // 绑定地址 |
| bind(sfd, (struct sockaddr*)&addr, sizeof(addr)); |
| // 设置监听队列 |
| listen(sfd, 5); |
| // 创建epoll实例 |
| int epfd = epoll_create1(EPOLL_CLOEXEC); |
3.2 事件处理循环
- 接受新连接:
| int client_fd = accept(sfd, ...); |
| // 设置非阻塞模式 |
| fcntl(client_fd, F_SETFL, O_NONBLOCK); |
| // 添加至epoll监视 |
| epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event); |
- 处理数据接收:
| while(recv() > 0) { // 边缘触发需循环读取 |
| printf("Received: %s", buf); |
| if(errno == EAGAIN) break; // 非阻塞模式退出条件 |
| } |
- 异常处理:
| if(ret == 0) { // 客户端关闭 |
| epoll_ctl(epfd, EPOLL_CTL_DEL, fd_temp, NULL); |
| close(fd_temp); |
| } |
4. 模型对比
特性 | select | poll | epoll |
---|
数据结构 | 位图(1024限制) | 结构体数组 | 红黑树 |
时间复杂度 | O(n)线性扫描 | O(n)线性扫描 | O(1)回调通知 |
触发模式 | 仅水平触发 | 仅水平触发 | 支持边缘触发 |
内存拷贝 | 每次全量复制 | 每次全量复制 | 增量操作 |
适用场景 | 低并发/跨平台 | 中等并发 | 高并发/Linux专用 |
5. 客户端实现要点
5.1 非阻塞IO设置
| int flags = fcntl(fd, F_GETFL); |
| fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
5.2 数据发送处理
| while(1) { |
| fgets(buf, sizeof(buf), stdin); |
| send(sfd, buf, strlen(buf), 0); // 边缘触发需保证完全发送 |
| if(errno == EAGAIN) usleep(1000); // 流量控制 |
| } |
6. 特殊场景处理
6.1 多客户端通信
- 服务端维护客户端映射表
- 使用epoll_data携带会话上下文
event.data.ptr = &client_ctx; // 传递自定义数据结构
6.2 心跳检测机制
- 定时器队列管理连接状态
- EPOLLERR事件处理异常断开

| # UDP套接字通信核心要点 |
| |
| ## 1. UDP协议特性 |
| ### 基础特征 |
| - 无连接不可靠传输 |
| - 数据报独立发送 |
| - 支持数据顺序错乱 |
| - 效率高延迟低 |
| |
| ### 适用场景 |
| - 实时音视频传输 |
| - 在线游戏数据交互 |
| - 广播/组播通信 |
| |
| ## 2. 核心函数解析 |
| ### socket创建 |
| ```c |
| int sockfd = socket(AF_INET, SOCK_DGRAM, 0); |
- 协议族必须为AF_INET
- 类型指定SOCK_DGRAM
数据接收
| ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, |
| struct sockaddr *src_addr, socklen_t *addrlen); |
数据发送
| ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, |
| const struct sockaddr *dest_addr, socklen_t addrlen); |
connect特殊用法
- 内核预存目标地址信息
- 允许使用send/recv简化调用
- 可多次调用更改目标地址
3. 服务器实现流程
核心步骤
- 创建DGRAM类型套接字
- 绑定固定IP和端口
- 循环接收客户端消息
- 响应消息附加处理标识
关键代码段
| struct sockaddr_in cliaddr; |
| socklen_t len = sizeof(cliaddr); |
| ret = recvfrom(sockfd, buf, sizeof(buf), 0, |
| (struct sockaddr*)&cliaddr, &len); |
| sendto(sockfd, modified_buf, strlen(modified_buf), 0, |
| (struct sockaddr*)&cliaddr, len); |
4. 客户端实现模式
基础版本
高效版本
| connect(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr)); |
| send(sockfd, buf, strlen(buf), 0); |
5. 特殊处理机制
地址重置
| struct sockaddr_in unspec = { .sin_family = AF_UNSPEC }; |
| connect(sockfd, (struct sockaddr*)&unspec, sizeof(unspec)); |
错误检测
- recvfrom返回0表示空数据包
- errno==EAGAIN时处理非阻塞状态
- 发送失败需重试或记录日志
6. 与TCP对比差异
特性 | UDP | TCP |
---|
连接方式 | 无连接 | 三次握手建立连接 |
数据边界 | 保留报文边界 | 字节流形式 |
可靠性 | 不保证数据完整 | 可靠传输 |
资源消耗 | 低 | 高 |
适用场景 | 实时性要求高的场景 | 数据完整性场景 |
7. 性能优化方向
- 使用连接式UDP减少系统调用
- 设置SO_RCVBUF/SO_SNDBUF
- 采用非阻塞IO+epoll多路复用
- 实现应用层重传机制