Answers:
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协议,因此您将看到完整的数据。
您无法确定它们确实是一次物理到达。如果需要,TCP / UDP下的数据链路层可能会将您的数据包分割开。特别是如果您通过Internet或控制范围之外的任何网络发送数据,则很难预测到这一点。
但是无论数据是在接收器中以一个包还是多个包到达。操作系统应该抽象化这些数据包的串联,因此对于您的应用程序来说,一切似乎都是一次到达的。因此,除非您是内核黑客,否则在大多数情况下,您无需担心是否以一个或多个数据包形式传输此数据。
对于UDP,操作系统还将做一些抽象,因此接收数据的应用程序不必知道已传输了多少个数据包。但是与TCP的区别在于不能保证数据实际到达。也有可能将数据分成多个数据包,其中一些到达,而有些则没有。对于接收应用程序而言,无论它是否完整,它看起来都像是数据流。
可以肯定的是,如果在TCP流的最开始发送20个字节,则它不会以两个10个字节的形式到达。这是因为TCP堆栈不会发送这么小的段:存在最小MTU大小。但是,如果发送在流中间的任何位置,则所有投注均关闭。可能是您的协议栈需要10字节的数据来填充一个段并将其发送出去,然后接下来的10个字节转到另一个段。
您的协议栈将数据分为多个部分,并将它们放入队列中。块大小基于路径MTU。如果执行发送操作,并且仍然有队列中的数据待处理,则协议栈通常会窥视位于队列尾部的段,并查看该段中是否有空间添加更多数据。这个空间可能只有一个字节,所以即使是两个字节的发送也可能被分成两部分。
另一方面,数据分段意味着可以进行部分读取。当只有一个段到达时,接收操作可能会唤醒并获取数据。在广泛实现的套接字API中,接收调用可以要求20个字节,但可能返回10个字节。当然,可以在其上构建一个缓冲层,该缓冲层将阻塞直到接收到20个字节或连接中断为止。在POSIX世界中,该API可以成为标准的I / O流:您可以fdopen
使用套接字描述符获取FILE *
流,然后可以fread
在其上使用它来填充缓冲区,以使完整的请求能够满足read
所需的所有调用。
UDP数据报构成数据。每个发送调用都会生成一个数据报(但请参见下文有关瓶塞)。另一端接收完整的数据报(并且,在套接字API中,它必须指定足够大的缓冲区来容纳它,否则数据报将被截断)。大数据报通过IP分段而被分段,并被透明地重组为应用程序。如果缺少任何片段,则整个数据报都会丢失;在这种情况下,无法读取部分数据。
接口存在扩展,允许多个操作指定一个数据报。在Linux中,可以“塞住”套接字(防止发送)。在将其塞好后,将写入的数据组装到一个单元中。然后,当套接字被“解开”时,可以发送单个数据报。