2

我正在创建一个协议,让两个应用程序通过 TCP/IP 流进行通信,并且正在研究如何为我的消息设计标头。使用 TCP 标头作为初始指南,我想知道是否需要填充。我知道,当我们处理缓存时,我们希望确保存储的数据适合一行缓存,以便在检索数据时能够高效地完成。但是,考虑到应用程序将解析字节流并将其存储为它认为合适的方式,我不明白填充标头有何意义。

例如:我想通过一个包含 3 字节字段后跟 1 字节填充字段的消息头发送 32 位对齐。然后我将发送消息数据。

在这种情况下,接收者只会从流中取出 3 个字节并丢弃填充字节。然后开始读取消息数据。正如我所看到的,他不会以他想要的方式存储 3 个字节和消息数据。字节对齐的全部意义在于以一种有效的方式对其进行检索。但是,如果检索器不关心填充,如何有效地检索它?

如果没有填充,检索器只需从流中获取 3 个标头字节,然后获取数据字节。由于检索器以他想要的方式存储这些字节,因此填充是否完成有什么关系?

也许我错过了填充点。

从这篇文章中提取一个问题有点困难,但是根据我所说的,你们可能会指出我的误解。

请让我知道你们的想法。

谢谢,jbu

4

5 回答 5

2

如果消息正文的字对齐有一些用处,那么一定要填充消息以避免其他扭曲。如果大部分消息被处理为具有适当强度的机器字,则填充将是有益的。

如果消息是字节流,例如 xml,那么填充不会给你带来很多好处。

就实际设计有线协议而言,您可能应该考虑使用带有压缩的纯文本协议(包括标头),这可能会比您可能发明的任何手工设计的二进制协议使用更少的带宽。

于 2009-06-17T01:31:01.037 回答
2

考虑到应用程序将解析字节流并将其存储为它认为合适的方式,我不明白填充标头有何意义。

如果我是接收者,我可能会将一个缓冲区(即一个字节数组)传递给协议驱动程序(即 TCP 堆栈)并说,“当其中有数据时把它还给我”。

那么,我(应用程序)返回的是一个包含数据的字节数组。使用 C 风格的技巧,如“强制转换”等,我可以将此数组的某些部分视为单词和双字(不仅仅是字节)......只要它们适当对齐(这是填充可能的位置必需的)。

这是从字节缓冲区中的偏移量读取 DWORD 的语句示例:

DWORD getDword(const byte* buffer)
{
  //we want the DWORD which starts at byte-offset 8
  buffer += 8;
  //dereference as if it were pointing to a DWORD
  //(this would fail on some machines if the pointer
  //weren't pointing to a DWORD-aligned boundary)
  return *((DWORD*)buffer);
}

这是英特尔汇编中的相应函数;请注意,它是单个操作码,即访问数据的一种非常有效的方式,比读取和累积单独的字节更有效:

mov eax,DWORD PTR [esi+8]
于 2009-06-17T01:50:47.253 回答
1

如果您有一个 3 字节的标头并将其对齐为 4 个字节,则将未使用的字节指定为“保留以供将来使用”并要求这些位为零(拒绝格式不正确的消息)。这给你留下了一些可扩展性。或者您可能决定将字节用作版本号 - 最初为零,然后如果(何时)您对协议进行不兼容的更改,则将其递增。不要让值是“未定义”和“不在乎”;如果您以这种方式开始,您将永远无法使用它。

于 2009-06-17T02:57:57.460 回答
1

考虑填充的一个原因是您是否计划随着时间的推移扩展您的协议。可以有意留出一些填充以供将来分配。

考虑填充的另一个原因是在长度字段上保存几个位。即总是 4 或 8 的倍数可以节省长度字段的 2 或 3 位。

于 2009-06-17T02:25:14.547 回答
1

TCP 具有填充(可能不适用于您)的另一个很好的理由是它允许专用网络处理硬件轻松地将数据与标头分开。由于数据始终以 32 位边界开始,因此在路由数据包时更容易将标头与数据分开。

于 2009-06-17T02:27:05.680 回答