bigywater.tech
网络协议 · 8

单播、广播、组播:同一个包到底发给了谁

同一个包发给“一个人”“网段所有人”“一组人”,在网线上有什么不同?目的 MAC 怎么由 IP 地址决定?谁会收到?广播 ping 为什么会放大流量?这篇用抓包和一组本机套接字实验一一验证。

01:00:5e + 低 23 位
IPv4 组播地址映射到以太网组播 MAC 的规则
2 → 52
ping 子网广播地址:2 个请求换来的应答数(含 50 个重复)
57 帧/秒
这个网段上广播加组播的帧速率(30 秒 1715 个)

三种目的

实测 · 本机执行输出
  目的                          以太网目的 MAC               MAC 类型  IP 目的地址           
  单播(发给默认网关)                70:85:6c:xx:xx:xx       单播      <网关>              帧长 50
  子网定向广播(网段最后一个地址)          ff:ff:ff:ff:ff:ff       广播      <网段广播>            帧长 50
  受限广播 255.255.255.255      ff:ff:ff:ff:ff:ff       广播      255.255.255.255   帧长 50
  组播 239.1.2.3              01:00:5e:xx:xx:xx       组播      239.1.2.3         帧长 50
  组播 239.1.2.3 的 MAC = 01:00:5e + IP 的低 23 位 = 01:00:5e:01:02:03:239.1.2.3 低 23 位是 0x010203 → 01:02:03;注意高位被丢掉,所以 239.129.2.3 也映射到同一个 MAC

目的 IP 决定目的 MAC:单播用下一跳的 MAC,广播用 ff:ff:ff:ff:ff:ff,组播用 01:00:5e 加 IP 低 23 位。

谁收到

实测 · 本机执行输出
  发 3 个数据报到 单播 127.0.0.1:接收者 A、B 各收到 [3, 0] 个
  发 3 个数据报到 受限广播 255.255.255.255:接收者 A、B 各收到 [3, 3] 个
  发 3 个数据报到 组播 224.9.9.9(两个都加入了组):接收者 A、B 各收到 [3, 3] 个
  单播只会交给其中一个套接字;广播和组播会交给所有符合条件的套接字——这是它们和单播的本质区别

单播只交给一个套接字,广播交给所有套接字,组播交给所有加入了该组的套接字。

广播 ping

实测 · 本机执行输出
  sysctl net.inet.icmp.bmcastecho = 1(本机是 1 = 会回应目的地址为广播 / 组播的 ICMP echo;0 = 不回应)
  ping 输出末几行: --- <网段广播> ping statistics --- | 2 packets transmitted, 2 packets received, +50 duplicates, 0.0% packet loss | round-trip min/avg/max/stddev = 0.165/127.667/1103.209/150.887 ms
  抓到发往网段广播地址的 ICMP 请求帧 2 个,其中目的 MAC 是 ff:ff:ff:ff:ff:ff 的 2 个——网络层的“广播地址”对应链路层的“广播 MAC”
  路由器不转发子网定向广播 / 受限广播:受限广播 255.255.255.255 的 TTL 虽然是 64,但它只在本链路上有效,第一台路由器就会丢掉它(路由器的行为本机无法实验,这是规范 / 经验,不是实测)

目的地址是子网广播地址的 ping 会让网段里所有愿意回应的主机都回一个应答,2 个请求得到了 52 个应答,这是放大攻击的原理。

广播的代价

实测 · 本机执行输出
  单播:6406 个帧,共 4849737 字节(78.9% 的帧)
  广播:789 个帧,共 417779 字节(9.7% 的帧)
  组播:926 个帧,共 512821 字节(11.4% 的帧)
  广播 + 组播 1715 个帧 ≈ 每秒 57.2 个——网段里每台主机的网卡都要收下并交给 CPU 看一眼;这就是为什么广播域不能无限大

每个广播和组播帧都会被网段里每台主机的网卡收下,所以广播域越大,无关流量越多。

分步演示

单播广播组播
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第一节在 en0 上抓包;第二节用本机 127.0.0.1 与 255.255.255.255 的套接字做实验,组播接口选 lo0;第三节会向本网段发一次广播 ping(只发了 3 个,实际只发出 2 个请求就收到大量应答);第四节统计整个网段 30 秒的帧,对别人的流量只做数量统计;MAC 只保留前 3 字节
    • 没有验证:路由器是否转发广播(规范是不转发,没实测);IGMP 如何管理组播成员(见 IGMP 两篇);交换机的组播泛洪 / IGMP snooping;IPv6 没有广播、用组播替代(见 NDP 一篇);广播风暴;Wi-Fi 下广播以最低速率发送带来的影响
    京ICP备2026067502号-1京公网安备11010502063487号