Protobuf 校验和 (crc)

Posted

技术标签:

【中文标题】Protobuf 校验和 (crc)【英文标题】:Protobuf checksum (crc) 【发布时间】:2014-05-11 23:43:58 【问题描述】:

我打算将一些大对象存储到数据库 (BLOB) 中。在我看来,protobuf 是序列化/反序列化 BLOB 的最佳候选者之一。尽管它具有二进制格式,但它仍然易于阅读和更改其内容(字符串、整数等)。所以我需要某种数据验证,无论何时原始 BLOB 或修改(被黑客?被太聪明的用户?)。

一种可能性是在表中有一个专用字段,称为crc,计算 BLOB 的校验和并将其放在那里。但是当 crc 是 BLOB 本身的一部分时(在许多情况下)会好得多。

我可以在 protobuf 流的末尾添加 额外 个字节,但我必须删除它们(否则反序列化器会抛出异常“invalid field blablabla”)。

我可以将 protobuf 流放入包装器中,但解包/包装又是开销。

是否有一种简单且便宜的方法可以在 protobuf 流的末尾添加一些内容,以避免在反序列化期间需要额外的操作?在 XML 中,我可以添加注释。我认为 protobuf 中没有注释,但是如何将 1 或 2 个字节的 CRC 放在示例中?

【问题讨论】:

【参考方案1】:

Crc 应该保存在之前。这使得通过使用Seek(跳过标题)从流中反序列化变得微不足道。

这是最简单的实现:

// serialize
using (var file = File.Create("test.bin"))
using (var mem = new MemoryStream())

    Serializer.Serialize(mem, obj); // serialize obj into memory first
    // ... calculate crc
    file.Write(new byte[]  crc , 0, 1);
    mem.WriteTo(file);


// deserialize
using (var file = File.OpenRead("test.bin"))

    var crc = file.ReadByte();
    // ... calculate and check crc
    file.Seek(1, SeekOrigin.Begin);
    Serializer.Deserialize<ObjType>(file);

【讨论】:

【参考方案2】:

Protobuf 流是可附加的。如果您知道数据中不存在的字段编号,您可以简单地将数据附加到该字段。如果您打算添加 1 或 2 个字节的 CRC 数据,那么“varint”可能是您最好的选择(请注意,“varint”是 7 位编码格式,第 8 位是延续标记,因此您可能想要使用7、14 或 21 位或实际的 CRC 数据),那么您可以追加:

选择的字段编号,左移 3 位,然后 varint 编码 CRC 数据,varint 编码

但是!问题在于解码器仍会经常解释和存储这些数据,这意味着如果您对其进行序列化,它会将这些数据包含在输出中。

避免这种情况的另一种方法是将 protobuf 数据封装到您自己设计的某种框架机制中。例如,您可以选择这样做:

4字节表示protobuf有效载荷长度,“n” “n”字节的 protobuf 负载 在“n”个字节上计算出的 2 个字节的 CRC 数据

我可能会选择第二个选项。请注意,如果需要,您可以为长度前缀选择“varint”编码而不是固定长度编码。不过,对于 CRC 来说可能不值得,因为 是固定长度的。

【讨论】:

好的,那我就用header了。作为奖励,这将允许我进行版本控制,以防 protobuf 不能(例如,如果我决定突然创建一个基类,protobuf 将 not work with old i> 继承数据,只是对其进行了测试,例如,在XmlSerializer 中,继承某些东西不是问题)

以上是关于Protobuf 校验和 (crc)的主要内容,如果未能解决你的问题,请参考以下文章

crc校验码计算方法是啥?

工控常用LRC XOR累加和CRC校验工具校验码自动生成软件多计算方式

CRC校验的原理是啥?

CRC和校验和有啥区别?

CRC和校验和有什么区别?

CRC校验的实现(C语言,CRC16)