发布时间:2026/7/10 7:23:51
文件互传中的数据帧设计 前言前面文件互传的各个业务的设计实现分析的差不多了这里回顾分析下文件传输过程中文件数据帧的设计消息帧的设计以及其他的一些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/7/10 7:23:51

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

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

2026/7/10 7:23:51

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

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

2026/7/10 7:23:51

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

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

2026/7/10 8:28:53

深度冲突与Reversed-Z:解决Unity远处物体闪烁的浮点数精度陷阱

1. 项目概述:当远处的物体开始“打架”在Unity3D里做项目,尤其是开放世界或者大场景,你可能遇到过一种诡异的视觉错误:远处的山体、地面或者建筑物,它们的边缘会不停地闪烁、抖动,看起来像是两个表面在疯狂…

2026/7/10 8:28:53

HBM3E vs HBM3 vs HBM2E:5代技术演进与带宽/功耗/成本三维对比

HBM3E vs HBM3 vs HBM2E:五代技术演进与三维性能对比在人工智能和高性能计算领域,内存带宽已成为制约系统性能的关键瓶颈。传统DDR内存架构在面对TB级数据处理需求时显得力不从心,而高带宽内存(HBM)技术通过革命性的3D堆叠设计,正…

2026/7/10 8:28:53

WordPress 在现代技术栈中的定位与适用场景分析

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 那天下午,团队 Slack 频道里突然跳出一条消息:“WordPress 已被淘汰,至少被我们淘汰了 :)”。发消…

2026/7/10 8:28:53

车载网关市场未来10年展望:三大增长驱动力与百亿级蓝海机遇

2024 年,车载网关市场价值52 亿美元,预计到 2033 年将达到124 亿美元,2026 年至 2033 年的复合年增长率为10.4%。 全球车载网关市场正经历显著且快速的扩张,其主要驱动力来自现代车辆架构日益复杂的趋势、互联汽车技术的广泛应用以…

2026/7/10 8:23:53

Ubuntu下GStreamer录音播放避坑指南:PulseAudio设备绑定与Caps配置

1. 项目概述:为什么一个“录音播放”的小功能,值得花一整篇教程来拆解?在Ubuntu系统上实现声音录制与播放,表面看只是调用arecord和aplay两条命令的事——但如果你真这么试过,大概率会卡在第一步:录出来的w…

2026/7/10 5:21:51

国内大模型选型与企业级落地实战指南

我不能提供任何关于访问境外网络信息的技术方案或变通方法。根据中国法律法规和网络管理要求,所有互联网服务必须遵守国家关于网络安全、数据安全和内容安全的规定。ChatGPT及其后续版本(如所谓“GPT-5”)是由境外机构研发的大语言模型&#…

2026/7/10 2:34:05

三步实战方案:高效获取智慧教育平台电子课本PDF的完整流程

三步实战方案:高效获取智慧教育平台电子课本PDF的完整流程 【免费下载链接】tchMaterial-parser 国家中小学智慧教育平台 电子课本下载工具,帮助您从智慧教育平台中获取电子课本的 PDF 文件网址并进行下载,让您更方便地获取课本内容。 项目…

2026/7/10 0:02:49

5大实战技巧:用ExifToolGUI轻松解决照片元数据管理难题

5大实战技巧:用ExifToolGUI轻松解决照片元数据管理难题 【免费下载链接】ExifToolGui A GUI for ExifTool 项目地址: https://gitcode.com/gh_mirrors/ex/ExifToolGui 你是否曾为整理旅行照片时发现拍摄时间错乱而头疼?是否需要在数百张照片中批量…

2026/7/10 6:36:15

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…