网络编程epoll和udp

|---|--------------------------------------------|
| | # 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 事件处理循环

  1. 接受新连接

|---|------------------------------------------------------|
| | int client_fd = accept(sfd, ...); |
| | // 设置非阻塞模式 |
| | fcntl(client_fd, F_SETFL, O_NONBLOCK); |
| | // 添加至epoll监视 |
| | epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event); |

  1. 处理数据接收

|---|-------------------------------------------|
| | while(recv() > 0) { // 边缘触发需循环读取 |
| | printf("Received: %s", buf); |
| | if(errno == EAGAIN) break; // 非阻塞模式退出条件 |
| | } |

  1. 异常处理

|---|--------------------------------------------------|
| | 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. 服务器实现流程

核心步骤

  1. 创建DGRAM类型套接字
  2. 绑定固定IP和端口
  3. 循环接收客户端消息
  4. 响应消息附加处理标识

关键代码段

|---|---------------------------------------------------------|
| | 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多路复用
  • 实现应用层重传机制
相关推荐
UpUpUp……4 分钟前
Linux--JsonCpp
linux·运维·服务器·c++·笔记·json
唐人街都是苦瓜脸11 分钟前
MySQL创建了一个索引表,如何来验证这个索引表是否使用了呢?
数据库·mysql
前进的程序员15 分钟前
SQLite 数据库常见问题及解决方法
数据库·sqlite
zhcong_19 分钟前
MySQL数据库操作
数据库·mysql
Willis_m21 分钟前
Linux 服务器用 SSH 拉取多个 Git 工程
linux·服务器·git·ssh
紫金修道28 分钟前
【Linux】在Arm服务器源码编译onnxruntime-gpu的whl
linux·服务器·arm开发
xixingzhe234 分钟前
Nginx 配置多个监听端口
服务器·前端·nginx
摸鱼仙人~39 分钟前
HTTP 响应状态码总结
网络·网络协议·http
Clockwiseee42 分钟前
文件上传总结
运维·服务器·学习·文件上传
liyi_hz20081 小时前
O2OA(翱途)开发平台系统安全-用户登录IP限制
运维·服务器·网络·o2oa开发