📚 第四章 IP协议
📄 一句话概括

多播是「一份数据、多个接收者」,省的不是带宽峰值,是「同样的内容只传一次」。 它映射到 MAC 时只取低 23 位,所以 32 个 IP 共用一个 MAC。

📝 本节和前面几节的关系

4.3.4 讲了广播,这一节讲它的替代品:广播「谁都收」,多播「订阅了才收」。 3.4 的无线、3.2.4 的交换机泛洪,都是「一对多」的老问题。

一、省的是重复传输

三种「一对多」的对比:

方式 发给谁 收的人要不要
单播 一个 —
广播 网段内所有主机 照单全收
多播 订阅了这一组的主机 先加入组

第三行是整个多播的价值所在: 「先加入组」这个动作(IGMP)让网络知道「这条链路上有没有人要」,没人要的链路就不转发。 所以「一路视频发给一千个人」,在源端只发一份,在分叉点才复制。 这就是「省的不是峰值,是重复」。 第二行说明了广播的问题:广播「谁都收」意味着「不关心的人也要花 CPU 处理」。 在多播里,不看的人根本不会收到——所以 IPv6 干脆取消了广播,全部换成多播。

二、28 位挤进 23 位

IPv4 多播地址是 D 类,28 位可用,但映射到 MAC 只用 23 位:

空间 位数 数量
多播 IP 28 位 268435456 个
多播 MAC 23 位 8388608 个
比值 — 正好 32

第三行那个 32 是「丢 5 位」的直接后果:28 减 23 得 5,2 的 5 次方就是 32。 所以「32 个不同的多播 IP 会映射到同一个 MAC」—— 这不是实现错了,是「MAC 地址里留给多播的位不够」。 为什么只留 23 位: 因为多播 MAC 的前缀 01-00-5E 占了 24 位,剩下只有 24 位可用,而其中最高位被规定为 0, 于是只剩 23 位。 所以这个「丢 5 位」是 MAC 地址格式的历史包袱,不是设计者的选择。 后果是「网卡只能粗筛」: 它收到这个 MAC 的帧就交上去,然后由 IP 层再查一次「是不是我要的那一组」。 所以「多播过滤」有两级:网卡一级、协议栈一级——这也是为什么「多播太多会占用 CPU」。

三、多播用在哪儿

三类典型用途:

用途 例子 为什么用多播
服务发现 mDNS、SSDP 不知道对方地址
路由协议 OSPF、RIP 邻居可能很多
媒体分发 IPTV、股票行情 一份内容很多人看

第一行那个「不知道对方地址」很关键:「找打印机」的时候你不知道打印机在哪儿, 所以只能「喊一声」。 但「喊」可以用多播而不用广播——关心这件事的设备才加入那个组,其余设备连处理都不用处理。 第三行是「省带宽」最明显的地方:一套 4K 频道 30 Mbps,一千个用户, 单播要 30 Gbps,多播只要 30 Mbps 加「分叉点的复制」。 所以运营商的内网视频几乎都用多播, 而互联网上用得少——因为「跨域多播」需要每一台路由器都支持,运维太复杂。 这也是「多播为什么没普及」的答案:技术很好,但「要求全网都改」——又是 3.6.5 那条规律。

四、动手 Demo:多播地址,和它被压缩的 MAC

把四个多播 IP 映射成 MAC,再算一下「丢 5 位」的后果。 注意最后一行:32 个 IP 共用一个 MAC。

for grp in ('224.0.0.1', '224.0.0.251', '239.1.1.1', '239.255.0.1'):
    o = [int(x) for x in grp.split('.')]
    low23 = ((o[1] & 0x7F) << 16) | (o[2] << 8) | o[3]
    mac = '01-00-5E-%02X-%02X-%02X' % (
        (low23 >> 16) & 0xFF, (low23 >> 8) & 0xFF, low23 & 0xFF)
    print('%-14s 0x%06X  %s' % (grp, low23, mac))

