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

IP数据包长度问题总结linux

首先要看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。
--------------------------------------------------------------------------------- 算法

IP数据包的最大长度是64K字节(65535),由于在IP包头中用2个字节描述报文长度,2个字节所能表达的最大数字就是65535。  
   
因为IP协议提供为上层协议分割和重组报文的功能,所以传输层协议的数据包长度原则上来讲没有限制。实际上限制仍是有的,由于IP包的标识字段终究不可能无限长,按照IPv4,好像上限应该是4G(64K*64K)。依靠这种机制,TCP包头中就没有“包长度”字段,而彻底依靠IP层去处理分帧。这就是为何TCP经常被称做一种“流协议”的缘由,开发者在使用TCP服务的时候,没必要去关心数据包的大小,只需讲SOCKET看做一条数据流的入口,往里面放数据就是了,TCP协议自己会进行拥塞/流量控制。  
   
UDP则与TCP不一样,UDP包头内有总长度字段,一样为两个字节,所以UDP数据包的总长度被限制为65535,这样刚好能够放进一个IP包内,使得 UDP/IP协议栈的实现很是简单和高效。65535再减去UDP头自己所占据的8个字节,UDP服务中的最大有效载荷长度仅为65527(好像还要减去IP头20个字节,最后为65507)。这个值也就是 你在调用getsockopt()时指定SO_MAX_MSG_SIZE所获得返回值,任何使用SOCK_DGRAM属性的socket,一次send的 数据都不能超过这个值,不然必然获得一个错误。  
   
那么,IP包提交给下层协议时将会获得怎样的处理呢?这就取决于数据链路层协议了,通常的数据链路层协议都会负责将IP包分割成更小的帧,而后在目的端重 组它。在EtherNet上,数据链路帧的大小如以上几位大侠所言。而若是是IP   over   ATM,则IP包将被切分红一个一个的ATM   Cell,大小为53字节。编程

******************************************************************************************************************************windows

******************************************************************************************************************************缓存

    CP提供的是一种面向链接的,可靠的字节流服务,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相关的成员进行数据同步,其基本的一个算法是:
    一、当前的MSS正常状况下应该为mtu-IP首部-TCP首部(不包括选项)。
    二、structtcp_sock->rx_opt->mss_clamp中含有对端通告的可以接受的MSS值,若是该值小于第一步计算所获得的MSS,则以该值为准。
    三、IP首部若是带有IP选项,则MSS中要减去选项长度。
    四、若是MSS已经小于48了,则令其等于48。
    五、减去TCP首部中选项的长度。
    六、若是MSS当前已经大于滑动窗口大小的1/2,则取滑动窗口大小的1/2做为MSS值(但不能小于48)。
    七、成员mss_cache用于缓存下刚刚计算所得的MSS。
    因此,说本地以太网中MSS为1460的说法并不正确,它还会动态变化,若是IP首部和TCP首部中出现选项,则MSS要相应的减少,通常TCP首部中会有12字节的时间戳选项(外加两字节的填充选项),这时的MSS就等于1448。
    MSS的主要做用是限制另外一端主机发送的数据的长度,同时,主机自己也控制本身发送数据报的长度,这将使以较小MTU链接到一个网络上的主机避免分段。
    struct tcp_sock有一个成员xmit_size_goal,用于记录该socket发送数据报时的segment的大小,通常状况下它的值就等于MSS(特殊状况有例外,之后再分析)。
----------------------------------------网络

 

以太网(IEEE 802.3)帧格式:socket

一、前导码:7字节0x55,一串一、0间隔,用于信号同步tcp

二、帧起始定界符:1字节0xD5(10101011),表示一帧开始函数

三、DA(目的MAC):6字节工具

四、SA(源MAC):6字节

五、类型/长度:2字节,0~1500保留为长度域值,1536~65535保留为类型域值(0x0600~0xFFFF)

六、数据:46~1500字节

七、帧校验序列(FCS):4字节,使用CRC计算从目的MAC到数据域这部份内容而获得的校验和。

