Linux下UDP套接字recvfrom/sendto收发变长结构体解析问题问询
问题1:可变长度报文的读取方案
- 你当前使用的是
SOCK_DGRAM类型的UDP套接字,核心特性是**recvfrom每次调用最多读取一个完整的UDP数据报**,不会出现TCP的粘包问题,因此你预设100字节缓冲区的思路是完全可行的,只要确保100字节大于你所有业务报文的最大长度即可,不需要分两次先读header再读剩余内容。 - 你不需要担心读取操作提前执行的问题:UDP下把整包数据全部读到缓冲区之后再解析header是工业界通用做法,反而分两次调用
recvfrom读UDP会出错:第一次调用读header的时候内核已经把整个报文返回给你了,剩余的报文内容会直接被丢弃,第二次调用recvfrom会等待下一个新报文,完全不符合预期。 - 长度校验逻辑参考:
- 每次调用
recvfrom之后首先读取其返回值,该值就是本次实际收到的字节数,如果返回值小于header的固定长度,直接丢弃该非法报文。 - 读取header确定报文类型之后,校验实际收到的字节数是否和该类型结构体的预期长度一致:如果小于预期直接丢弃;如果大于预期可以选择丢弃或者仅取前N个有效字节,建议直接丢弃避免非法数据干扰。
- 每次调用
问题2:报文反序列化方案与现有代码问题
- 使用
uint8_t buffer[]逐字节存储报文的方案非常实用,是网络编程的通用实现方式,不存在实用性问题。 - 不需要额外的位操作保证长度一致,只要做好两步校验即可:一是校验实际收报长度不小于header长度,二是校验收报长度和对应header的报文预期长度匹配。
- 你现有代码存在几个安全隐患和bug:
- 没有处理
recvfrom的返回值:如果recvfrom调用出错返回负数,你直接读取deserializedData[0]会访问残留数据或者未初始化内存,触发未定义行为。 - header读取逻辑错误:你定义的
Pkt结构体中header是int类型(通常占4字节),但你仅读取了deserializedData[0]的第一个字节,只要header值大于255,读取到的header就会完全错误。 - 直接发送结构体的兼容问题:不同架构CPU、不同编译器的结构体内存对齐规则不同,加上大小端字节序差异,跨平台运行时接收端解析出来的数值会完全出错。
MSG_WAITALL参数无效:该参数仅对TCP套接字生效,UDP下要么返回整包要么不返回,加这个参数没有任何作用。
- 没有处理
- 优化建议:
- 把header改为固定大小的标准类型,比如
uint8_t或uint16_t,发送时统一转成网络字节序(大端),接收时转成本地字节序。 - 自定义报文结构体添加
__attribute__((packed))属性取消内存对齐,避免不同编译器对齐规则不一致的问题。 - 解析时不要直接把缓冲区强转为结构体指针,避免地址不对齐触发硬件异常,建议逐字段手动拷贝到本地结构体变量中。
- 把header改为固定大小的标准类型,比如
内容的提问来源于stack exchange,提问作者Jazzy
相关产品推荐
相关产品推荐

