可以将TCP和UDP数据包拆分为几部分吗?


41

TCP数据包可以分段到达接收者吗?

例如,如果我使用TCP协议发送20个字节,我可以100%确定一次将恰好接收20个字节,而不是10个字节,然后再接收10个字节左右吗?

UDP协议也有同样的问题。
我知道UDP是不可靠的,数据包不能全部到达或以不同的顺序到达,但是单个数据包呢?如果到达,我可以确定它是完整的包装,而不是一个包装吗?


7
需要说明的地方:它称为TCP段和UDP数据报。他们不是小包。TCP =段,UDP =数据报,IP =数据包,以太网=帧,在所有其他层(AFAIK)上,它们仅被称为PDU(协议数据单元)。
joeqwerty

Answers:


33

TCP数据包可以分段到达接收者吗?

是。IP支持分段,尽管出于性能原因,TCP通常会尝试确定路径MTU并将其数据包保持比其小。碎片灾难性地增加了数据报丢失率。如果路径的丢包率为10%,则将数据报分成两个数据包会使数据报的丢包率几乎达到20%。(如果任一数据包丢失,则数据报丢失。)

您不必担心这一点,TCP层也不必担心。IP层将数据包重组为整个数据报。

例如:如果我使用TCP协议发送20个字节,是否可以100%确保一次可以接收到正好20个字节,而不是10个字节,然后再接收10个字节左右?

不,但这与数据包无关。从根本上说,TCP是不保留应用程序消息边界的字节流协议。

UDP协议也有同样的问题。我知道UDP是不可靠的,数据包无法全部到达或以不同的顺序到达,

TCP也是一样。数据包就是数据包。区别在于,TCP已将重试和重新排序内置到协议中,而UDP没有。

但是一包呢?如果到达,我可以确定它是完整的包装,而不是一个包装吗?

不,但这不是您的问题。UDP协议处理数据报重组。这是其工作的一部分。(实际上,IP协议是针对UDP协议执行此操作的,因此UDP只能通过在IP之上分层来完成它。)如果数据报被分成两个数据包,则IP协议会将其重组为UDP协议,因此您将看到完整的数据。


10
对于新手读者而言,可能有必要澄清最后一点:您将看到有关数据 的完整数据。如果任何拆分数据包丢失,则数据报将丢失,并且UDP层将永远不会知道它。只要接收到数据报中的所有数据包,它们都将在IP层组装,然后向上传递到UDP层。这并不排除在数据流中丢失“块”的可能性。不要成为一个书呆子,但是当我学习这些东西时,直到第二或第三次通过教科书时,我才意识到IP碎片和UDP丢失之间的区别。
贾斯汀ᚅᚔᚈᚄᚒᚔ2013年

20

您无法确定它们确实是一次物理到达。如果需要,TCP / UDP下的数据链路层可能会将您的数据包分割开。特别是如果您通过Internet或控制范围之外的任何网络发送数据,则很难预测到这一点。

但是无论数据是在接收器中以一个包还是多个包到达。操作系统应该抽象化这些数据包的串联,因此对于您的应用程序来说,一切似乎都是一次到达的。因此,除非您是内核黑客,否则在大多数情况下,您无需担心是否以一个或多个数据包形式传输此数据。

对于UDP,操作系统还将做一些抽象,因此接收数据的应用程序不必知道已传输了多少个数据包。但是与TCP的区别在于不能保证数据实际到达。也有可能将数据分成多个数据包,其中一些到达,而有些则没有。对于接收应用程序而言,无论它是否完整,它看起来都像是数据流。


网卡驱动程序不负责重组数据包,而不负责内核吗?
bluehallu 2013年

2
@Hallucynogenyc:除非情况有所变化,否则Internet协议旨在允许超过576字节的数据包在其传输过程中的任何时候被拆分,但是除了最终的接收者之外,别无他意。我认为,在大多数情况下,使用较大的数据包是为了减少开销。一旦数据包在旅途中的某个时刻被拆分,开销就已经产生了,因此在最终接收者不愿意提供任何帮助之前进行重组,如果必须重新拆分,可能会造成伤害。
2013年

我相信,虽然可以拆分超过576个字节的任何数据包,但不拆分小于该大小的数据包;不能处理拆分数据包的嵌入式系统应避免要求更大的数据。
2013年

