
|---|--------------------------------------------|
| | # 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);
|
- 获取发送方地址信息
- flags支持阻塞/非阻塞模式
数据发送
|---|----------------------------------------------------------------------|
| | ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
| | const struct sockaddr *dest_addr, socklen_t addrlen);
|
- 必须指定目标地址
- 单次发送上限4096字节
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多路复用
- 实现应用层重传机制