UDP 是一个很简单的协议,定义它的 rfc768 只有短短三页。作为一个传输层协议,UDP 只在网络层协议如 IPV4/IPV6 上做了一个简单的扩展。
UDP 的 Header 由四个字段组成,Src Port(2 Bytes), Dest Port(2 Bytes), Checksum(2 Bytes), Length(2 Bytes)。相比于 IP 协议,UDP 增加了:
端口机制使得在同一个 IP 地址下,可以同时有多个使用 UDP 的应用互不干扰。Checksum 则为数据提供了简单的错误发现机制。
值得注意的是,Checksum 计算的时候,在 UDP 的 Header 和包内容之前,还额外的包含了 IP 的 Pseudo Header,里面主要包含了 IP Header 中的 Src Addr, Dest Addr, Protocal Number 和 Data Length 等信息。
这是因为包的接收方需要验证包的源地址和包的目标地址,在网络传输的过程中,没有发生损坏,以确保未受到期望之外的数据包。由于上层的协议需要依赖于下层的数据,这是一种违反分层的做法 (Layer Violation)。
IPV4 Header 中,本身就包含了 Checksum,UDP 的 Checksum 尽管被推荐使用,在性能敏感的情况下,用户也可以将 UDP 的 Checksum 设置成 0 来关闭校验。IPV6 的 header 中没有 Checksum,因此使用 IPV6 协议的 UDP 包,必须包括 Checksum。
考虑到在某些场景下,可能只需要对 UDP 内容的一部分数据进行校验,所以产生了 UDP Lite 协议。
UDP lite 协议去掉了多余的 Length 字段(UDP Length 可以通过 IP 的 Length 减去 IP 头的长度算出来),加入了 checksum coverage 字段,来设置需要 Checksum 的数据长度。
UDP 由于无连接的特性,适合需要 Broadcast 或 Multicast 的场景。
在 UDP 的使用中,应额外关注 IP Fragmentation。UDP 没有自动的措施防止 Fragmentation。
网络中各段的 MTU 是不一样的,假设 UDP 的包+IP header 超过了某段链路 MTU,如果 IP 的 DF(Don't Fragment) 字段没有设置,UDP 就可能被分成若干个 IP Fragment 发送。
先到的 Fragment 会被接收方缓存,经过一段超时 (通常为 30s 或者 60s)后,如果全部的 fragment 还没有到达,buffer 中的数据会被完全丢弃,并(可能)向发送方发送一条 ICMP 消息,告知包内容已被丢弃。
如果这些 Fragment 中有任意一个在传输过程中丢失或错误传输,因为 UDP 没有重传和纠错机制,这个 UDP 包会被整体丢弃,影响网络性能。
尽管 UDP 本身支持 2^16 - 1 = 65535 字节大小的长度,但大部分的 UDP 包设置在 1500 Bytes 以下,以避免分片。1500 Bytes 这个边界,是因使用广泛的 Ethernet 限制的。
在 IP 的 DF 字段设置的时候,经过支持 PMTUUD(Path MTU Unit Discovery) 的路由的时候,路由会丢弃超过 MTU 大小的包,并回传一条 ICMP 消息,告诉发送方当前路径的 MTU。
如果发送方接受到这条 ICMP 消息,会根据消息内容记录 MTU,并调整 UDP 包的大小。
UDP 使用简单方便,开销小,适合对性能要求高,数据准确性要求较低的场景使用,如流媒体,视频等。但缺乏重传,Congestion Control,Flow Control 等机制,需要上层应用自己实现。