以太网帧(Ethernet)、IP包、TCP、UDP的长度范围

时间:2024-03-09 21:38:49
http://kibazen.cn/2015/ethernet-ip-tcp-udp-packet-length/

一、它们之间的关系

TCP/IP协议是分层的。以太网帧的数据帧在链路层,IP包在网络层,TCP/UDP在传输层,TCP/UDP中的数据在应用层。

应用层 TCP/UDP中的数据
传输层 TCP/UDP
网络层 IP包
数据链路层 以太网帧
物理层  

上一层的数据连同该层的控制信息打包交给下一层,直到最底层。所以他们之间的关系:以太网帧{IP包{TCP/UDP{数据}}}。

二、以太网帧(数据链路层)

目前以太网帧有5种,交换机之间BPDU(桥协议数据单元)数据包使用的是IEEE802.3/LLC帧,其格式如下:

字段 长度(字节) 目的
前导码(Preamble) 7 0x55,一串1、0间隔,用于信号同步
帧开始符(SFD) 1 1字节0xD5(10101011),表示一帧开始
目的MAC地址 6 指明帧的接受者
源MAC地址 6 指明帧的发送者
长度(Length)/类型(Type) 2 0~1500保留为长度域值,1536~65535保留为类型域值(0x0600~0xFFFF)
数据和填充(Data and Pad) 46~1500 高层的数据,通常为3层协议数据单元。对于TCP/IP是IP数据包(注:如果帧长小于64字节,则要求“填充”,以使这个帧的长度达到64字节)
帧校验序列(FCS) 4 使用CRC计算从目的MAC到数据域这部分内容而得到的校验和

 

 

据RFC894的说明,以太网封装IP数据包的最大长度是1500字节(所以,数据链路层的最大传输单元(Maximum Transmission Unit,MTU)是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抓到的就有填充数据段)

而为什么以太网帧最后最小的值是64字节呢?考虑如下的情况,主机发送的帧很小,而两台冲突主机相距很远。在主机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。

三、IP包(网络层)

IP头大小最小为20字节。所以,网络层的MTU=数据链路层的MTU1500-20=1480字节。

 

 

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

由于IP协议提供为上层协议分割和重组报文的功能,因此传输层协议的数据包长度原则上来说没有限制。实际上限制还是有的,因为IP包的标识字段终究不可能无限长,按照IPv4,好像上限应该是4G(64K*64K)。

四、TCP(传输层)

依靠IP协议提供的报文分割和重组机制,TCP包头中就没有“包长度”字段,而完全依靠IP层去处理分帧。这就是为什么TCP常常被称作一种“流协议”的原因,开发者在使用TCP服务的时候,不必去关心数据包的大小,只需讲SOCKET看作一条数据流的入口,往里面放数据就是了,TCP协议本身会进行拥塞/流量控制。

 

五、UDP(传输层)

UDP则与TCP不同,UDP包头内有总长度字段,同样为两个字节,因此UDP数据包的总长度被限制为65535,这样恰好可以放进一个IP包内,使得 UDP/IP协议栈的实现非常简单和高效。

 

所以UDP包的最大值是:IP数据包的最大长度65535-IP头的大小20-UDP头的大小=65507字节。最小值是0。

这个值也就是你在调用getsockopt()时指定SO_MAX_MSG_SIZE所得到返回值,任何使用SOCK_DGRAM属性的socket,一次send的 数据都不能超过这个值,否则必然得到一个错误。

———————————————————————————
在应用程序中我们用到的Data的长度最大是多少,直接取决于底层的限制。
我们从下到上分析一下:
1.在链路层,由以太网的物理特性决定了数据帧的长度为(46+18)-(1500+18),其中的18是数据帧的头和尾,也就是说数据帧的内容最大为1500(不包括帧头和帧尾),即MTU(Maximum Transmission Unit)为1500;
2.在网络层,因为IP包的首部要占用20字节,所以这的MTU为1500-20=1480;
3.在传输层:

UDP包的首部要占用8字节,所以UDP的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字节以内。

TCP包的首部要占用20字节,所以TCP的MTU为1480-20=1460;

4.如果用户使用了PPPoE协议,还应减去PPPoE的首部占用需要的8字节。即TCP的MTU应该是1452;UDP的MTU应该是1464;

1 const unsigned long kMinEthernetFrameSize = 64;
2 const unsigned long kMaxEthernetFrameSize = 1518;
3 const unsigned long kEthernetFrameHeadSize = 6 + 6 + 2;
4 const unsigned long kEthernetFrameTailSize = 4;
5 const unsigned long kEthernetMTU = kMaxEthernetFrameSize - kEthernetFrameHeadSize - kEthernetFrameTailSize;
6 const unsigned long kPPPoEHeadSize = 8;
7 const unsigned long kIPHeadSize = 20;
8 const unsigned long kTCPHeadSize = 20;
9 const unsigned long kUDPHeadSize = 8;
10 const unsigned long kTCPMSS = kEthernetMTU - kPPPoEHeadSize - kIPHeadSize - kTCPHeadSize;
11 const unsigned long kUDPMSS = kEthernetMTU - kPPPoEHeadSize - kIPHeadSize - kUDPHeadSize;

———————————————————————————

 

参考:

http://blog.csdn.net/naturebe/article/details/6712153

http://blog.csdn.net/ixidof/article/details/7669422

https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html