2

我有一个简单的协议,由 4 个字段组成:

Field-1 (4-bits)
Field-2 (6-bits)
Field-3 (4-bits)
Field-4 (2-bits)

目前,我组织它们,使它们字节对齐为:

Field-1,Field-3,Field-2,Field-4

消息总共占用 2 个字节,开销为 0 个字节。

为了使其向后兼容,以便我可以理解来自以前版本的消息,我在开头添加了一个 1 字节的版本字段,它变为:

Version-Field,Field-1,Field-3,Field-2,Field-4

总共 3 个字节,开销为 1 个字节。

如何添加前向兼容性,以便我可以在新版本的协议中添加新字段,同时确保旧版本的软件仍然可以理解消息,并且开销尽可能低?

4

2 回答 2

2

通常,您的协议会指定每条消息具有:

  • 适用于所有未来版本的消息长度指示器。这通常是一个保证足够大的固定大小的整数,或者是一个使用扩展位的可变长度编码的整数,就像您在 VLQ 或 UTF-8 中看到的那样。
  • 您需要了解以解析消息的协议的最低版本的指标。这很重要,因为新版本可能会引入必须理解的内容。

然后,协议的每个新版本都允许您将新数据添加到符合协议先前版本的前缀,并且协议的每个版本都必须指定如何识别它定义的数据的结尾(在您的示例中固定长度,所以很容易),以及在某些未来版本中定义的数据的开始。

为了处理消息,消费者检查以确保它是一个足够高的版本,处理它理解的前缀,并使用长度字段跳过其余部分。

对于像你的协议这样空间受限的东西,我可能会做这样的事情:

  • 第一个字节是 4 位最小版本和 4 位长度字段。

  • 如果长度字段 L 在 0-11 之间,则消息的其余部分是 L+1 个字节长。

  • 否则,第一个字节之后的 L-11 个字节是包含长度的整数。
  • 当您必须了解的最低版本大于 15 时,则版本 15 之前的某些协议版本将在消息中定义附加版本信息。
于 2017-12-10T19:02:54.990 回答
1

通过使用此规则确保严格的 BC,您将拥有 FC:

新版本必须保持以前版本已知的字段布局。

如果你能遵守规则,你将自动拥有 BC 和 FC。因此,使用该规则,您只能通过将新字段附加到现有布局来添加它们。

让我用一个例子来解释。假设您需要为版本 2 添加这些字段:

Field-5 (1-bit)
Field-6 (7-bits)

请记住规则,新字段只能附加到现有布局。所以,这是版本 2 的消息布局:

Version-Field,Field-1,Field-3,Field-2,Field-4,Field-5,Field-6

因为版本 1 已知的布局是完整的,所以您的版本 1 代码可以读取任何版本的消息(伪代码):

function readMessageVersion1(byte[] input) {
    var msg = {};
    msg.version = input[0];

    msg.field1 = input[1] & 0x0f;
    msg.field3 = input[1] >> 4 & 0x0f;

    msg.field2 = input[2] & 0x3f;
    msg.field4 = input[2] >> 6 & 0x03;

    return msg;
}

版本 1 不需要检查版本字段,因为已知布局是无条件的。但是,版本 2 和所有其他版本都需要检查版本字段。假设我们使用值 2 来表示版本 2,这将执行(伪代码):

function readMessageVersion2(byte[] input) {
    var msg = readMessageVersion1(input);

    //check version field
    if (msg.version < 2) return msg;

    msg.field5 = input[3] & 0x01;
    msg.field6 = input[3] >> 1 & 0x7f;

    return msg;
}

代码中最重要的部分是它重用了以前版本中的代码,并进行了以下检查:

if (msg.version < 2) return msg;

版本 3 的代码可以像这样简单地遵循版本 2:

function readMessageVersion3(byte[] input) {
    var msg = readMessageVersion2(input);

    //check version field
    if (msg.version < 3) return msg;

    // read the input bytes here

    return msg;
}

将其视为未来版本的模板。通过遵循规则和示例,任何版本的协议都可以读取来自任何版本的消息,只需 1 字节开销。

于 2017-12-07T22:21:00.737 回答