User Datagram Protocol 用户数据报协议
UDP是一个保留消息边界的简单的面向数据包的传输层协议。
不提供:差错纠正、队列管理,重复消除流量控制和拥塞控制
提供:差错检验
简单来讲,就是UPD发送的数据包一个就是一个,又开始有结束(分组除外),用户写一个UDP发一个,服务端收一个。
UDP提供最小功能,因此,如果需要的话其上层需要实现想要的功能。
upd的无连接特征,比其他的传输协议使用更少的开销,广播和组播操作更多的使用udp这样的无连接传输协议。
2 upd头部
报文格式
- 端口号:
帮助协议辨认发送和接受的进程,纯属抽象的,不与主机上任何物理实体相关联。端口号为16bit的无符号整数。
同时,因为IP数据报在IP层根据协议分配给了TCP和UDP,因此不同的协议独立维护一整套端口号,也就是同一台主机,同一时刻可以开启TCP,UDP切指定的端口号一致。
传输层利用端口号辨别数据报需要投送的进程。 - 源端口号
是可选的:发送者不需要接受者回复,设置为0 - 长度
UDP长度字段是,UDP头部和UDP数据的总长度,以字节为单位。但是如果不满足是8的倍数,也不会被填充。最小值为8。
如果不够还不能填充,因此,UDP的长度可以是奇数。
UDP的长度字段是没有必要存在的,因为IP数据报包含数据报总长度,其减去IP数据报头部,就是荷载的有效长度了。
冗余的根本原因在于,upd没有可选部分,也就是udp的头部是固定长度。 - 校验和
发送端可以不校验,但是默认是要校验和。当不校验时其值为0
UDP的校验和
包括如下部分:
这个结构是在计算校验和的时候构造出来了,只是校验和的时候使用,在发送的时候并不会发送这个结构。
和ICMP的校验和相同,UDP的校验和也包含了荷载的数据。
UDP校验和不包括TTL这样,经过路由器的时候就不需要重新计算。
如果通过NAT,改变目的地址,那么就需要重新计算校验和。
在计算校验和的时候,如果数据长度为奇数,那么会填充一个0,但是仅仅是计算的时候填充,发送的时候并不会填充。
在计算的时候多加的12字节,(32*3bit)的伪头部,同样发送出去UDP数据报也不会包括伪头部。
在校验和中加入这个伪头部的意义在于:能够让接受UDP数据报的一端能够确认自己接受到的数据包是否是发给自己的,也就是目的ip地址是自己。(可能是因为UDP数据报头部没什么辨别标志吧)。
这里UDP长度被计算了两次。
计算过程应该是:
short checksum(unsigned short *buf, int nwords)
{
//一个大于16位的变量
unsigned long sum;
//低位开始叠加,每次16bit
for (sum = 0; nwords > 0; nwords--)
sum += *buf++;
//一个大于16位的值,如果左移16位不是0,表明发生进位
while (sum >> 16)
//进位的值都是1,然后再加上原来的sum,但是使用0xffff将高位清空,也就是进位清空
//只剩下低16位的值
sum = (sum >> 16) + (sum & 0xffff);
//返回得是反码
return ~sum;
}
取反码是因为,当再次相加的时候结果就是0
如果接收端在计算校验和时,发现一个校验差错,也就是校验和不一样:
校验和错误的话,接收端会丢弃该数据报,并且没有任何的差错消息。
这个校验码在一定程度上也是多余的,因为在数据链路层也有crc校验。
10.7分片
链路层对每一个帧的最大长度有一个上限(MTU),如果数据报长度超过该值,将会被分片,同时,数据报分片还是能够再次分片的。
分片重组
当数据报被分片了,只有他到达最终目的地时,才会被重组。
原因在于,路由器充足分片会加重路由器负担。且不能保证所有分片都经过同一路由器,因此不可能在路由器重组。
报文
只有第一片带有一个头部,其他的分片使用IP数据报的标识共享一个UDP头部。
标识字段是每一个数据报独有一个,不会重复。
在分片的时候,标识字段被复制为同一个,标志字段指示该片是否是最后一片(0),
分片偏移指示,荷载区数据在源数据报中的偏移量,单位为字节。
当分片产生是,第一片的偏移量为0,荷载为1480(包括头部),第二片偏移量为1480。
只要产生分片,那么除了最后一片,其他的分片大小都是8的倍数。
MF表示是否是最后一片:1表示不是,0表示是最后一片。
问题是,当分片通过NAT时,这个校验和怎么计算?
IP数据报
如果一片丢失,需要重传整个数据报,因此通常要避免分片。
分片传输
顺序
较大偏移量的分片比第一片优先投递,原因在于,较大的偏移量能够通知接收方,缓冲区设置为多大。
但另一方面,只有第一片有端口号。重组以后才会用到,因此是可以的。
超时
每一片分片达到,IP层启动一个计数器,且新的分片到达以后不会被重置。
这样计时器给出同一数据报分片之间被分割的最大时间限度。
当接受第一篇30s后,所有分片仍未到达,那么发送ICMP报文,通知对端超时,数据丢失,同时携带第一片的拷贝。
IP分片与ARP
当数据报分片以后,如果假设需要进行arp建立ip与mac的映射的话,过程是这样的:
某一片发送arp请求报文,如果在1秒内收到,那么其他分片直接发出。
否则,每一片都会发送arp请求,间隔为1s.
10.8数据报最大长度
这和分片的长度不同。
IP数据报最大长度为65535出去IP报头,剩余65527字节,可供UDP使用。
但是UDP的长度字段是一个16bit的只能到30000多。
但是数据报的最大长度仍被其他因素限制:
- 系统本地协议实现
- 接收方可能没准备好去处理这么大的数据
缓存不够?
UDP编程结构允许应用程序指定每次网络操作完成是返回的最大字节数,如果超过盖子结束,就会出现API阶段,也就是超出的数据直接丢弃了。
10.11UDP服务器设计
IP地址与UDP端口号
我们需要拿到发送方的端口号和IP地址,而这需要操作系统以其他的方式告诉进程。
因为发送给进程的数据,已经不在包含IP数据报头部,和UDP数据报头部,只有荷载数据。
也就是通常的accept()
限制本地IP地址
主要使用在一台服务器有多个网卡,多个对外地址时。
UDP服务器通常时期本地IP具有通配符的特点,也就是bind()
时不绑定具体的IP。
这样,所有能够达到的数据报都会被接受。
如果指定了具体的IP,那么只有数据报的目的地址是指定IP的数据报才能被UDP接收,否则直接丢弃。
产生一个ICMP端口不可达。
多地址
多个ip公用一个端口
同一个主机上的多个服务器,可以同时绑定同一个端口。也就是多个服务端程序同时监听一个端口。需要告诉系统允许重用端口
需要指定bind()
时的IP地址,同时SO_REUSEADDR
需要被设置。
这个场景中潜在的条件是:服务器有多个ip,然后着多个ip都绑定了相同的端口。
当同时又指定ip的服务器和统配符的ip,那么指定ip的服务器具有较高的优先级。
限制远端IP
只接受指定IP的主机发送的信息。
同一端口多服务程序监听
默认同一端口对应一个套接字(IP:port),但是仍然可以在同一ip地址和端口上启动多个服务器程序。
需要设置SO_REUSEPORT
,当数据报到达时,系统将一个数据报拷贝多分分别送给不同的服务器程序。
服务器共用套接字
一个ip地址,一个端口,绑定了多个服务器。(可能是多个进程)
同时需要开启SO_REUSEADDR
。
当一个udp数据报到到达服务器的时候,该服务其只有一个进程能够接收到数据报,当服务器上多个进程都在监听同一个套接字,那么只有一个进程能够接收到数据报。
当队列溢出是,超额的数据被丢弃。没有ICMP报文
UDP没有拥塞控制,发送端没有合适的机制进行控制。称为拥塞控制缺失
拥塞控制
UPD服务器大多是迭代的,也就是说只有一个线程在服务有所的udp连接,而系统为每一个UDP端口维持一个有限大小的队列。UPD连接自动排入队列中,接收到UDP数据报以他们达到的顺序被传给应用程序(进程或进程)。
UPD不提供流量控制,当UDP处理队列满时,超额数据包被丢弃,且没有ICMP差错报文,