您的位置:首页 > 理论基础 > 计算机网络

IP、TCP、UDP数据包长度问题

2017-03-20 19:46 423 查看
首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层。  
其中以太网(Ethernet)的数据帧在链路层
  
IP包在网络层  
TCP或UDP包在传输层   
TCP或UDP中的数据(Data)在应用层
  

它们的关系是 数据帧{IP包{TCP或UDP包{Data}}}







在应用程序中我们用到的Data的长度最大是多少,直接取决于底层的限制。   

我们从下到上分析一下:   

1.在链路层,由以太网的物理特性决定了数据帧的长度为(46+18)-(1500+18),其中的18是数据帧的头和尾,也就是说数据帧的内容最大为1500(不包括帧头和帧尾),即MTU(Maximum

Transmission Unit)为1500;  

2.在网络层,因为IP包的首部要占用20字节,所以这的MTU为1500-20=1480; 

3.在传输层,对于UDP包的首部要占用8字节,所以这的MTU为1480-8=1472;   

所以,在应用层,你的Data最大长度为1472。(当我们的UDP包中的数据多于MTU(1472)时,发送方的IP层需要分片fragmentation进行传输,而在接收方IP层则需要进行数据报重 组,由于UDP是不可靠的传输协议,如果分片丢失导致重组失败,将导致UDP数据包被丢弃)。

  

从上面的分析来看,在普通的局域网环境下,UDP的数据最大为1472字节最好(避免分片重组)。  

但在网络编程中,Internet中的路由器可能有设置成不同的值(小于默认值),Internet上的标准MTU值为576,所以Internet的UDP编程时数据长度最好在576-20-8=548字节以内。

---------------------------------------------------------------------------------  

MTU对我们的UDP编程很重要,那如何查看路由的MTU值呢?  

对于windows OS: ping -f -l   如:ping-f -l 1472 192.168.0.1  

如果提示:Packets needs to be fragmented but DF set.   则表明MTU小于1500,不断改小data_length值,可以最终测算出gateway的MTU值;   

对于Linux OS: ping -c -M do -s   如: ping -c 1 -M do -s 1472 192.168.0.1  

如果提示 Frag needed and DF set……   则表明MTU小于1500,可以再测以推算gateway的MTU。

-
建立tcp连接的两端在三次握手时会协商tcp mss大小,具体如下:
  Client 发出syn报文,其中option选项填充的mss字段一般为1460,同样server收到syn报文后,会发送syn+ack报文应答,option选项填充的mss字段也为1460;协商双方会比较syn和syn+ack报文中mss字段大小,选择较小的mss作为发送tcp分片的大小。通过比较,协商双方的tcp
mss都是1460。 

-------------------------------------------------------------------------------- 

IP数据包的最大长度是64K字节(65535),因为在IP包头中用2个字节描述报文长度,2个字节所能表达的最大数字就是65535。  

   

由于IP协议提供为上层协议分割和重组报文的功能,因此传输层协议的数据包长度原则上来说没有限制。实际上限制还是有的,因为IP包的标识字段终究不可能无限长,按照IPv4,好像上限应该是4G(64K*64K)。依靠这种机制,TCP包头中就没有“包长度”字段,而完全依靠IP层去处理分帧。这就是为什么TCP常常被称作一种“流协议”的原因,开发者在使用TCP服务的时候,不必去关心数据包的大小,只需讲SOCKET看作一条数据流的入口,往里面放数据就是了,TCP协议本身会进行拥塞/流量控制。
TCP提供的是一种面向连接的,可靠的字节流服务,TCP提供可靠性的一种重要的方式就是MSS。通过MSS,应用数据被分割成TCP认为最适合发送的数据 块,由TCP传递给IP的信息单位称为报文段或段(segment)。代表一个TCP

socket的结构体struct tcp_sock中有多个成员用于确定应用数据被分割成最大为多大的数据块较为合适(最大报文段长度MSS)。

    我们不难联想到,跟最大报文段长度最为相关的一个参数是网络设备接口的MTU,以太网的MTU是1500,基本IP首部长度为20,TCP首部是20,所以MSS的值可达1460(MSS不包括协议首部,只包含应用数据)。

    前面的TCP三次握手协议中我们看到,通讯的双方都通过TCP选项通告了自己期望接收的MSS值,该值直接来源于struct tcp_sock的成员advmss,而这个值直接取自于网络设备接口的MTU减去IP首部和TCP首部的长度。在本地以太网中可达1460(如果首部都

不含选项的话)。而成员rx_opt是一个结构体struct tcp_options_received,它记录的是来自对端的TCP选项通告,其成员mss_clamp表示mss的上限值,其来源就是对端的MSS通告,而mss_user是用户设置的mss,其优先级最高,如果有user_mss,则使用user_mss,忽略其它。

    从上面我们可以看到,MSS是可以通过SYN段进行协商的(MSS选项只能出现在SYN报文段中),但它并不是任何条件下都可以协商的,如果一方不接受来 自另一方的MSS值,并且没有user_mss,则MSS就定为默认值536字节(加上首部,允许576字节的IP数据报)。实际上,struct

tcp_sock->rx_opt->mss_clamp的初始值就定为536,等收到来自对端的MSS通告后,才进行修改。而结构体 struct tcp_sock的成员mss_cache用于缓存上次的有效的mss,其初始值也被定为536。

    函数mytcp_sync_mss为一个tcp socket中的mss相关的成员进行数据同步,其基本的一个算法是:

    1、当前的MSS正常情况下应该为mtu-IP首部-TCP首部(不包括选项)。

    2、structtcp_sock->rx_opt->mss_clamp中含有对端通告的能够接受的MSS值,如果该值小于第一步计算所得到的MSS,则以该值为准。

    3、IP首部如果带有IP选项,则MSS中要减去选项长度。

    4、如果MSS已经小于48了,则令其等于48。

    5、减去TCP首部中选项的长度。

    6、如果MSS当前已经大于滑动窗口大小的1/2,则取滑动窗口大小的1/2作为MSS值(但不能小于48)。

    7、成员mss_cache用于缓存下刚刚计算所得的MSS。

    所以,说本地以太网中MSS为1460的说法并不正确,它还会动态变化,如果IP首部和TCP首部中出现选项,则MSS要相应的减小,一般TCP首部中会有12字节的时间戳选项(外加两字节的填充选项),这时的MSS就等于1448。

    MSS的主要作用是限制另一端主机发送的数据的长度,同时,主机本身也控制自己发送数据报的长度,这将使以较小MTU连接到一个网络上的主机避免分段。

    struct tcp_sock有一个成员xmit_size_goal,用于记录该socket发送数据报时的segment的大小,一般情况下它的值就等于MSS(特殊情况有例外,以后再分析)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: