news 2026/9/15 18:53:55

F´ 文件包(Fw::FilePacket)协议详解:数据结构、序列化实现与文件上下行应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
F´ 文件包(Fw::FilePacket)协议详解:数据结构、序列化实现与文件上下行应用

F´ 文件包(Fw::FilePacket)协议详解:数据结构、序列化实现与文件上下行应用

【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime

F´(F Prime)的Fw::FilePacket是飞行软件与嵌入式系统中文件传输的基础数据单元,负责把整份文件切分为"开始(START)、数据(DATA)、结束(END)、取消(CANCEL)"四种包,并通过下行遥测链路发送、在上行链路中重组还原。本文以 Fw/FilePacket/docs/sdd.md 设计文档为主体,结合 Fw/FilePacket/FilePacket.hpp 等源码实现与单元测试,逐字段讲解包格式、序列化/反序列化机制、校验和算法,并说明其在Svc::FileDownlink/Svc::FileUplink中的实际作用。读完本文,你将能够读懂 F´ 文件包字节流,并掌握基于该类型自行构造、解析文件包的方法。

1 Fw::FilePacket 是什么

Fw::FilePacket是 F´ 框架中表示"文件包"的类型,定义于 Fw/FilePacket/FilePacket.hpp。它的作用是把一个文件组织成多个自包含的数据包:每个包既能独立地被序列化为字节流(用于放入遥测/遥控链路传输),也能从收到的字节流中反序列化还原,供接收端重建文件。

从协议设计看,文件包的格式与 CCSDS 文件传输协议(CCSDS File Delivery Protocol,CFDP)中定义的协议数据单元(Protocol Data Unit,PDU)相似,设计参考了 CCSDS 727.0-B-4 推荐标准第 5 节的内容。因此,理解 CFDP PDU 的读者会对 F´ 文件包有天然的亲切感,但 F´ 的实现更加精简,去掉了 CFDP 的事务管理、NACK 等机制,只保留了与文件内容传输最直接相关的四种包类型。

在命名空间组织上,Fw::FilePacket实际是一个 C++union(见 Fw/FilePacket/FilePacket.hpp),其中嵌套定义了:

  • FilePacket::Header:包头类;
  • FilePacket::StartPacketFilePacket::DataPacketFilePacket::EndPacketFilePacket::CancelPacket:四种具体包结构;
  • FilePacket::PathName:路径名字符串的编码类。

从源码结构可以推断,联合体设计使同一个FilePacket对象根据包头中的类型字段被"视为"不同的子包(通过asStartPacket()asDataPacket()等访问器),从而在内存占用上复用空间,在接口上又保持类型安全。

2 每个文件包的公共结构

不论哪种类型,每个文件包都包含以下公共数据

字段大小说明
包类型(packet type)1 字节取值 START(0)、DATA(1)、END(2)、CANCEL(3)
序列索引(sequence index)4 字节无符号整数,用于标识包;每个文件从 0 开始,后续包逐一加 1
包数据(packet data)可变格式取决于包类型,见下文各节

这 5 字节的公共前缀即Header。源码中的定义如下(Fw/FilePacket/FilePacket.hpp):

