如何将recvfrom()接收的数据存入结构体以避免UDP短包写入多余字节?
问题原因分析
- 核心问题:写入操作固定使用
data数组的总长度1400字节作为写入量,没有区分每个数据包的实际有效数据长度。PSG结构体头部固定长度为24字节(2个uint64_t+ 2个uint32_t),当recvfrom返回的接收总长度bytesrecv小于1424时,data数组中只有bytesrecv - 24字节是实际收到的有效数据,剩余部分是结构体未被覆盖的内存脏数据,这部分数据被写入文件就会导致末尾出现多余字节。 - 逻辑冗余问题:当前代码每次收到一个包就对整个vector排序并全量写入文件,会导致文件内容重复写入,建议调整为收完全部数据包后再统一排序写入。
解决方案
1. 新增字段记录每个包的有效数据长度
新增封装结构体,将PSG包和对应有效数据长度绑定存储,避免丢失接收长度信息:
#pragma pack(push, 1) struct PSG { uint64_t id; uint64_t size; uint32_t type; uint32_t count; uint8_t data[1400]; }; #pragma pack(pop) // 封装结构,存储PSG包和对应有效数据长度 struct PSGEntry { PSG pkg; uint32_t valid_data_len; };
2. 收包时增加合法性校验,计算有效长度
收包后先校验包完整性,再计算本次包的有效数据长度存入结构体,写入时使用有效长度替代固定的数组长度:
std::vector<PSGEntry> psg_vector; while(1) { PSG psg; int bytesrecv = recvfrom(m_sock, &psg, sizeof(PSG), 0, (sockaddr*)nullptr, nullptr); if (bytesrecv < 0) { perror("recvfrom"); close(m_sock); return -1; } // 非法包校验:至少要收到完整头部才处理 const uint32_t header_len = offsetof(PSG, data); if (bytesrecv < header_len) { continue; } // 计算有效data长度,不超过data数组最大容量 uint32_t valid_len = std::min( static_cast<uint32_t>(bytesrecv - header_len), static_cast<uint32_t>(sizeof(psg.data)) ); psg_vector.push_back({psg, valid_len}); } // 所有包收完后统一排序、写入 sort(psg_vector.begin(), psg_vector.end(), [](const auto& lhs, const auto& rhs) { return lhs.pkg.count < rhs.pkg.count; }); for (const auto& entry : psg_vector) { file.write( reinterpret_cast<const char*>(entry.pkg.data), entry.valid_data_len ); }
额外说明
如果你的业务场景需要边收边写,可以在每次收到新包后,只写入新的单个包数据,避免全量重复写入,降低IO开销。
内容的提问来源于stack exchange,提问作者Cancel
相关产品推荐
相关产品推荐

