单播问「你」,组播问「关心这件事的各位」,广播问「全网段所有人」。 ONVIF 选组播的理由只有一个:广播出不了网段,而单播要先知道地址。
上一节讲完 WS-Discovery 的机制,这一节把三种寻址方式摆在一起比。 这是网络基础,但放在 ONVIF 上下文里比较才有意义—— 因为这里的选择直接决定了「能不能跨网段发现」。
一、三种寻址方式
| 方式 | 地址 | 一对几 | 跨网段 |
|---|---|---|---|
| 单播 | 对方 IP | 一对一 | 能 |
| 组播 | 239.255.255.250 |
一对多,只给加入组的 | 要路由器支持 |
| 广播 | 255.255.255.255 |
一对全网段 | 不能 |
第四列是 ONVIF 选组播的全部理由: 广播出不了网段——一栋楼三层,每层一个 VLAN,广播就废了。 而组播在跨网段时依赖「组播路由」(IGMP + PIM), 这是企业网里常见的配置项—— 所以「发现不了设备」这个故障,很多时候不在设备上,在交换机的 IGMP 配置上。 第一列那个「单播能跨网段」也是有条件的:要能路由到对方,而「不知道地址」就无从谈起。
二、组播地址是规定好的
239.255.255.250 与端口 3702 是 WS-Discovery 定死的,不是 ONVIF 自己选的——
所以理论上「任何支持 WS-Discovery 的设备」都在这个地址上喊话,
包括打印机、扫描仪、智能电视。
| 项 | 值 | 能不能改 |
|---|---|---|
| 组播地址 | 239.255.255.250 |
不能,规范定死 |
| 端口 | 3702 |
不能 |
| 本机绑定的地址 | 0.0.0.0(所有网卡) |
能,但没必要 |
第三行有个实际问题:一台机器有两个网卡(有线 + 无线)时,
0.0.0.0 会让它从两个网卡都发一遍,
于是同一台设备可能被「发现两次」——
所以客户端要按 EndpointReference(UUID)去重,不能按 IP 去重。
这个坑在笔记本上调试时特别常见:
插着网线又开着 WiFi,搜出来一堆重复设备,还以为设备有问题。
三、本机实测三种
这一节的值在于「真跑一遍」,因为三种方式在不同系统上表现差别很大:
| 方式 | 本机结果 | 说明 |
|---|---|---|
| 单播 | 通 | 回环上的单播,正常 |
| 组播 | 通 | 绑 3702 + join + 自发自收,都成功 |
| 广播 | 通 | 只能说「没被拦」,没有对象回 |
第二行那个「通」要解释清楚:我既发了 Probe、又绑了 3702 并加入了组播组, 所以收到的那条其实是自己发出去的——这证明「组播路径通」,不证明「设备会应答」。 两件事必须分开说,不然很容易把「能发包」当成「能发现设备」。 第三行那个「通」更弱:广播没有回执,「发出去了」只说明系统没拦, 所以「用广播搜索设备」在工程上从来不是可靠手段。
四、什么时候真要考虑组播
日常用不上,但两种场景必须考虑:
| 场景 | 为什么必须组播 | 注意什么 |
|---|---|---|
| 大规模部署 | 几百台设备,单播要逐个填 IP | 组播路由要配 |
| 跨网段发现 | 广播出不去 | 交换机要开 IGMP snooping |
| 实时流分发 | 一人推、多人看省带宽 | 这是 RTSP 的组播,不是发现 |
第一行是「发现」的用途,第三行是「取流」的用途——两者都叫组播,但是两件事:
发现是一次性的 UDP 报文,取流是持续的 RTP 流。
混起来讲是很多资料的毛病,
结果读者以为「组播发现」和「组播取流」是同一套配置——
其实前者的地址是定死的,后者由 GetStreamUri 里的 RTP-Multicast 决定。
第二行那个 IGMP snooping 值得单独记一句:
不开它,组播会在交换机里泛洪成广播——表现是「一开组播,整个网段都卡」。
五、动手 Demo:单播、组播、广播,本机各通不通
Demo 三种都试一遍,成功失败都如实打出来。 重点是看「组播」那一行到底通不通——这台机器的结果不能代表所有机器,所以必须实测。
# ① 单播:往回环上某个端口发
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(b"x", ("127.0.0.1", 9)); s.close()
# ② 组播:绑 3702、join 239.255.255.250、自发自收
print(D.try_multicast())
# ③ 广播:开 SO_BROADCAST 再发
print(D.try_broadcast())
实跑输出:
--------------------------------------------------------------------
单播、组播、广播,本机各通不通
--------------------------------------------------------------------
**这三种地址不是「随便挑一个」,各有各的适用面**:
方式 地址 一对几 要不要路由器配合
-----------------------------------------------------------------------------------
单播 对方 IP 一对一 不要
**组播** `239.255.255.250` **一对多,只发给加入组的** **要**
广播 `255.255.255.255` 一对全网段 **不跨网段**
**ONVIF 选组播的理由在第三、四列**:
**广播出不了网段,而摄像机可能在任何网段**;
**单播要先知道地址,可「发现」这件事本身就是为了找地址**——
**只剩组播:不知道地址也能问到,且只吵到「关心这件事的机器」。**
**我把三种都在本机试了一遍,结果如下**:
方式 结果 实测细节
---------------------------------------------------------------------------
单播 **通** 能发(回环上的单播)
组播 **通** 绑 3702 成功、join 成功、发出 630 字节,收到自己的回环
广播 **通** 广播发出去了(没有对象回,所以只能说「没被拦」)
**第二行那句「收到自己的回环」要解释一下,不然会误解**:
**我既发了 Probe 又绑了 3702 并加入了组播组**,
**所以收到的那条其实是自己发出去的**——
**这证明「组播路径是通的」,不证明「设备会应答」。**
**两件事必须分开说**,不然很容易把「能发包」当成「能发现设备」。
**第三行也有个坑**:广播「发出去了」只说明系统没拦,
**不代表有人收到**——**广播没有回执**,
**所以「广播搜索设备」在工程上从来不是可靠手段。**
第二段那行是最有信息量的:绑 3702 成功、join 成功、发出 630 字节,收到自己的回环。
三件事都成功,但结论要保守:「收到的是自己发的」这一点必须强调,
否则读者会以为「组播发现已经通了」。
第三段的输出是「广播发出去了(没有对象回)」,这句措辞是刻意的:
广播没有回执,所以只能说「没被拦」。
如果哪天要在这台机器上做真的组播测试,
得有两台机器、同一个网段、都没开防火墙拦 UDP 3702——这三条缺一条,测试结果就没意义。
最后那句「工程上从来不用广播搜索设备」是有历史原因的:早期的设备搜索工具确实用广播,
后来因为跨不了网段、又容易被防火墙拦,才统一到 WS-Discovery。
30 秒实验
- 把
try_multicast()的seconds改成 3,看结果会不会变。 - 在
try_broadcast()里加一个 socket 收包,看能不能收到自己发的广播。 - 查一下你所在网段的交换机有没有开 IGMP snooping,这决定了组播能不能跨网段。
常见误区
- 以为「组播能发出去」就等于「能发现设备」。 收到自己发的包只证明路径通, 不证明有设备在听。
- 用广播搜索设备。 广播出不了网段,而且没有回执,工程上从来不是可靠手段。
- 把「组播发现」和「组播取流」当成一件事。 前者的地址是定死的
239.255.255.250, 后者由RTP-Multicast决定。
自测三问(点开对答案)
1. ONVIF 为什么选组播而不是广播? —— **因为广播出不了网段**,而摄像机可能在任何网段。组播靠组播路由能跨,广播不能。 2. 本机测出「组播通」,能得出什么结论? —— **只能得出「组播路径通」**——因为收到的是自己发的包。**不能推出「设备会应答」。** 3. 一台机器有线和无线同时开,为什么设备会被发现两次? —— **因为组播绑的是 `0.0.0.0`,两个网卡各发一遍。**客户端要按 UUID 去重,不能按 IP。下一节把发现和信息获取串起来:从一句「谁在」到拿到厂家型号。
- ☐ (本节完,下一节 08.02.03 设备搜索)