Protobuf校验和(CRC)

9
我要将一些大的对象存储到数据库(BLOB)中。在我看来,protobuf 是序列化/反序列化 BLOB 的最佳候选之一。尽管它是二进制格式,但仍然很容易读取和更改其内容(字符串、整数等)。因此,我需要某种数据验证,无论是原始 BLOB 还是被黑客或太聪明的用户修改后的 BLOB。
一种可能的方法是在表中有一个专用字段,称为 crc,计算 BLOB 的校验和并将其放置在那里。但在许多情况下,如果 crc 是 BLOB 本身的一部分,则会更好。
我可以在 protobuf 流的末尾添加额外的字节,但我将不得不删除它们(或者反序列化器将抛出“无效字段 blablabla”的异常)。
我可以将 protobuf 流放入包装器中,但这又是一个解封装/封装的开销。
是否有一种简单、便宜的方法来在 protobuf 流的末尾添加一些东西,以避免在反序列化期间需要进行其他操作?在 XML 中,我可以添加注释。我不认为 protobuf 中有注释,但是如何添加 CRC,使其成为一个或两个字节的示例?
2个回答

10

Protobuf流是可添加的。如果您知道一个数据中不存在的字段号,可以简单地追加该字段的数据。 如果您想添加1或2个字节的CRC数据,则“变长整数”可能是最好的选择(请注意,“变长整数”是一种7位编码格式,第8位是连续标记,因此您可能希望使用7、14或21位实际CRC数据),然后可以追加:

  • 所选字段号,左移3位,然后采用varint编码
  • CRC数据,采用varint编码

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

另一种避免此问题的方法是,在自己设计的某些框架机制中“封装”protobuf数据。例如,您可以选择执行:

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

我可能会选择第二个选项。请注意,如果您想要,可以选择使用“变长整数”编码而不是固定长度编码来表示长度前缀。然而,对于CRC来说,这可能不值得,因为它将是固定长度的。


好的,那我将使用头文件。作为额外的奖励,这将允许我进行版本控制,以防万一协议缓冲无法做到(例如,如果我决定突然制作基类,协议缓冲将无法处理旧的继承数据,我刚刚测试过了,而在 XmlSerializer中,制作继承的东西不是一个问题)。 - Sinatr

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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接