class Header { private: Type m_type; // 包类型 U32 m_sequenceIndex; // 序列索引 public: enum { HEADERSIZE = sizeof(U8) + sizeof(U32) }; // 固定 5 字节 // ... };

包类型枚举(Fw/FilePacket/FilePacket.hpp):

typedef enum { T_START = 0, // START 包 T_DATA = 1, // DATA 包 T_END = 2, // END 包 T_CANCEL = 3, // CANCEL 包 T_NONE = 255 // 未初始化 } Type;

序列化时,Header 先写入 1 字节类型,再写入 4 字节序列索引(大端序由SerialBuffer保证,见 Fw/FilePacket/Header.cpp);反序列化时按相同顺序读取(Fw/FilePacket/Header.cpp)。

序列索引是文件包重组的关键:发送端为同一文件的包从 0 递增编号,接收端据此判断包是否按序到达。START 包固定为序列索引 0(见下文),因此一个文件的第一个 DATA 包通常从索引 1 开始。

2.1 路径名字符串编码(PathName)

START 包中的源路径与目标路径使用FilePacket::PathName编码。它的线上格式为:

字段大小说明
路径长度1 字节无符号整数,路径字符串的字节数
路径值可变不含结尾\0的路径字符

路径最大长度为 255 字节(PathName::MAX_LENGTH = 255,见 Fw/FilePacket/FilePacket.hpp)。初始化时,PathName::initialize使用StringUtils::string_length计算长度并截断到 255(Fw/FilePacket/PathName.cpp);序列化时先写长度字节,再pushBytes写入路径值(Fw/FilePacket/PathName.cpp)。bufferSize()返回sizeof(m_length) + m_length,即 1 + 路径长度。

3 四种包格式详解

以下各节对应设计文档第 2.1~2.4 节,并补充源码级字段验证。

3.1 START 包(开始传输)

START 包用于宣告一次文件传输的开始,包类型为 START、序列索引固定为 0。其数据结构为:

字段大小说明
包头5 字节类型 = START(0),序列索引 = 0
文件大小4 字节整个文件的字节数
源路径长度1 字节源路径字节数
源路径可变文件源路径(下行时为待发送文件路径)
目标路径长度1 字节目标路径字节数
目标路径可变文件目标路径(接收端落盘路径)

源码中StartPacket::initialize将 header 固定初始化为T_START, 0(Fw/FilePacket/StartPacket.cpp),与文档"序列索引为零"的描述完全一致。字段成员为U32 m_fileSize与两个PathName(Fw/FilePacket/FilePacket.hpp)。

3.2 DATA 包(文件数据)

DATA 包承载文件的实际内容,包类型为 DATA,序列索引递增。其数据结构为:

字段大小说明
包头5 字节类型 = DATA(1),序列索引
字节偏移4 字节本包文件数据在整个文件中的起始字节偏移
数据长度2 字节本包携带的文件数据字节数
文件数据可变文件内容片段

注意这里有两个"长度"概念的区别:数据长度(2 字节)指示本包内数据的字节数;字节偏移(4 字节)指示这段数据在整个文件中的位置,二者共同支持接收端把各包数据写入文件的正确位置。接收端校验规则也很严格:反序列化时要求"缓冲区剩余字节数必须恰好等于m_dataSize",否则返回FW_DESERIALIZE_SIZE_MISMATCH(Fw/FilePacket/DataPacket.cpp)。

由于数据长度是U16,单个 DATA 包最多携带 65535 字节数据,文件实际发送时会被切分为多个 DATA 包。从结构上看,DATA 包头部(含字节偏移与数据长度)总大小为Header::HEADERSIZE + sizeof(U32) + sizeof(U16) = 11字节,源码以DataPacket::HEADERSIZE常量给出(Fw/FilePacket/FilePacket.hpp)。

3.3 END 包(传输结束)

END 包宣告文件传输成功结束,包类型为 END。其数据结构为:

字段大小说明
包头5 字节类型 = END(2),序列索引
校验和4 字节按 CFDP 协议由文件数据计算出的 32 位哈希值

END 包的校验和取自CFDP::Checksum对象(见第 5 节)。源码中EndPacket内部保存U32 m_checksumValue,通过setChecksum/getChecksumCFDP::Checksum互转(Fw/FilePacket/EndPacket.cpp)。反序列化时同样要求缓冲区恰好用尽,剩余字节数必须为 0(Fw/FilePacket/EndPacket.cpp)。接收端比对 END 包中的校验和与本地按文件内容计算的校验和,即可判断文件是否完整无损。

3.4 CANCEL 包(取消传输)

CANCEL 包用于中止一次文件传输,包类型为 CANCEL,不携带任何数据。其结构只有 5 字节包头:

字段大小说明
包头5 字节类型 = CANCEL(3),序列索引

源码中CancelPacket只有 header 成员,bufferSize()即 header 大小;反序列化时也强制剩余字节数为 0(Fw/FilePacket/CancelPacket.cpp)。典型使用场景是发送端发现错误后通知接收端丢弃已收到的该文件片段。

4 序列化与反序列化机制

Fw::FilePacket的所有包都通过Fw::SerialBuffer完成字节流的写入与读取。

序列化(对象 → 字节流):调用toBuffer(Fw::Buffer&),例如StartPacket::toBuffer用目标Buffer构造SerialBuffer后依次写入包头、文件大小、路径等(Fw/FilePacket/StartPacket.cpp)。FilePacket::toBuffer根据当前类型分派到对应子包的toBuffer(Fw/FilePacket/FilePacket.cpp)。

反序列化(字节流 → 对象)FilePacket::fromBuffer先用缓冲区数据构造并fill()一个SerialBuffer,再交给fromSerialBuffer处理(Fw/FilePacket/FilePacket.cpp)。fromSerialBuffer首先解析 Header,随后依据包类型分派到对应子包解析,同时处理两类异常情况(Fw/FilePacket/FilePacket.cpp):

  • 类型为T_NONE(未初始化对象):返回FW_DESERIALIZE_TYPE_MISMATCH
  • 类型为其他未知值:返回FW_DESERIALIZE_INVALID_DATA

各子包解析完成后还会核对缓冲区剩余字节数,任何多余或不足的字节都会触发FW_DESERIALIZE_SIZE_MISMATCH,这一严格检查保证了包边界与字段布局的健壮性。

缓冲区大小计算:每种包都实现bufferSize(),汇总各字段占位:

  • Header:sizeof(U8) + sizeof(U32)= 5 字节;
  • StartPacket:header + 4 字节文件大小 + 源路径(1+len) + 目标路径(1+len);
  • DataPacket:header + 4 字节偏移 + 2 字节长度 + 数据长度;
  • EndPacket:header + 4 字节校验和 = 9 字节;
  • CancelPacket:仅 header = 5 字节。

5 END 包校验和:CFDP 32 位校验算法

END 包中的 4 字节校验和并非普通 CRC,而是遵循 CFDP 协议规定的 32 位累加校验,实现在 CFDP/Checksum/Checksum.hpp 中。

算法核心规则(摘自该类注释,CFDP/Checksum/Checksum.hpp):

  • 从文件起始位置开始,以4 字节窗口按每次 4 字节向上滑动,将窗口内数据与现有校验值做逐字节加法,溢出进位直接丢弃;
  • 若更新从非 4 字节对齐的文件偏移开始,窗口仍从最近一个 4 字节边界起算,左侧以零填充;
  • 若更新数据在非对齐字节处结束,窗口延伸至下一个边界,右侧以零填充。

注释中给出的示例:对缓冲区0xDE 0xAD 0xBE 0xEF 0xCA 0xFE,初始校验值为 0:

Update 1: Window 0xDE 0xAD 0xBE 0xEF → Checksum = 0xDEADBEEF Update 2: Window 0xCA 0xFE → Checksum = 0xDEADBEEF + 0xCAFE0000 = 0xA8ABBEEF

发送端通过Checksum::update(data, offset, length)逐段累加整份文件内容后取getValue();接收端对收到的文件做同样计算,与 END 包携带值比对。需要特别注意的是,updatedata指向的是本次更新数据段的起始字节(通常即&file_buffer[offset]),而offset用于对齐判断——理解这一点对正确复现校验至关重要。

6 代码中使用方式

FilePacket作为union,提供了两套对称的构造与访问接口(Fw/FilePacket/FilePacket.hpp):

构造(从具体包 → FilePacket)

Fw::FilePacket packet; packet.fromStartPacket(startPacket); // 或 fromDataPacket / fromEndPacket / fromCancelPacket packet.toBuffer(buffer); // 序列化为字节流

访问(从 FilePacket → 具体包):反序列化后依据asHeader().getType()判断类型,再调用对应访问器,访问器内部会以FW_ASSERT断言类型匹配(Fw/FilePacket/FilePacket.cpp):

const Fw::FilePacket::Header& hdr = packet.asHeader(); switch (hdr.getType()) { case Fw::FilePacket::T_START: const auto& sp = packet.asStartPacket(); // sp.getFileSize(), sp.getSourcePath(), sp.getDestinationPath() break; case Fw::FilePacket::T_DATA: const auto& dp = packet.asDataPacket(); // dp.getByteOffset(), dp.getDataSize(), dp.getData() break; // T_END / T_CANCEL 同理 }

测试方面,仓库提供了两套验证设施:单元测试入口 Fw/FilePacket/test/ut/FilePacketMain.cpp 对 Header、各类包进行"构造→序列化→反序列化→比对"的往返测试;GTest 辅助头 Fw/FilePacket/GTest/FilePackets.hpp 为每种包类型提供compare比较函数,供Svc::FileDownlinkSvc::FileUplink等上层组件的单元测试复用,验证上下行路径上文件包的正确生成与解析。

7 在 F´ 文件上下行链路中的角色

Fw::FilePacket是 F´ 文件传输功能的底层载体,其典型使用方为Svc/FileDownlinkSvc/FileUplink两个组件(源码位于 Svc/FileDownlink 与 Svc/FileUplink):

  • 下行(FileDownlink):读取待发送文件,先发出一个 START 包(携带文件大小与源/目标路径),再按块发出若干 DATA 包(每包带偏移与数据),最后发出携带 CFDP 校验和的 END 包;若中途失败则发出 CANCEL 包;
  • 上行(FileUplink):接收上述各类包,根据序列索引与字节偏移把 DATA 包内容写入目标文件,文件写完后按同样算法计算校验和并与 END 包比对,完成文件重组与完整性验证。

因此,Fw::FilePacket的字段布局直接决定了上下行链路的字节流协议:链路双方必须对四种包格式、路径编码、偏移语义与校验算法保持一致的理解,这正是本文逐字段讲解的价值所在。若读者希望进一步实践,可以在本地按 F´ 文档构建框架后,结合 Fw/FilePacket/test/ut/FilePacketMain.cpp 运行单元测试,观察各类包的序列化结果,或参考 FileDownlink/FileUplink 的测试代码跟踪完整文件传输流程。

8 总结

Fw::FilePacket用简洁的 5 字节包头 + 四类变长数据体,完整定义了 F´ 框架中文件传输的线上协议:START 宣告传输并携带路径与文件大小,DATA 以"偏移 + 长度"切片传输内容,END 以 CFDP 32 位校验和确认完整性,CANCEL 用于中止。其序列化/反序列化依托Fw::SerialBuffer实现,并以严格的大小与类型检查保证健壮性。理解这一数据结构,是阅读、调试乃至扩展 F´ 文件上下行功能的起点。

【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 18:51:16

北京百度网站排名优化速查手册:告别零流量的3个设计坑

北京百度网站排名优化速查手册:告别零流量的3个设计坑 网站上线三个月,后台看着空荡荡的访问记录,心里是不是在滴血?很多北京的项目经理都遇到过这种尴尬:代码写得再漂亮,服务器跑得再快,只要百度不给流量,一切白搭。这背后往往不是内容的问题,而是 网站结构设计 没对齐搜索引擎的抓取逻辑。…

作者头像 李华
网站建设 2026/9/15 18:51:14

抖音无水印下载5分钟实操:从一条视频到整账号备份

抖音无水印下载5分钟实操:从一条视频到整账号备份 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. …

作者头像 李华
网站建设 2026/9/15 18:50:26

AI如何革新学术专著写作:从文献处理到智能写作

1. 专著写作的范式革命:当AI遇上学术创作去年协助一位教授完成跨学科专著时,我们团队在文献综述环节遭遇了瓶颈——需要梳理近十年间发表的3000多篇相关论文。传统人工筛选方式至少需要两个月,而截稿日期就在眼前。当我引入语义分析工具构建文…

作者头像 李华
网站建设 2026/9/15 18:50:06

Dozzle 匿名统计机制全解:Beacon 字段、数据流向与隐私关闭方案

Dozzle 匿名统计机制全解:Beacon 字段、数据流向与隐私关闭方案 【免费下载链接】dozzle Realtime log viewer for containers. Supports Docker, Swarm and K8s. 项目地址: https://gitcode.com/GitHub_Trending/do/dozzle Dozzle 作为一款面向容器的实时日…

作者头像 李华