以CSMA/CD做为MAC算法的一类LAN称为以太网。CSMA/CD冲突避免的方法:先听后发、边听边发、随机延迟后重发。一旦发生冲突,必须让每台主机都能检测到。关于最小发送间隙和最小帧长的规定也是为了不冲突。

考虑以下的状况,主机发送的帧很小,而两台冲突主机相距很远。在主机A发送的帧传输到B的前一刻,B开始发送帧。这样,当A的帧到达B时,B检测到冲突,因而发送冲突信号。假如在B的冲突信号传输到A以前,A的帧已经发送完毕,那么A将检测不到冲突而误认为已发送成功。因为信号传播是有时延的,所以检测冲突也须要必定的时间。这也是为何必须有个最小帧长的限制。

按照标准,10Mbps以太网采用中继器时,链接的最大长度是2500米,最多通过4个中继器,所以规定对10Mbps以太网一帧的最小发送时间为51.2微秒。这段时间所能传输的数据为512位,所以也称该时间为512位时。这个时间定义为以太网时隙,或冲突时槽。512位=64字节,这就是以太网帧最小64字节的缘由。

512位时是主机捕获信道的时间。若是某主机发送一个帧的64字节仍无冲突,之后也就不会再发生冲突了,称此主机捕获了信道。

因为信道是全部主机共享的,若是数据帧太长就会出现有的主机长时间不能发送数据,并且有的发送数据可能超出接收端的缓冲区大小,形成缓冲溢出。为避免单一主机占用信道时间过长,规定了以太网帧的最大帧长为1500。

100Mbps以太网的时隙仍为512位时,以太网规定一帧的最小发送时间必须为5.12μs。

1000Mbps以太网的时隙增至512字节,即4096位时,4.096μs。

*************************************

MTU的含义: MAC帧内的数据(Payload)字段的最大长度

    咱们使用Ping命令时, -l参数所指定的数据包大小,是指的ICMP报文中的ICMPData字段的长度,不包括ICMPHeader,更不包括IPHeader.

以太网封装IP数据包的最大长度是1500字节,也就是说以太网最大帧长应该是以太网首部加上1500,再加上7字节的前导同步码和1字节的帧开始定界符,具体就是:7字节前导同步吗+1字节帧开始定界符+6字节的目的MAC+6字节的源 MAC+2字节的帧类型+1500+4字节的FCS。

    按 照上述,最大帧应该是1526字节,可是实际上咱们抓包获得的最大帧是1514字节,为何不是1526字节呢?缘由是当数据帧到达网卡时,在物理层上网卡要先去掉前导同步码和帧开始定界符,而后对帧进行CRC检验,若是帧校验和错,就丢弃此帧。若是校验和正确,就判断帧的目的硬件地址是否符合本身的接收条件(目的地址是本身的物理硬件地址、广播地址、可接收的多播硬件地址等),若是符合,就将帧交“设备驱动程序”作进一步处理。这时咱们的抓包软件才能抓 到数据,所以,抓包软件抓到的是去掉前导同步码、帧开始分界符、FCS以外的数据,其最大值是6+6+2+1500=1514。

   以 太网规定,以太网帧数据域部分最小为46字节,也就是以太网帧最小是6+6+2+46+4=64。除去4个字节的FCS,所以,抓包时就是60字节。当数 据字段的长度小于46字节时,MAC子层就会在数据字段的后面填充以知足数据帧长不小于64字节。因为填充数据是由MAC子层负责,也就是设备驱动程序。 不一样的抓包程序和设备驱动程序所处的优先层次可能不一样,抓包程序的优先级可能比设备驱动程序更高,也就是说,咱们的抓包程序可能在设备驱动程序尚未填充不到64字节的帧的时候,抓包程序已经捕获了数据。所以不一样的抓包工具抓到的数据帧的大小可能不一样。下列是本人分别用wireshark和sniffer抓包的结果,对于TCP 的ACK确认帧的大小一个是54字节,一个是60字节,wireshark抓取时没有填充数据段,sniffer抓取时有填充数据段。