如何标记TCP数据包的结尾?

28
在一个客户端/服务器应用程序中,文本数据的长度会在客户端和服务器之间来回发送。如何标记正在发送的数据包的末尾?例如,当服务器从客户端接收数据包时,服务器如何知道客户端数据包已经完全接收?
告诉服务器它将要接收到的数据包的全部长度,还是有一些东西标记数据包的末尾更常见?
发送的一些数据只有几个字符长,而有些则可能有数千个字符。

7
我猜你不是担心TCP数据包,而是TCP流中的应用层消息,对吗? - Seva Alekseyev
6个回答

45

TCP提供连续的数据流。TCP使用数据包实现,但TCP的整个重点是隐藏它们。

将其视为要在其上绘制的墙壁。墙壁由砖块构成。砖块用砂浆粘合在一起,并涂抹灰泥,使墙面变得光滑。砖块是IP数据包,TCP是灰泥。

因此,您现在拥有了经过平滑处理的TCP隧道,并且希望在其中添加一些结构。您想要绘制方框,以使绘画彼此分离。这就是您要做的事情:在数据中添加一些“管理”结构(在绘画周围添加方框)。

许多协议使用“数据包”的概念,这是一堆数据,以固定格式的管理标头开头。标头包含足够的信息来决定数据包的结束位置;例如,包括数据包长度。HTTP使用Content-Length标头进行此操作,或者(使用HTTP / 1.1)使用“分块传输编码”,其中数据被分成一个或多个小数据包,每个小数据包都具有仅由小数据包长度指示器构成的简单标头。

另一种方法是使用特殊的终止序列,该序列不能出现在“正常数据”中。如果您的数据是文本,则可以使用值为零的字节作为终止符。

另一种方法是使用自我终止数据。这是一种结构化的数据,可以在任何时候知道元素的结束位置。例如,XML数据组织为嵌套的标记对,如<foo>...</foo>。当到达结束标记(</foo>)时,您就知道元素已完成。


5

从HTTP中获取启示。

使用字符终止序列,或在消息头中指定长度,或使用两者的巧妙组合。

就像HTTP一样:标头以CR-LF-CR-LF结束。如果标头之后有数据,则数据长度在其中一个标头中。


1
当你需要发送任意数据时,终止序列会变得棘手,因为数据可能(巧合地)包含终止序列,这会使接收解析器混淆。你可以通过实现某种转义码协议来避免这种情况,但此时事情比仅仅先发送长度字段更加复杂,所以你最好只是先发送长度字段并保持简单。 - Jeremy Friesner
原帖中提到该协议涉及“长度不同的文本数据”。 “文本”意味着可能的字节值的某个子集,例如没有空字符。有一个终止符。话虽如此,原帖作者更清楚该协议可能是什么样子。 - Seva Alekseyev

5
请注意,如果您在开头编码长度,请小心垃圾。例如,如果您使用4个二进制字节表示长度,并且某个外部探测器发送了HTTP请求,则可能会得到一个巨大的数字并等待很久(更不用说分配缓冲区可能会导致程序崩溃)。我通过两个不同的函数分别发送长度两次,并进行比较(例如~len和len xor 0x139AF321)。如果有人试图主动破坏您的程序,您还应该设置最大值。如果我收到错误的长度,我会关闭连接。
如果您的流量已加密,这还需要额外的HMAC。

4
请确保您的数据包在开始时包含一个长度字段。关于数据包结构方面,您需要进行适当的规划。

3
如果发送方知道长度,那么发送方应该提前作为固定大小字段提供长度,然后是可变大小的数据。
与尾标记相比的优点在于接收方可以针对预期的数据量进行优化,例如,分配正确大小的缓冲区。例如,通过TCP/IP协议进行存储与你们面临的问题一样。在这些情况下,头部提供随后预期数据的长度。
在以后的发展中,你可能会发现其他位需要放在你的“头”中。你会很高兴有一些结构来增加自己的第五层协议。

2
如果你感到特别大胆,可以考虑使用 SCTP 套接字而不是 TCP 套接字。

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