1 minute read

0. 前言

UDP 单独拿出来讲,其实内容不算特别多,所以这一篇我干脆把 IP 的一些基础内容也放进来一起看。

对于一个 iOS 开发者来说,IP 协议已经算是非常底层了,不一定需要背得特别细,但至少要知道:

  1. UDP 到底比 TCP 少了什么;
  2. UDPIP 之上到底只加了哪些最基本的信息;
  3. IP 层又负责哪些更底层的事情。

1. UDP 是何物?

UDP(User Datagram Protocol,用户数据报协议)是一个传输层协议,在 RFC 768 中定义。

它提供的是一种按消息为单位的、不可靠的传输服务。也就是说:

  • 它能把一份完整消息发出去;
  • 但它不保证一定送达;
  • 也不保证按顺序送达;
  • 更不会像 TCP 那样帮你重传、排队、去重。

除了复用功能以及少量差错检测之外,它几乎没有在 IP 之上增加太多东西。所以很多时候,使用 UDP 的感觉,其实已经差不多是在直接和 IP 打交道了。

这里顺便说一下“数据报”这个词:

  • 数据报(Datagram)
    • 一个完整、独立的数据实体;
    • 自己携带从源到目的地所需的基本信息;
    • 不依赖之前或之后的数据交换状态。

2. UDP 的特点

UDP 的特点,不在于它额外引入了多少复杂机制,而恰恰在于它主动省略了很多 TCP 拥有的能力。

  • 无需建立连接
    • TCP 在发送数据前需要三次握手;
    • UDP 则可以直接发。
  • 无连接状态
    • 不需要长期维护连接状态;
    • 更适合一些短消息、流媒体、语音、游戏这类对时延更敏感的场景。
  • 首部开销小
    • UDP 首部很短;
    • 协议处理逻辑也更简单。
  • 时延更低
    • 没有握手带来的启动成本;
    • 也没有 TCP 那种重传、拥塞控制、按序交付的额外等待。

UDP 在 IP 之上只加了什么

这个要从 IP 协议说起。

IP 协议的主要任务,是按照地址把数据报从源主机送到目标主机。为此,消息会先被封装进一个 IP 分组,里面至少要写清楚源地址、目标地址,以及一些路由和分片相关的控制信息。

UDPIP 之上做的事情其实很少:它只额外增加了一层很薄的首部,核心就是下面这几个字段:

字段 作用
源端口 发送方进程的端口号。IPv4 中可以为 0,表示未使用。
目的端口 接收方进程的端口号。
长度 UDP 首部 + UDP 数据的总长度。
校验和 用于校验 UDP 首部、数据以及伪首部。

这几个字段的作用分别可以理解成:

  • 源端口
    • 发送方进程的端口号;
    • 某些场景下接收方可以根据它回消息;
    • 但它并不是绝对必须的。
  • 目的端口
    • 数据最终要交给目标主机上的哪个进程;
    • 这是 UDP 复用能力里最关键的字段。
  • 长度
    • 表示整个 UDP 报文的大小;
    • 包括 UDP 首部和数据本身。
  • 校验和
    • 用于校验 UDP 首部、数据,以及伪首部中的相关信息;
    • 这是端到端意义上的校验,和 IP 头部自己的校验不是一回事。

这里需要特别强调一个很容易说错的点:

  • IPv4 头部确实自带校验和;
  • 但这个校验和只覆盖 IP 头部本身
  • 它并不能替代 UDP 对自身首部和数据的校验。

所以不能简单说“IP 自己有校验码,UDP 可以直接用”。这是两层不同的事情。

另外再补一句:

  • 在 IPv4 里,UDP 校验和可以为 0,表示发送方没有计算;
  • 但在 IPv6 里,UDP 校验和一般不能省略。

UDP 的消息边界

TCP 是字节流协议,它不保留应用层消息边界;而 UDP 不一样,它保留的是报文边界

也就是说:

  • 应用层发出去的一次 sendto,对接收方来说就是一份完整消息;
  • 接收方要么拿到完整的一份 UDP 报文;
  • 要么这份报文直接丢失;
  • 不会像 TCP 那样把一条消息拆成多个有序片段再交给你重组。

但这里也要注意一个容易误解的点:

UDP 保留消息边界,不等于 “UDP 数据报在网络层绝对不会被分片”。

实际上:

  • UDP 本身不负责分段、重传、排序和重组;
  • 但它承载的数据在经过 IP 层传输时,仍然可能被 IP 层分片
  • 只是对应用层来说,最终交付单位依然是一份完整 UDP 报文,而不是一个字节流。

3. IP

IP(Internet Protocol,网际协议)是网络层协议。它最核心的任务很简单:

  • 给数据报寻址;
  • 决定数据往哪里走;
  • 尽力把它送到目标主机。

IP 是无连接的

IP面向无连接的。

也就是说,在发送数据之前,它不需要像 TCP 那样先和目标建立连接。它看到的只是一个个独立的分组,然后尽力转发。

这也决定了 IP 本身的风格:

  • 不保证可靠送达;
  • 不保证按序;
  • 不保证不重复;
  • 更不关心“这是第几次通信”。

这些事情,如果上层需要,就交给 TCP 这种协议自己去补。

4. IPv4 & IPv6

IPv4 地址长度是 32 位;随着互联网规模越来越大,地址逐渐不够用了,于是就有了 IPv6

IPv6 地址长度是 128 位,也就是 IPv4 的 4 倍。

这两者除了地址长度不一样之外,首部设计思路也有变化。简单对比如下:

项目 IPv4 IPv6
地址长度 32 位 128 位
首部长度 可变,最少 20 字节 固定 40 字节
头部校验和 没有
分片方式 路由器和主机都可能参与 主要由源主机负责,使用扩展头
扩展能力 依赖选项字段 使用扩展头机制

如果只从学习角度记,我觉得最值得记住的是这几件事:

  1. IPv6 不只是“地址更长”;
  2. 它也顺手把很多旧设计重新整理了,比如首部固定长度、去掉头部校验和、改进扩展能力;
  3. 从现代网络演进方向来看,IPv6 显然更适合继续扩展。

这里就不继续把每个字段都展开了,有兴趣的话可以继续翻《图解 TCP/IP》或者直接看相关 RFC。

5. 小结

如果把这一篇压缩成几句话,我觉得可以这么记:

  • UDP 是一个非常薄的传输层协议;
  • 它在 IP 之上只增加了最基本的端口、长度和校验能力;
  • 它保留消息边界,但不提供 TCP 那种可靠、有序、可重传的字节流语义;
  • IP 则更底层,只负责寻址和转发,不保证可靠性;
  • IPv6 不只是地址变长,而是对整个 IP 首部设计做了一次更现代化的整理。

资料

RFC 768

《图解 TCP/IP》