1
@ mauro.stettler:我已经在“裸机”上写了一个TCP堆栈(写代码直接与许多网络接口芯片对话)。对于与带宽限制为576字节的链接进行通信的硬件来说,拆分较长的数据包很简单。重新组装数据包复杂得多,特别是因为在一个完整的数据包被接收之前,可能会收到许多不同的数据包。
2013年

有人希望它不会被分割成很小的有效载荷(大约10或20字节应该可以),因为ipv4上IP协议栈的每个点都有“保证的最大大小” :至少68个字节(包括IP标头,不计算较低级别的标头)。请参阅en.wikipedia.org/wiki/Maximum_transmission_unit中的第一表。与HOSTS要求的最小大小的576字节不同(即,传输的始发或结束,而不是所有中间跃点)。小心:有效负载仍然较低(因为每一层的标头占用一些空间)。
Olivier Dulac 2013年

14

例子。连续字符块对应于send()调用:

TCP:

Send: AA BBBB CCC DDDDDD E         Recv: A ABB B BCC CDDD DDDE

发送的所有数据均按顺序接收,但不一定是相同的块。

UDP:

Send: AA BBBB CCC DDDDDD E         Recv: CCC AA E

数据不一定是相同的顺序,也不一定要完全接收,但是消息将完整保留。


5

例如:如果我使用TCP协议发送20个字节,我是否可以100%确定一次将恰好接收20个字节,而不是10个字节,然后再接收10个字节左右?

不,TCP是一种流协议,它使数据保持顺序,但不按消息将其分组。另一方面,UDP是面向消息的,但是不可靠。SCTP具有两全其美的优点,但由于NAT破坏了Internet,因此无法在本地使用。


1

可以肯定的是,如果在TCP流的最开始发送20个字节,则它不会以两个10个字节的形式到达。这是因为TCP堆栈不会发送这么小的段:存在最小MTU大小。但是,如果发送在流中间的任何位置,则所有投注均关闭。可能是您的协议栈需要10字节的数据来填充一个段并将其发送出去,然后接下来的10个字节转到另一个段。

您的协议栈将数据分为多个部分,并将它们放入队列中。块大小基于路径MTU。如果执行发送操作,并且仍然有队列中的数据待处理,则协议栈通常会窥视位于队列尾部的段,并查看该段中是否有空间添加更多数据。这个空间可能只有一个字节,所以即使是两个字节的发送也可能被分成两部分。

另一方面,数据分段意味着可以进行部分读取。当只有一个段到达时,接收操作可能会唤醒并获取数据。在广泛实现的套接字API中,接收调用可以要求20个字节,但可能返回10个字节。当然,可以在其上构建一个缓冲层,该缓冲层将阻塞直到接收到20个字节或连接中断为止。在POSIX世界中,该API可以成为标准的I / O流:您可以fdopen使用套接字描述符获取FILE *流,然后可以fread在其上使用它来填充缓冲区,以使完整的请求能够满足read所需的所有调用。

UDP数据报构成数据。每个发送调用都会生成一个数据报(但请参见下文有关瓶塞)。另一端接收完整的数据报(并且,在套接字API中,它必须指定足够大的缓冲区来容纳它,否则数据报将被截断)。大数据报通过IP分段而被分段,并被透明地重组为应用程序。如果缺少任何片段,则整个数据报都会丢失;在这种情况下,无法读取部分数据。

接口存在扩展,允许多个操作指定一个数据报。在Linux中,可以“塞住”套接字(防止发送)。在将其塞好后,将写入的数据组装到一个单元中。然后,当套接字被“解开”时,可以发送单个数据报。


这是错误的:如果发送带有10或20字节有效负载的paquet,则将生成1个paquet,并且(如上所述),如果使用ipv4,即使添加其他协议层的所有标头,它也应该适合到68个字节以内,从而确保它经过1个数据包中的所有跃点。Tcp堆栈不会(如您的第一段所暗示的那样)“等到mtu装满(即,添加几个数据包以形成适当大小的数据包)”来发送数据包!...这种行为会破坏很多东西(即使这些“碎片”是从同一对主机发送到的)
Olivier Dulac

@OlivierDulac:那是不正确的。TCP通常会根据需要生成数据包,以尝试优化网络使用率,因此20个字节可能会以两个不同的数据包结尾(如Kaz所述)。可以使用TCP_NODELAY套接字选项进行控制,如果您的应用程序需要更快的TCP网络连接,该选项将禁用Nagles算法,该算法将字节分配给数据包。同样,68字节绝不是数据包长度的实际标准:1500字节是更常见的默认值(这在网络之间确实有所不同)。
jjmontes
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.