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

UDP报文的数据结构

主要内容参照https://doc.embedfire.com/net/lwip/zh/latest/doc/chapter14/chapter14.html#id6,整理出来自用。

1. UDP 报文首部结构体(udp_hdr)

        为清晰定义 UDP 报文首部的各个字段,LwIP 设计了udp_hdr结构体,其包含 4 个核心字段,具体结构通过代码定义,各字段功能如下:

  • srcdest:均为 16 位无符号整数(u16_t),分别表示 UDP 通信的源端口号和目的端口号,用于标识通信双方的应用进程。
  • len:16 位无符号整数,代表 UDP 报文的总长度(包括首部和数据部分)。
  • chksum:16 位无符号整数,用于 UDP 报文的校验和计算,保障数据传输的完整性(若值为 0 则表示不进行校验)。

        结构体定义中使用PACK_STRUCT相关宏,是为了确保结构体在内存中紧凑存储,避免因编译器对齐规则导致字段偏移,保证数据解析的准确性。

2. UDP 控制块(udp_pcb)

        与 TCP 类似,LwIP 通过 “UDP 控制块” 管理 UDP 通信的所有关键信息,每个基于 UDP 的应用线程都会对应一个控制块,并与特定端口绑定,以便系统识别和处理该应用的 UDP 数据。

(1)控制块的核心组成

UDP 控制块结构体(udp_pcb)的内容可分为两部分:

#define IP_PCB                             \/* 本地ip地址与远端IP地址 */             \ip_addr_t local_ip;                      \ip_addr_t remote_ip;                     \/* 网卡id */                             \u8_t netif_idx;                          \/* Socket 选项 */                        \u8_t so_options;                         \/* 服务类型   */                         \u8_t tos;                                \/* 生存时间 */                           \u8_t ttl                                 \IP_PCB_NETIFHINT/** UDP控制块 */struct udp_pcb{IP_PCB;//指向下一个控制块struct udp_pcb *next;//控制块状态u8_t flags;/** 本地端口号与远端端口号 */u16_t local_port, remote_port;/** 接收回调函数 */udp_recv_fn recv;/** 回调函数参数 */void *recv_arg;};
  • 复用 IP 层信息:通过引入IP_PCB宏,直接包含 IP 层通信所需的基础信息,如本地 IP 地址、远端(目标)IP 地址、网卡 ID(netif_idx)、Socket 选项(so_options)、服务类型(tos)和生存时间(ttl),避免信息重复定义,简化 IP 层与 UDP 层的交互。
  • UDP 层专属信息:包括控制块链表指针(next,用于连接多个控制块)、控制块状态标识(flags)、本地端口号与远端端口号(local_portremote_port,核心标识字段,用于匹配应用线程),以及接收数据的回调函数(recv)和回调参数(recv_arg,用于数据递交给上层应用)。
(2)控制块的管理方式

        LwIP 会将所有 UDP 控制块通过next指针串联成一个链表,链表的头节点由全局变量udp_pcbs记录。这种链表结构便于系统在处理 UDP 数据时,通过遍历链表快速查找对应的控制块,提高数据处理效率。

(3)回调函数的注册

        回调函数(udp_recv_fn)是 UDP 层向应用层递交数据的关键接口,其函数原型(见原文代码清单 14_3)规定了参数格式:回调参数(arg)、对应的 UDP 控制块(pcb)、存储数据的缓冲区(pbuf)、数据来源的 IP 地址(addr)和端口号(port)。

        回调函数的注册通过udp_recv函数实现:该函数将用户定义(或系统默认)的回调函数及其参数,分别赋值给控制块的recvrecv_arg字段。实际开发中,若使用 NETCONN API 或 Socket API,LwIP 内核会自动注册recv_udp作为回调函数,无需用户手动实现;若使用 RAW API,则需用户自行定义并注册回调函数。

二、UDP 报文发送流程

        UDP 作为传输层协议,需接收上层应用数据并添加首部后,交付给 IP 层发送,核心逻辑由udp_sendto_if_src函数实现,整体流程简洁,具体步骤如下:

  1. 端口绑定检查:若当前 UDP 控制块未绑定本地端口(local_port为 0),则先调用udp_bind函数完成端口绑定,确保数据能被正确识别和处理。
  2. 数据长度与内存检查
    • 校验数据总长度:判断添加 UDP 首部(长度为 UDP_HLEN)后,总长度是否超过 16 位整数的最大值(避免溢出),若超过则返回内存错误(ERR_MEM)。
    • 检查缓冲区空间:尝试在当前数据缓冲区(pbuf)头部预留 UDP 首部空间,若空间不足,则新分配一个仅存储首部的pbuf,并与原数据缓冲区链接成链表(pbuf_chain)。
  3. 填充 UDP 首部:将控制块中的本地端口号、目标端口号,以及缓冲区总长度(转换为网络字节序,通过lwip_htons函数),分别填入 UDP 首部的srcdestlen字段;校验和字段(chksum)默认设为 0(表示不校验,可根据需求调整)。
  4. 交付 IP 层发送:调用ip_output_if_src函数,将封装好 UDP 首部的数据交付给 IP 层,由 IP 层负责通过指定网卡(netif)发送到目标地址;发送完成后,根据缓冲区是否为新分配,决定是否释放内存(pbuf_free),并更新相关统计指标(如udp.xmitmib2.udpoutdatagrams)。

        相较于 TCP,UDP 发送流程无需建立连接、重传确认等复杂逻辑,仅需完成首部封装和层间交付,处理效率更高。

