多播是「一份数据、多个接收者」,省的不是带宽峰值,是「同样的内容只传一次」。 它映射到 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 秒实验
- 算一下:224.1.1.1 和 239.1.1.1 映射到同一个 MAC 吗?
- 想一下:为什么「跨互联网多播」很难部署?
- 想一下:多播过滤为什么要两级(网卡 + 协议栈)?
常见误区
- 以为「多播省的是峰值带宽」。 省的是「重复传输」,峰值该多少还是多少。
- 以为「多播 IP 一对一映射到 MAC」。 32 个 IP 共用一个 MAC,因为丢了 5 位。
- 以为「多播能跨互联网用」。 要每一跳都支持,所以实际很少跨域部署。
自测三问(点开对答案)
1. 多播和广播的区别是什么? —— **广播谁都收,多播只有加入组的人收**,所以不关心的人不花 CPU。 2. 为什么 32 个多播 IP 共用一个 MAC? —— **28 位地址映射到 23 位 MAC,丢了 5 位**,2 的 5 次方就是 32。 3. 多播 MAC 的前缀是什么? —— **01-00-5E**,后面 23 位取多播 IP 的低 23 位。下一节看子网掩码:把「分界线」写成一个数。
- ☐ (本节完,下一节 4.3.6 子网掩码)