文件互传中的数据帧设计

发布时间:2026/10/23 13:58:58

文件互传中的数据帧设计 前言前面文件互传的各个业务的设计实现分析的差不多了这里回顾分析下文件传输过程中文件数据帧的设计消息帧的设计以及其他的一些open Harmony关于内存对齐的设计。看完这篇就会理解为什么面试官老是揪着int占用几字节问不放了哈哈哈。消息帧设计就比如文件发送完以后软总线会把发送文件列表发送给接收端核对是否全部发完发送文件列表给接收端源码int32_t FileListToBuffer(const char **destFile, uint32_t fileCnt, FileListBuffer *outbufferInfo) { if (destFile NULL || outbufferInfo NULL || fileCnt 0) { TRANS_LOGE(TRANS_FILE, invalid param); return SOFTBUS_INVALID_PARAM; } int32_t errCode SOFTBUS_OK; uint32_t totalLength 0; uint32_t offset 0; for (uint32_t i 0; i fileCnt; i) { size_t fileNameLength strlen(destFile[i]); if (fileNameLength 0 || fileNameLength MAX_FILE_PATH_NAME_LEN) { TRANS_LOGE(TRANS_FILE, bad file name at index%{public} PRIu32, i); return SOFTBUS_INVALID_PARAM; } else { totalLength fileNameLength; } } size_t bufferSize totalLength (sizeof(struct FileListItem) * fileCnt); uint8_t *buffer (uint8_t *)SoftBusCalloc(bufferSize); if (buffer NULL) { TRANS_LOGE(TRANS_FILE, calloc filelist failed); return SOFTBUS_MALLOC_ERR; } for (uint32_t index 0; index fileCnt; index) { uint32_t fileNameSize strlen(destFile[index]); struct FileListItem *fileItem (struct FileListItem *)(buffer offset); fileItem-index htonl(index); fileItem-fileNameLength htonl(fileNameSize); offset sizeof(struct FileListItem); // note: no \0 here if (memcpy_s(fileItem-fileName, bufferSize - offset, destFile[index], fileNameSize) ! EOK) { TRANS_LOGE(TRANS_FILE, copy file name failed!); errCode SOFTBUS_MEM_ERR; break; } offset fileNameSize; } if (errCode ! SOFTBUS_OK) { SoftBusFree(buffer); return errCode; } outbufferInfo-buffer buffer; outbufferInfo-bufferSize offset; return SOFTBUS_OK; }逐行解析int32_t FileListToBuffer(const char **destFile, uint32_t fileCnt, FileListBuffer *outbufferInfo) { // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 参数校验 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ if (destFile NULL || outbufferInfo NULL || fileCnt 0) { return SOFTBUS_INVALID_PARAM; } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 第1轮循环计算所有文件名的总长度 ⭐ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ uint32_t totalLength 0; for (uint32_t i 0; i fileCnt; i) { size_t fileNameLength strlen(destFile[i]); if (fileNameLength 0 || fileNameLength MAX_FILE_PATH_NAME_LEN) { return SOFTBUS_INVALID_PARAM; // 文件名不能为空或超长 } totalLength fileNameLength; // 累加文件名长度 } // 假设 destFile [photo1.jpg, doc.pdf] // totalLength 11 7 18 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 分配缓冲区 ⭐ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ size_t bufferSize totalLength (sizeof(struct FileListItem) * fileCnt); // 18 8*2 34 字节 // ↑文件名 ↑每个Item头部8字节(indexfileNameLength) uint8_t *buffer (uint8_t *)SoftBusCalloc(bufferSize); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 第2轮循环填充二进制数据 ⭐⭐ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ uint32_t offset 0; for (uint32_t index 0; index fileCnt; index) { uint32_t fileNameSize strlen(destFile[index]); // 把 bufferoffset 位置强转为 FileListItem 指针 struct FileListItem *fileItem (struct FileListItem *)(buffer offset); // 填充头部转网络字节序 fileItem-index htonl(index); fileItem-fileNameLength htonl(fileNameSize); // offset 跳过 Item 头部8字节这里为什么传入结构体的大小就能跳过头部后面有讲到 offset sizeof(struct FileListItem); // 拷贝文件名到 fileName[0] 的位置不带 \0 memcpy_s(fileItem-fileName, bufferSize - offset, destFile[index], fileNameSize); // ↑ 目标地址 ↑ 剩余空间 ↑ 源数据 ↑ 长度 // offset 跳过文件名 offset fileNameSize; } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 输出结果 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ outbufferInfo-buffer buffer; outbufferInfo-bufferSize offset; return SOFTBUS_OK; }例子输入destFile [photo1.jpg, doc.pdf]fileCnt 2 第1轮计算总长度 strlen(photo1.jpg) 11 strlen(doc.pdf) 7 totalLength 18 分配bufferSize 18 8*2 34 字节 第2轮填充数据 index0 offset0 fileItem 指向 buffer0 fileItem-index htonl(0) 0x00000000 fileItem-fileNameLength htonl(11) 0x0000000B offset 0 8 8 memcpy(fileItem-fileName, photo1.jpg, 11) offset 8 11 19 index1 offset19 fileItem 指向 buffer19 fileItem-index htonl(1) 0x00000001 fileItem-fileNameLength htonl(7) 0x00000007 offset 19 8 27 memcpy(fileItem-fileName, doc.pdf, 7) offset 27 7 34 输出 buffer → 34字节的二进制数据 bufferSize 34最终二进制布局为什么需要两轮循环第1轮算总长度 → 才知道要分配多少内存 第2轮填充数据 → 往分配好的内存里写 不能合并成一轮 第1个文件时还不知道总长度 → 无法确定分配多少 如果先分配一个估算值 → 可能不够或浪费 两轮循环是最稳妥的方式柔性数组设计发送的帧里面如果帧需要包含的数据长度不确定怎么办这个项目提供了柔性数组的设计解决。方案1固定大小数组浪费空间 struct FileListItem { uint32_t index; uint32_t fileNameLength; char fileName[256]; // 固定256字节 }; → 文件名只有 a.jpg5字节但占了256字节 → 浪费251字节 → 文件名超过256字节就放不下 → 限制 方案2指针两次内存分配 struct FileListItem { uint32_t index; uint32_t fileNameLength; char *fileName; // 指针 }; → 需要单独 malloc 文件名 → 两次内存操作 → 序列化时麻烦要序列化结构体 单独序列化字符串 方案3柔性数组最佳✅ struct FileListItem { uint32_t index; // 4字节 uint32_t fileNameLength; // 4字节 char fileName[0]; // 0字节占位符 }; → 不占空间 → 实际数据紧跟在后面 → 长度由 fileNameLength 决定柔性数组是什么struct FileListItem { uint32_t index; // 4字节 uint32_t fileNameLength; // 4字节 char fileName[0]; // ← 这就是柔性数组 };在这里柔性数组只是一个标识符是一个大小为0的数组不占用结构体空间。普通数组 char name[32]; // 占32字节不管你用不用 → 浪费空间文件名可能只有5字节 → 限制长度超过32就放不下 柔性数组 char name[0]; // 占0字节只是一个标记 → 实际数据紧跟在结构体后面 → 长度由 fileNameLength 告诉你 → 不浪费空间不限制长度项目中的效果// 分配内存结构体(8) 文件名(11) 19字节 uint8_t *buffer SoftBusCalloc(sizeof(FileListItem) strlen(photo1.jpg)); // 内存长这样 // buffer 指向的地址 ┌──────────┬──────────┬─────────────────────┐ │ index0 │ len11 │ p h o t o 1 . j p g │ │ (4字节) │ (4字节) │ (11字节) │ └──────────┴──────────┴─────────────────────┘ 0 8 19 // 使用 FileListItem *item (FileListItem *)buffer; item-index 0; // 前4字节 item-fileNameLength 11; // 第5-8字节 memcpy(item-fileName, photo1.jpg, 11); // 从第9字节开始写 // ↑ fileName 就是 buffer8 的别名为什么使用memcpy而不是strcpy// note: no \0 here代码注释明确说了 memcpy(item-fileName, destFile[index], fileNameSize); // ↑ 没有 \0 结尾节省1字节 strcpy(item-fileName, destFile[index]); // ↑ 会自动加 \0 → 多1字节 → 网络传输浪费带宽1字节对齐这里就涉及到项目对内存对齐的处理了。当一个变量不满默认的4字节或8字节的时候编译器会自动进行补齐但是这样有两个坏处第一帧变长第二两端内容对不上了这是为了保障发送过去的数据和接收到的数据保持一致。发送端1字节对齐 [index(4)][len(4)][fileName...] 第8字节开始就是文件名 接收端默认4字节对齐 [index(4)][len(4)][填充(4)][fileName...] 第12字节才开始文件名 ← 位置对不上 → 两端结构体布局不一致 → 解析错误 1字节对齐后 两端都是 [index(4)][len(4)][fileName...] → 布局一致 → 正确解析 ✅对齐规则详解默认对齐是什么默认对齐值 编译器决定通常等于该平台上最大的基本类型对齐要求 常见平台 x86 / ARM (32位)默认4字节对齐 x86_64 / ARM64 默认8字节对齐 可以用 #pragma pack(N) 或 __attribute__((aligned(N))) 手动指定判断对齐值的公式成员的对齐值 min(成员自身大小, 结构体当前对齐值) 例1默认4字节对齐 struct Example { char a; // 大小1对齐值min(1,4)1 → 偏移0 // 下一个成员偏移011 int b; // 大小4对齐值min(4,4)4 → 偏移必须是4的倍数 // 当前偏移1不是4的倍数 // → 插入3字节填充到偏移4 }; 内存布局 偏移: 0 1 2 3 4 5 6 7 内容: [a] [填充][填充][填充] [ b b b b ] sizeof 8内存对齐在编码中的具体操作#pragma pack(push, 1) // 保存当前设置设为1字节对齐 struct FileListItem { uint32_t index; // 偏移0大小4 uint32_t fileNameLength; // 偏移4大小4紧接index char fileName[0]; // 偏移8大小0 }; // sizeof 8无填充 #pragma pack(pop) // 恢复之前的设置 对比默认对齐假设4字节 struct FileListItem { uint32_t index; // 偏移0 uint32_t fileNameLength; // 偏移4 char fileName[0]; // 偏移8 }; // sizeof 8这个例子没区别 但如果有个 char 成员在中间就有区别了 #pragma pack(push, 1) struct BadExample { char type; // 偏移0 uint32_t index; // 偏移1紧接type无填充 }; // sizeof 5 默认4字节对齐 struct BadExample { char type; // 偏移0 // 偏移1-3填充 uint32_t index; // 偏移4 }; // sizeof 8
延伸阅读

更多相关文章

2026/10/24 7:40:12

AI编程助手实战:从Codex原理到DeepSeek API集成配置全指南

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 1. 先搞清楚 Codex 到底是什么,以及它现在能解决什么问题 如果你在找 Codex 的安装包、使用教程,或者想知道它…

2026/10/23 9:58:48

基于AD590和uA741的温度测量电路设计与Multisim仿真

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 在模拟电子技术课程设计中,温度测量与信号转换是一个经典且实用的课题。很多同学在使用运算放大器进行温度传感器信号调理…

2026/10/23 4:01:54

计算机毕业设计之社团纳新管理系统设计与实现

本文论述了社团纳新管理系统的设计和实现,该网站从实际运用的角度出发,运用了计算机网站设计、数据库等相关知识,基于系统管理和Mysql数据库设计来实现的,网站主要包括用户、社团 、社团信息、社团类型、社团申请、系统管理等功能…

2026/10/24 8:23:45

大模型排行榜-Arena-Agent-榜单解读

Agent 排行榜到底怎么看:2.1 亿次真实会话拆出的 5 个维度,前六名其实分不出胜负数据截至 2026 年 10 月 11 日。Agent 行为数据取自 Arena AI 公开排行榜(arena.ai/leaderboard/agent),能力分取自 Artificial Analysi…

2026/10/24 8:23:45

从 RSSI 到楼层:蓝牙信标室内定位的部署、算法与工程实践

从 RSSI 到楼层:蓝牙信标室内定位的部署、算法与工程实践 一、开篇:户外的米级,室内的盲区 卫星定位给户外人员管理立下了米级基准,但人一进楼宇、车间、地库,墙体、钢结构和顶棚层层衰减卫星信号,位置就“…

2026/10/24 8:23:45

2026算力网络选型指南:6大评估维度+12家主流服务商横向拆解

2026年做算力网络选型,难点不在服务商数量,而在把“算力能否连、业务能否稳、成本能否控”拆成可验证的条件。工信部在2025中国算力大会披露(2025年8月),截至2025年6月底,全国智能算力规模达到788 EFLOPS&a…

2026/10/24 8:18:45

MATLAB相关性分析建模:从皮尔逊到偏相关与滚动诊断

简介:本资源是一套面向数学建模初学者与MATLAB入门者的相关性分析实战教学包,聚焦变量间关联性度量这一建模关键环节,适用于课程设计、竞赛备赛及科研数据预处理场景。压缩包共11个文件(194KB),含3个Excel体…

2026/10/23 22:56:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/24 0:02:40

MATLAB缩进快捷键与批量格式化实战指南

1. 被自动缩进折磨的那些年如果你写过MATLAB代码,大概率经历过这种场景:从某个文档、网页或者聊天记录里复制了一段代码,粘贴进编辑器之后,每一行前面都带着莫名其妙的空格,有的行首是四个空格,有的是一个T…

2026/10/24 0:02:40

用Poco实现Qt风格信号与槽的跨平台C++方案

简介:本资源是一份面向C中级开发者的技术实践工程,聚焦于在非Qt框架环境下模拟实现Qt核心通信机制——信号与槽,借助Poco网络库的事件系统完成解耦式对象通信。适用于需在跨平台C项目中引入松耦合回调机制、但受限于无法集成Qt依赖的开发场景…

2026/10/24 0:02:40

ESXi裸机虚拟化安装指南:从硬件兼容到虚拟机部署

1. 为什么还要折腾ESXi:从一台闲置机器说起手里攒了几台淘汰下来的迷你主机,配置不算差,i5加16G内存,扔了可惜,装个桌面系统又觉得浪费。这种场景下,ESXi就成了一个绕不开的选择。它是一套裸机虚拟化平台&a…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