编写流协议:消息大小字段或消息分隔符?
Posted
技术标签:
【中文标题】编写流协议:消息大小字段或消息分隔符?【英文标题】:Writing a stream protocol: Message size field or Message delimiter? 【发布时间】:2010-11-06 00:10:57 【问题描述】:我即将编写一个通过 TCP 流的消息协议。接收者需要知道消息边界在哪里。
我可以发送 1) 固定长度的消息,2) 大小字段,以便接收者知道消息有多大,或者 3) 唯一的消息终止符(我想这不能在消息的其他任何地方使用)。
出于效率原因,我不会使用 #1。
我喜欢#2,但流有可能不同步吗?
我不喜欢第 3 个想法,因为这意味着接收者无法提前知道消息的大小,并且还要求终止符不会出现在消息的其他位置。
对于#2,如果有可能不同步,我是否可以添加一个终止符,或者只要发送方程序发送的内容是正确的,我是否可以保证永远不会不同步?有必要做#2 AND #3吗?
请告诉我。
谢谢, jbu
【问题讨论】:
对于选项 #3,查看 字节填充 以了解在消息正文中使用分隔符值的方法。我不是说您应该使用选项#3,只是指出如何使分隔符在字节流中明确。 【参考方案1】:您使用的是 TCP,数据包传递是可靠的。因此,连接要么断开、超时,要么您将阅读整条消息。 所以选项 #2 没问题。
【讨论】:
我认为即使是 TCP 数据也会损坏。【参考方案2】:我同意 sigjuice。 如果您有一个大小字段,则 不需要 添加和消息结束分隔符 -- 但是,这是个好主意。 两者兼备会使事情变得更加健壮且更易于调试。
考虑使用标准netstring format,它既包括大小字段,也包括字符串结尾字符。 因为它有一个大小字段,所以可以在消息中使用字符串结尾字符。
【讨论】:
【参考方案3】:如果您是从头开始开发发送和接收代码,那么同时使用长度标头和分隔符并没有什么坏处。这将提供稳健性和错误检测。考虑只使用#2 的情况。如果你在 TCP 流中写入一个长度为 N 的字段,但最终发送的消息大小与 N 不同,那么接收端将无法更好地了解并最终感到困惑。
如果你同时使用#2 和#3,虽然不是万无一失的,但如果接收者在从 TCP 流中消耗 N 个字节后遇到分隔符,则它可以有更大程度的信心认为它正确接收了消息。您还可以安全地在消息中使用分隔符。
查看 HTTP Chunked Transfer Coding,了解同时使用 #2 和 #3 的真实示例。
【讨论】:
【参考方案4】:根据您的工作级别,#2 实际上可能不会出现不同步的问题(TCP 在数据包中具有序列号,并且如果流到达,它会以正确的顺序为您重新组合流顺序)。
因此,#2 可能是您最好的选择。此外,在传输的早期就知道消息的大小将更容易在接收端分配内存。
【讨论】:
此外,在传输的早期就知道消息的大小将更容易在接收端分配内存。 注意:一定要限制多少内存被分配。否则,您很容易受到大小字段为 2^32-1(或无论您的整数有多大)的自定义数据包的 DDoS 攻击,从而迅速填满您的内存。 如果长度被破坏,例如,变得比预期的大,事情就会变得非常错误。顺便说一句,TCP 可能会损坏某种数据。【参考方案5】:有趣的是这里没有明确的答案。 #2 通常在 TCP 上是安全的,并且经常在“现实世界”中完成。这是因为 TCP 保证所有数据都完好无损*并按发送顺序到达。
*除非以 TCP 校验和仍然通过的方式损坏。
【讨论】:
其实TCP并不能保证数据完好无损。 @guanboshen 谢谢。更新以反映这一点。【参考方案6】:回答旧消息,因为有东西要更正:
与此处声称的许多答案不同,TCP 不保证数据完好无损地到达。甚至实际上都没有。
TCP 协议有一个 2 字节的 crc 校验和,如果超过一位翻转,显然有 1:65536 的碰撞机会。这是一个很小的机会,它永远不会在测试中遇到,但是如果您正在开发的东西要么传输大量数据和/或被非常多的最终用户使用,那么这个骰子就会被抛出数万亿次(不是开玩笑,youtube每个用户每秒抛出大约 30 次。)
选项 2:由于您自己列出的原因,大小字段是唯一实用的选项。固定长度的消息会很浪费,并且分隔符标记需要通过某种编码-解码阶段运行整个有效负载,以替换至少三个不同的符号:开始符号、结束符号和表示已发生替换的替换符号。
除此之外,很可能还想使用某种带有严重校验和的错误检查。可能与加密协议一起实现作为消息有效性检查。
关于不同步的可能性: 这在每条消息中都是可能的,但有补救措施。
一个有用的方案是用一个标题开始每条消息。此标头可以很短(
现在接收端将始终处于以下两种状态之一:
-
等待新的消息头到达
接收更多数据到正在进行的消息中,其长度和校验和是已知的。
这样,接收者在任何情况下都会在最多一条消息的最大长度内不同步。 (假设消息长度字段中存在损坏的标头)
使用这种方案,所有消息都作为离散的有效载荷到达,即使中间有恶意破坏的数据,接收方也不会永远卡住,到达有效载荷的长度是预先知道的,并且成功传输的有效载荷已经通过额外的更长校验和验证,并且校验和本身已经过验证。所有这些的开销可能只是一个包含三个 64 位字段和两个定界符号的 26 字节标头。
(标头不需要替换编码,因为它只在一个正在进行的消息中被预期,并且整个 26 字节可以一次处理)
【讨论】:
“每个用户每秒 30 次”?真的吗?有参考吗? 我的文字可能有点绕。我的意思是用户(正在获取高清视频的视频数据)每秒获得约 30 个 tcp 数据包。每个数据包本质上都是掷骰子,如果它被损坏,crc 可能会意外匹配。一小部分已损坏,还有一小部分未被捕获。 header不需要replacement-encoding:当你不同步搜索headers时,message中的header符号会误导你。【参考方案7】:还有第四种选择:自描述协议,例如 XML。
【讨论】:
这不适用于纯二进制消息。它基本上是选项#3,但比单个分隔符差得多。以上是关于编写流协议:消息大小字段或消息分隔符?的主要内容,如果未能解决你的问题,请参考以下文章