面试题【网络收发数据与缓存大小不匹配问题,TCP/UDP分析】

来源:互联网 发布:wpf编程宝典 pdf 编辑:程序博客网 时间:2024/06/05 10:34

—————-此篇文章 比较基础——————

题目

前提: 发送方一次发送20M数据,接受缓存4K;
讨论: TCP 和 UDP 协议下 ,发送方、接收方现象,并解释原因。

先回答问题:

TCP协议:
发送方能发出去,接受方能接收到数据, 每次接收大小为4K,可循环接收;
【 相关函数: send recv】

UDP协议:
发送方发送失败返回-1,接受方自然没收到数据;【 相关函数: sendto recvfrom】

解释原因:
首先, 需要知道TCP和UDP最基本的区别: TCP 基于字节流、UDP 基于数据报;

——————————-先分析UDP协议——————————-

UDP是数据报,严格定界符,不可分割, 所以当使用sendto函数,
数据大小 (= 数据长+UDP头+IP头 )> 65535(最大报文长度) 就直接返回-1,发送失败;
那么这个公式是怎么来的呢?首先我们看一下IP头部结构:
这里写图片描述

再看一下UDP头部结构:
这里写图片描述
UDP头中有一个字段是UDP长度占16位,理论上最大能表示的数字就是2^16 = 65535! 这个数值描述的是 封装上UDP头后的报文长度。

再看IP头,IP报文数据报总长也是16位即65535。
故而,使用UDP协议数据报最大长度 = 65535 - 20(IP头)-8(UDP头) = 65507
可使用sendto函数检验,当 数据的长度 参数填写数值大于65507时直接返回-1

以前学习的时候并没有注意过着点, 现在发现在互动百科中早有所注
这里写图片描述


分析完发送端,再说说接收端。此时我们把20M的数据分割成6K大小的数据包,发送端现在可以发送了。

接收缓冲区只有4K显然小于发送的6K数据, 那么那么会出现什么现象呢?
接收缓冲区能够接收到4K的有效数据,不能够循环接收;

解释这里需要先知道这个概念:

IO操作在内核中分2步:
1. 数据准备阶段
2. 数据准备完,将数据从内核空间拷贝到用户空间。

假设我们使用recvfrom 函数,此时线程会处于阻塞状态,一直等到步骤1,2完成后返回。当数据准备完成,内核将数据从内核空间拷贝到用户空间;

UDP 与TCP 又有一点不同的是: TCP有接收缓存,而UDP没有;
因为IO机制定是一样的,所以UDP无法循环接收,TCP可以,推测是因为此原因。 没有深入,不敢断言。

——————————-再析TCP协议——————————-

TCP传输的实质,是流式传输,就是不管你发送的时候是一次发送多少,TCP的底层处理,都是一个字节一个字节按流发送的。
发送端: 可以正常发送出去;
接收端:能接收到4K数据, 可循环接收;

我们不能只回答个 “因为他是流式传输”,当然还有协议层面的原因。

先知道两个概念:

1. 最大传输单元MTU:是数据链路层中的网络对数据帧的一个限制,以以太网为例,MTU为1500字节,一个IP数据报在以太网中传输,如果它的长度大于MTU的值,就要进行分片传输,使得每片数据报的长度小于MTU。分片传输的IP数据报不一定按序到达,但IP首部中的 信息下面详解】能让这些数据报片按序组装。IP数据报的分片与重组是在网络层中完成的。

在IP头中有三个字段: 分组ID(16位),标志(3位)、段偏移量(13位);
分组ID:唯一标识一个数据报,可以将之当成一个计数器,每发送一个数据包,该值就+1,如果数据报分片,则每个分片的ID都相同,各个分片共享一个标识号。
标志:

  1. bit0 不使用;
  2. bit1:DF(不分片),若该位为1,并且传输的数据报超过MTU,则该数据报会被丢弃,并发送一个ICMP差错报文;
  3. bit2:MF(more fragment 更多分片),如果该位为1,则说明后续还有分片,最后一片MF为0;

段偏移量:指出该分段的第一个数据字节在原始数据报中的偏移位置(以8字节为单位),IP分片后每一个分组都具有自己的首部,而且标志位相同,但是片偏移值不同,通过片偏移值接收端可以重新组装IP包;

2. 最大报文段长度MSS:TCP协议中的概念。MSS是TCP数据包每次能够传输的最大数据分段,TCP报文段大于MSS时要分段。该值在建立链接时进行协商,MSS选项只出现在SYN报文段中,即TCP三次握手的前两次。

MSS的值一般为:MTU-IP头-TCP头。若链路层用以太网(MTU= 1500),MSS的值通常为: 1500 - 20 - 20 = 1460。而Internet上标准的MTU为576,那么默认的MSS为 576 - 20-20 = 536个字节。很多时候,MSS的值最好为512的倍数。TCP报文段的分段与重组是在传输层完成的。

到此小结一下:TCP分段的原因是MSS,IP分片的原因是MTU。因为MSS<= MTU,所以分段后的每一段TCP报文再加上IP头后不会超过MTU。因此TCP报文很少发生IP分片的情况。
再来看UDP,由于UDP不会自己分段,因此报文长度>MTU,会在网络层进行IP分片。

这块再单拎出来一个问题:
既然IP层已经对UDP报文分片了,那么为什么UDP报文不能超过65507呢?
这是因为IP头中的段偏移值!再看一遍段偏移的含义:

段偏移量(13位):指出该分段的第一个数据字节在原始数据报中的偏移位置(以8字节为单位),IP分片后每一个分组都具有自己的首部,而且标志位相同,但是片偏移值不同,通过片偏移值接收端可以重新组装IP包;

因为 段偏移13位, 最多能表式 2^13 = 8192 个单位,每个单位是8个字节,
最多能偏移的字节数 = 8192 * 8 - 1 = 65536 -1 = 65535
因此IP层即使对上层数据进行分片, 最多也就能偏移65535个字节的数据。

总结:
UDP报文是数据报,自己本身没有分段。在应用层最多一次能发送65507大小的数据,在网络层的IP分片也无法表示更多的量级(由于段偏移量的控制)。

TCP协议自己就有MSS控制来进行分段了,在应用层一次发送的大小没有什么限制, TCP有发送和接受缓存。把待发数据放到发送缓存中,TCP自己把数据进行分段,数据到网络层一般也不需要分片,就继续下传了。