引言
传输层(TCP/UDP)的可靠性/不可靠性都是由网络层(IP)引起的。UDP的不可靠性,是由IP的不可靠性造成的。而TCP的可靠性,也只是其加入额外的控制,克服了IP的不可靠性。
UDP首部
各16位的源目端口,用于标识程序程序。
16位的UDP长度,U D P长度字段指的是UDP首部和UDP数据的字节长度。UDP长度字段的值最小为8(字节),包括8字节的UDP头部和0字节的数据部分。
UDP检验和覆盖U D P首部和U D P数据。UDP和TCP在首部中都有覆盖它们首部和数据的检验和。UDP的检验和是可选的,而TCP的检验和是必需的。
UDP/TCP与IP头部计算校验和的方法是相同的。但也有区别,即UDP/TCP在计算校验和的时候,需要加上一个伪首部,同时,对于数据部分如果没有对齐到16bit,需要补0以使其对齐。
注意:TCP/IP协议簇中所有的检验和,是简单的16 bit和。它们检测不出交换两个16 bit的差错。因此,无论何种协议(IP、TCP、UDP、ICMP等),即使校验和验证是正确的,也并不能保证数据包没有错误。
IP分片
任何时候I P层接收到一份要发送的 IP数据报时,它要判断向本地哪个接口发送数据(选路),并查询该接口获得其 M T U。 I P把M T U与数据报长度进行比较,如果需要则进行分片。分片可以发生在原始发送端主机上,也可以发生在中间路由器上。
对于TCP来说,MTU是路路径MTU。
对于UDP来说,MTU是本地的MTU。
无论TCP还是UDP,即使在发送时,已按MTU大小来限制,但仍有可能在中途发生分片。这是因为,对于TCP来说,路径MTU并不一定是当前路径的MTU,当前路径的MTU仍可能小于源端发送数据包时的MTU。对于UDP来说,路径中的任何节点的MTU都可能小于发送端的MTU。
把一份 I P数据报分片以后,只有到达目的地才进行重新组装。重新组装由目的端的 I P层来完成,其目的是使分片和重新组装过程对运输层( T C P和U D P)是透明的。已经分片过的数据报有可能会再次进行分片(可能不止一次)。
重新组装的依据(参考IP头部格式):IP首部中的三个字段
16位的标识符(数据报的唯一标识)
3位标识符(1位未用,1位是否分片标志,1位是否还有更多分片标识)
13位偏移片(单位是字节;在分片时,除最后一片外,其他每一片中的数据部分(除 I P首部外的其余部分)必须是 8字节的整数倍。)
在分片时,除最后一片外,其他每一片中的数据部分(除 I P首部外的其余部分)必须是 8
字节的整数倍。
IP分片之后,
只有第一片中有上层协议的头部。这是正常的,因为对于IP层来说,IP分片分的只是数据部分,而上层协议(TCP/UDP)是属于IP数据报的数据部分的。
分片之后,每一个分片中都含有IP的头部,但只有第一个分片中才会包含上层协议的头部。
当发生分片丢失时,即使只丢失一片数据也要重传整个数据报。为什么会发生这种情况呢?因为 I P层本身没有超时重传的机制,需要由更高层来负责超时和重传( T C P有超时和重传机制,但 U D P没有该机制,因此需要使用UDP协议的上层应用来实现重传功能)。
在第一个到达数据报片出现时, I P层必须启动一个定时器。这里“第一个”表示给定数据报的第一个到达数据报片,而不是第一个数据报片(数据报片偏移为 0)。正常的定时器值为 3 0或6 0秒。如果定时器超时而该数据报的所有数据报片未能全部到达,那么将这些数据报片丢弃,避免接收端缓存满。另外,如果最终超时未收到该IP数据报的全部分片,但收到了该IP数据报的第一个分片(即片偏移为0,包含TCP/UDP的头部),则会产生ICMP差错报文给发送端;如果未收到该IP数据报的第一个分片,则不产生ICMP差错报文。(为什么只有在收到第一个IP分片后才会产生ICMP差错报文,请参考: 第三章:IP 网际协议 )
关于TCP分段和IP分片
1. IP分片产生的原因是网络层的MTU;TCP分段产生原因是MSS(最大报文段);
2. IP分片由网络层完成,也在网络层进行重组;TCP分段是在传输层完成,并在传输层进行重组;
3.对于以太网,MSS为1460字节,而MUT往往会大于MSS。
ICMP不可达差错(需要分片)
当一个IP数据报或IP分片,通过一个路由器时,其大小超过了该路由器的MTU,但由于IP首部中又设置了不可分片标识,这时,收到该数据包的路由器将产生一个ICMP差错报文,发送给发送源(即IP数据报中的源地址)。其ICMP需要分片的不可达差错报文格式如下:
编程相关
TCP粘包,分为发送粘包和接收粘包;
发送粘包,是指应用程序一次发送的数据较小,TCPIP协议栈采用了Negal算法优化,先将数据包缓存在socket发送缓冲区中,待数据包足够大时,再一次性发送。
接收粘包,是指应用程序接收端,一次从socket接收缓冲区中读出多个数据包,需要做粘包和半包处理。
UDP不存在粘包问题,是由于UDP发送的时候,不使用Negal算法优化,不会将多个小包合并一次发送出去。另外,在UDP协议的接收端,采用了链式结构来记录每一个到达的UDP包,这样接收端应用程序一次recv只能从socket接收缓冲区中读出发送端发送的一个数据包。也就是说,发送端send了几次,接收端必须recv几次(无论recv时指定了多大的缓冲区)。
UDP服务器端,在绑定本地端口进行监听时,可以限定客户端的IP地址,以此来指定特定的连接。
UDP服务器,一个Socket内核对象需要处理所有的客户端数据。每个 U D P端口都与一个有限大小的输入队列相联系。这意味着,来自不同客户的差不多同时到达的请求将由 U D P自动排队。接收到的 U D P数据报以其接收顺序交给应用程序。排队溢出造成内核中的 U D P模块丢弃数据报的可能性是存在的。因此,接收端需要做足够的能力处理大量的并发。
UDP协议socket内核对象的实现:实际上目前认为,是由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息)。(该段转自:点击打开链接)