print('IP 空间 %d,MAC 空间 %d,比值 %d'
      % (2 ** 28, 2 ** 23, 2 ** 28 // 2 ** 23))

实跑输出:

--------------------------------------------------------------------
多播地址,和它被压缩的 MAC
--------------------------------------------------------------------
**多播是「一对多」:一份数据,多个接收者。**
**它省的不是带宽峰值,是「同样的内容只传一次」。**

**D 类占 224.0.0.0/4,共 268435456 个地址**
**其中 224.0.0.0/24 是「本地链路控制」,路由器不转发**——
**所以 224.0.0.1「所有主机」、224.0.0.2「所有路由器」都在这段里。**

**关键的压缩在这儿:IPv4 多播映射到 MAC 时,只取低 23 位。**

  多播 IP         低 23 位     映射到的 MAC
  -----------------------------------------------
  224.0.0.1       0x000001     01-00-5E-00-00-01
  224.0.0.251     0x0000FB     01-00-5E-00-00-FB
  239.1.1.1       0x010101     01-00-5E-01-01-01
  239.255.0.1     0x7F0001     01-00-5E-7F-00-01

**第一行和第二行只差最后一段,MAC 也就只差最后一段。**
**但注意「只取 23 位」这件事的后果**:
**32 个不同的多播 IP 会映射到同一个 MAC**(因为 28 位里丢了 5 位)。
**所以主机收到这个 MAC 的帧之后,还要再查一次 IP**,
**确认「是不是我要的那一组」**——**这一层「过滤」是必须的,否则会收到别人的数据。**
**把这个「丢 5 位」的后果量化一下**:
**多播地址空间 268435456 个(2 的 28 次方),MAC 空间只有 8388608 个(2 的 23 次方)。**
**两者相除正好 32**——**所以每一个多播 MAC,背后对应着 32 个不同的 IP。**
**这意味着「靠 MAC 过滤多播」是做不到的**,**网卡只能做到「粗筛」,细筛必须交给上层。**

o[1] & 0x7F 这个动作就是「丢掉第 5 位」: 第二段的最高位(值为 128 的那一位)被丢掉,所以 239 和 224 开头的地址可能映射到同一个 MAC。 比如 224.1.1.1 和 239.1.1.1 就是同一个 MAC——因为它们只差这一位。 「224.0.0.0/24 不被转发」这条规则很实用: 它是「本地链路控制组」,224.0.0.1 是「本链路上所有主机」、224.0.0.2 是「所有路由器」。 所以「路由器之间的邻居发现」在这段里进行,不需要配置地址、也不需要跨网段。 最后一个实践提醒:多播需要「每一跳都支持」,所以「跨互联网的多播」基本上是不通的。 实际部署里,跨域视频用的是「单播加 CDN」或者「应用层多播」—— 后者是「让服务器帮你在应用层复制」,不依赖网络设备。

30 秒实验

  1. 算一下:224.1.1.1 和 239.1.1.1 映射到同一个 MAC 吗?
  2. 想一下:为什么「跨互联网多播」很难部署?
  3. 想一下:多播过滤为什么要两级(网卡 + 协议栈)?

常见误区

  1. 以为「多播省的是峰值带宽」。 省的是「重复传输」,峰值该多少还是多少。
  2. 以为「多播 IP 一对一映射到 MAC」。 32 个 IP 共用一个 MAC,因为丢了 5 位。
  3. 以为「多播能跨互联网用」。 要每一跳都支持,所以实际很少跨域部署。
自测三问(点开对答案) 1. 多播和广播的区别是什么? —— **广播谁都收,多播只有加入组的人收**,所以不关心的人不花 CPU。 2. 为什么 32 个多播 IP 共用一个 MAC? —— **28 位地址映射到 23 位 MAC,丢了 5 位**,2 的 5 次方就是 32。 3. 多播 MAC 的前缀是什么? —— **01-00-5E**,后面 23 位取多播 IP 的低 23 位。

下一节看子网掩码:把「分界线」写成一个数。

相关笔记