三、UDP 报文接收流程

        当 IP 层接收到 UDP 报文后,会调用udp_input函数将数据递交给 UDP 层处理,核心是通过匹配控制块找到对应应用,并完成数据递交,具体步骤如下:

  1. 合法性初步校验

    • 检查报文长度:若当前缓冲区长度小于 UDP 首部长度(UDP_HLEN),则判定为无效报文,更新错误统计(如udp.lenerr)并释放缓冲区,直接结束处理。
    • 解析基础信息:提取 UDP 首部(转换为udp_hdr类型),判断报文是否为广播包(ip_addr_isbroadcast),并将源端口号、目的端口号从网络字节序转换为主机字节序(lwip_ntohs)。
  2. 遍历控制块链表匹配应用

    • 遍历udp_pcbs链表,对比控制块的 “本地端口号” 与报文的 “目的端口号”,同时通过udp_input_local_match函数校验 IP 地址匹配性(本地 IP 与报文目的 IP),筛选出候选控制块。
    • 进一步筛选 “完全匹配” 的控制块:在候选控制块中,对比控制块的 “远端端口号” 与报文的 “源端口号”、控制块的 “远端 IP” 与报文的 “源 IP”,若均匹配,则确定为目标控制块;若存在多个候选,会将完全匹配的控制块移至链表头部,优化后续查找效率。
    • 无完全匹配时的处理:若未找到完全匹配的控制块,会选取第一个未绑定远端信息(UDP_FLAGS_CONNECTED未置位)的候选控制块作为替代;若仍无候选,则判定为 “无对应应用”。
  3. 数据递交或差错反馈

    • 数据递交(找到对应控制块):先从缓冲区中移除 UDP 首部(pbuf_remove_header),提取纯数据部分;若控制块已注册回调函数(recv不为空),则调用该函数,将数据、源 IP、源端口等信息递交给上层应用(回调函数需负责后续缓冲区释放);若未注册回调函数,则直接释放缓冲区。
    • 差错反馈(无对应控制块):若报文非广播包或多播包,会构造 “端口不可达” 的 ICMP 差错报文(通过icmp_port_unreach函数),反馈给报文源主机,同时释放缓冲区并更新统计指标(如udp.proterrmib2.udpnoports)。
  4. 资源清理与统计更新:处理结束后,释放相关资源(如缓冲区),停止性能计时(PERF_STOP),并更新 UDP 接收相关的统计数据(如udp.recvmib2.udpindatagrams)。

http://www.dtcms.com/a/346870.html

相关文章:

  • Python训练营打卡Day41-Grad-CAM与Hook函数
  • 【亲测可用】Suno-API 调用使用指南
  • GEO优化服务引领AI时代营销变革 “AI黄金位”成企业竞争新焦点
  • Leetcode—931. 下降路径最小和【中等】
  • Nginx 优化(一)
  • 百度面试题:赛马问题
  • 小迪安全v2023学习笔记(七十讲)—— Python安全SSTI模板注入项目工具
  • 容器安全实践(三):信任、约定与“安全基线”镜像库
  • 博士招生 | 美国圣地亚哥州立大学 Yifan Zhang 课题组博士招生,AI 安全领域顶尖平台等你加入!
  • 使用 LangChain 和 Neo4j 构建知识图谱
  • Linux docker上部署Dify
  • Linux服务环境搭建指南
  • 第四十三天(JavaEE应用ORM框架SQL预编译JDBCMyBatisHibernateMaven)
  • 海外媒体引流进阶:指纹手机的全维度技术支持与实践应用
  • ​崩坏世界观中的安全漏洞与哲学映射:从渗透测试视角解构虚拟秩序的脆弱性​
  • Base64编码、AES加密、RSA加密、MD5加密
  • 基于大模型的对话式推荐系统技术架构设计
  • 【K8s】整体认识K8s--K8s架构与集群创建过程
  • Doxygen是什么?
  • 多模态大语言VLM模型综述
  • 【GPT入门】第56课 大模型分布式训练的三种方式、模型层介绍及DeepSpeed ZeRO的支持
  • 《Linux》基础命令到高级权限管理指南
  • 【KO】前端面试题三
  • React Hooks UseRef的用法
  • 【Win10 画图板文字方向和繁体问题】
  • 浮点数比较的致命陷阱与正确解法(精度问题)
  • linux下的网络编程:基础概念+UDP编程
  • Class41样式迁移
  • 55.Redis搭建主从架构
  • 计算机网络 各版本TLS握手的详细过程