bigywater.tech
网络协议 · 31

UDP 广播:SO_BROADCAST 与子网广播地址

广播地址有两种,SO_BROADCAST 这个选项为什么必须打开,本机的套接字绑在哪里才能收到广播?局域网上每秒到底有多少广播?这篇用本机和 en0 上的真实流量一一验证。

EACCES
没有 SO_BROADCAST 时往广播地址发 UDP 的错误:Permission denied
ff:ff:ff:ff:ff:ff
受限广播与定向广播共同的以太网目的 MAC
828 个 / 30 秒
本机所在局域网上的广播帧数(约 99.6 Kbit/s),其中 ARP 271 个、IPv4 UDP 529 个

发送

实测 · 本机执行输出
  受限广播 255.255.255.255:SO_BROADCAST 未打开 → 失败 [Errno 13] Permission denied
  受限广播 255.255.255.255:SO_BROADCAST 打开 → 发送成功(8 字节)
  本网段的定向广播地址:SO_BROADCAST 未打开 → 失败 [Errno 13] Permission denied
  本网段的定向广播地址:SO_BROADCAST 打开 → 发送成功(8 字节)
  getsockopt(SO_BROADCAST) 默认值 0(0 = 关)。对单播地址发包不需要这个选项

广播必须先打开 SO_BROADCAST,否则内核返回 Permission denied;单播不需要。

两种广播

实测 · 本机执行输出
  以太网目的 MAC ff:ff:ff:ff:ff:ff,IP 目的 255.255.255.255(受限广播),源地址是本机,TTL 64,帧长 49 字节(IP 总长 35)
  以太网目的 MAC ff:ff:ff:ff:ff:ff,IP 目的 本网段的定向广播地址,源地址是本机,TTL 64,帧长 50 字节(IP 总长 36)
  route get 255.255.255.255:接口 en0,标志 <UP,HOST,DONE,LLINFO,WASCLONED,BROADCAST,IFSCOPE>
  route get 定向广播地址:接口 en0,标志 <UP,HOST,DONE,LLINFO,WASCLONED,BROADCAST,IFSCOPE>
  两种广播的以太网目的地址都是 ff:ff:ff:ff:ff:ff,同一网段所有设备的网卡都会收下并交给 IP 层;区别在 IP 目的:受限广播永远不会被路由器转发,定向广播理论上可以被转发到目标子网(现在的路由器默认都不转发)

255.255.255.255 是受限广播,不会离开本网段;子网最后一个地址是定向广播。两者在以太网上都是 ff:ff:ff:ff:ff:ff。

接收

实测 · 本机执行输出
  发 定向广播:0.0.0.0:收到;本机 en0 地址:没收到;定向广播地址:收到;127.0.0.1:没收到;255.255.255.255:绑定失败(Can't assign requested address)
  发 受限广播:0.0.0.0:收到;本机 en0 地址:没收到;定向广播地址:没收到;127.0.0.1:没收到;255.255.255.255:绑定失败(Can't assign requested address)
  也就是说广播只会交给绑定了通配地址(或恰好是那个广播地址)的套接字;绑在本机单播地址或 127.0.0.1 上的收不到

只有绑定了通配地址 0.0.0.0(或恰好是那个广播地址)的套接字才能收到广播;绑在本机单播地址 / 127.0.0.1 上的收不到。

代价

实测 · 本机执行输出
  30 秒共 828 个广播帧(373406 字节,平均 27.6 帧/秒、99.6 Kbit/s):IPv4 UDP 529;ARP 271;其它 0x9998 15;其它 0x2589 8;其它 0x002e 4;其它 0x3689 1
  IPv4 UDP 广播的目的端口:5353(mDNS) 384;137(NetBIOS 名字服务) 84;12476(?) 22;5684(?) 17;51007(?) 9;138(NetBIOS 数据报) 3;68(DHCP 客户端端口) 3;161(?) 2
  每个广播帧都会打断同一网段里每一台设备的网卡 / 内核,即使它们不关心——这就是广播的代价,组播(下一篇)让不关心的设备可以在网卡层就丢掉

局域网 30 秒有 828 个广播帧,每一台设备都要处理;这是下一篇组播要解决的问题。

分步演示

UDP 广播
本步的对象

    本文用到的工具与边界

    • 本机 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 / 回环上做(广播发到端口 33435 或随机端口,没有人监听);第四节被动抓 30 秒,别的设备的帧只统计个数,不写任何地址;抓到的“其它”类型是以太网类型字段不是 IPv4 / ARP / IPv6 的帧,没有进一步解析
    • 没有验证:定向广播是否会被路由器转发(假设现在默认都不转发);广播风暴(环路)的触发与抑制;Wi-Fi 接入点如何把组播变成广播发送(5353 的帧是广播 MAC,但没有统计它们的 IP 目的地址);IPv6 没有广播,用组播代替(见 NDP 一篇);IP_BOUND_IF 对多网卡机器上受限广播的影响
    京ICP备2026067502号-1京公网安备11010502063487号