编写流协议:消息大小字段或消息分隔符?

时间:2009-06-25 23:05:23

标签: stream message protocols delimiter terminator

我即将编写一个通过TCP流的消息协议。接收方需要知道消息边界的位置。

我可以发送1)固定长度消息,2)大小字段,以便接收者知道消息有多大,或者3)唯一消息终止符(我猜这不能在消息中的任何其他地方使用)。

出于效率原因,我不会使用#1。

我喜欢#2,但是流可能会失去同步吗?

我不喜欢想法#3,因为这意味着接收者不能提前知道消息的大小,并且还要求终结符不会出现在消息的其他地方。

对于#2,如果可能不同步,我可以添加终结符,或者我保证永远不会失去同步,只要发送者程序在发送的内容中是正确的吗?是否有必要做#2 AND #3?

请告诉我。

谢谢, JBU

7 个答案:

答案 0 :(得分:5)

您正在使用TCP,数据包传送是可靠的。所以连接要么掉线,要么超时,否则你会读完整个信息。 所以选项#2没问题。

答案 1 :(得分:3)

根据您工作的级别,#2可能实际上不会出现同步失败的问题(TCP在数据包中有序列编号,并且如果它到达时会以正确的顺序重新组合流订单)。

因此,#2可能是你最好的选择。此外,在传输的早期知道消息大小将使得在接收端更容易分配内存。

答案 2 :(得分:3)

我同意sigjuice。 如果您有一个大小字段,则添加和消息结束分隔符不是必需 - 但是,这是一个好主意。 两者都使事情更加健壮,更容易调试。

考虑使用标准netstring format,其中包括大小字段和字符串结尾字符。 因为它有一个大小字段,所以可以在消息中使用字符串结尾字符。

答案 3 :(得分:2)

有趣的是,这里没有明确的答案。无论如何,#2对TCP都是安全的,并且经常在“现实世界”中完成。这是因为TCP保证所有数据都是未损坏的,并且按照发送的顺序到达,因此正确的实现不可能不同步。

答案 4 :(得分:1)

如果从头开始开发发送和接收代码,使用长度标头和分隔符都没有坏处。这将提供稳健性和错误检测。考虑一下你只使用#2的情况。如果你将长度字段N写入TCP流,但最终发送的消息大小与N不同,则接收端不会更好地知道并最终混淆。

如果同时使用#2和#3,虽然不是万无一失,但如果在从TCP流中消耗N个字节后遇到分隔符,则接收者可以更有信心地接收到消息。您还可以安全地在邮件中使用分隔符。

查看HTTP Chunked Transfer Coding,了解使用#2和#3的真实示例。

答案 5 :(得分:0)

还有第四种选择:自描述协议,例如XML。

答案 6 :(得分:0)

正在回答旧消息,因为有东西需要纠正:

与这里的许多答案不同, TCP不能保证数据不会损坏。甚至没有。

TCP协议具有2字节的crc-checksum,如果翻转多于1位,则很明显有1:65536的冲突机会。这是一个很小的机会,它在测试中永远不会遇到,但是如果您开发的东西要么传输大量数据和/或被非常多的最终用户使用,那么骰子就会被抛出数万亿次(不是在开玩笑,youtube每位用户每秒将其抛出30次。)

选项2:由于您自己列出的原因,尺寸字段是唯一实用的选项。固定长度的消息将是浪费的,并且定界符标记必须通过某种编码-解码阶段运行整个有效负载,以替换至少三个不同的符号:开始符号,结束符号和已发生信号替换的替换符号。

除此以外,最有可能要使用带有严重校验和的某种错误检查。可能与加密协议一起作为消息有效性检查而实现。


关于不同步的可能性: 每条消息都有可能,但是有补救措施。

一个有用的方案是用标题开始每个消息。此标头可能很短(<30个字节),并且包含消息有效负载长度,有效负载的最终正确校验和以及标头本身的第一部分的校验和。邮件也将具有最大长度。这样的短标题也可以用已知符号定界。

现在接收端将始终处于以下两种状态之一:

  1. 等待新邮件头到达
  2. 为正在进行的邮件接收更多数据,该邮件的长度和校验和已知。

通过这种方式,接收器在任何情况下最多将不同步一条消息的最大长度。 (假设邮件头字段中的标头已损坏,并且已损坏)

采用这种方案,所有消息均作为离散的有效载荷到达,即使中间存在恶意损坏的数据,接收器也不会永远卡住,有效载荷的长度事先已知,并且成功传输的有效载荷已经通过额外的更长校验和进行了验证,并且校验和本身已经过验证。所有这些开销可能只是一个26字节的标头,其中包含三个64位字段和两个定界符号。

(标头不需要替换编码,因为仅在状态不佳的消息中才期望使用该标头,并且可以立即处理整个26个字节)