WS-Discovery 干的事:往组播地址喊一句「谁在?」,设备各自回一句「我在,这是我的地址」。 一条 582 字节的 Probe 里,真正带信息的只有 Types 那 28 个字符。
上一节讲的是「知道地址之后怎么问」,这一节讲「地址是怎么来的」。 没有发现机制,每一台设备都要手填 IP——这正是 ONVIF 要终结的局面之一(07.01.01)。
一、四个固定参数
| 项 | 值 | 说明 |
|---|---|---|
| 传输 | UDP | 不是 TCP,发完不等回执 |
| 组播地址 | 239.255.255.250 | IPv4 的本地链路组播段 |
| 端口 | 3702 | WS-Discovery 的固定端口 |
| 格式 | SOAP over UDP | 和 HTTP 那套是同一个 SOAP |
第四行是理解它的关键:
WS-Discovery 不是另一套协议,是「把 SOAP 塞进 UDP」——
没有 HTTP 头,没有 Content-Length,一个 UDP 包里就是一个完整信封。
所以它的大小必须控制在一个 MTU 内(一般 1400 字节左右),
否则会被 IP 分片,而分片一丢就是整包作废。
这也是为什么 Probe 报文里没有任何多余字段——它不是不想写全,是空间有限。
二、Probe:582 字节里只有 28 个字符有用
一条 Probe 报文的四段,前三段是「信封」,第四段才是「内容」:
| 段 | 内容 | 作用 |
|---|---|---|
| Header | MessageID |
这条消息的编号,回包要带它 |
| Header | To |
固定的 discovery URI |
| Header | Action |
说明这是「寻找」 |
| Body | d:Types |
dn:NetworkVideoTransmitter(找摄像机) |
实测总长 582 字节,而 Types 只有 28 个字符。
其余全是命名空间和固定 URI——
这和 07.01.02 的结论完全一致:ONVIF 的成本在「格式」,不在「内容」。
第四行那个类型是「找什么」的开关:
NetworkVideoTransmitter 是网络视频发送端(NVT),也就是摄像机;
换成 NetworkVideoDisplay 就找解码器,NetworkVideoStorage 就找 NVR。
所以「发现不到摄像头」有时是类型写错了,不是网络问题——这一条能省掉很多抓包的时间。
三、ProbeMatch:四个字段,最后一个是目的
设备收到 Probe 之后回一个 ProbeMatch,带四样东西:
| 字段 | 本例的值 | 用途 |
|---|---|---|
RelatesTo |
uuid:1234… |
对上那条 Probe |
EndpointReference |
urn:uuid:ipc-2000-0001 |
设备唯一标识 |
Scopes |
onvif://…/type/video_encoder |
自报类型和名字 |
XAddrs |
http://192.168.1.64/onvif/device_service |
关键:后续请求发这里 |
第四行是发现的全部目的:拿到这个地址,后面的 SOAP 请求才有地方发。
前三个字段都是为了「确认这条回包是给你的、以及它是谁」。
第一行的 RelatesTo 是配对机制:
设备可能同时收到多台客户端的 Probe,靠这个字段区分回给谁。
所以「MessageID 随便填」是可以的,但「不能不填」——
不填的话设备的 RelatesTo 没法对上,客户端只能靠猜。
第三行的 Scopes 值得单独说:它是「自报家门」用的,里面有设备名、厂商、类型。
很多客户端靠它做「按名字筛选」,比逐个连上去问 GetDeviceInformation 快得多。
四、本机能验什么,不能验什么
这一节的诚实边界在这里,必须写清:
| 项 | 本机 | 原因 |
|---|---|---|
| 报文格式 | 能 | 上面就是真构造、真解析的 |
| 组播可达性 | 不能 | 沙箱通常禁组播,也没有真设备 |
| 发现耗时 | 不能 | 没设备可发现,测不出 |
「报文该长什么样」我给了实物,「能不能发现设备」我没测。 这两件事经常被混为一谈——能拼出报文,不代表网络里能收到回包, 中间隔着组播、防火墙、交换机 IGMP 三道关。 所以现场「发现不到设备」的排查顺序应该是:先手填 IP 看能不能连通(排除网络), 再用抓包看有没有 ProbeMatch(排除客户端解析), 最后才怀疑设备没实现发现(Profile S 要求它必须有)。 这个顺序是「从确定到不确定」,反过来查会很慢。
五、动手 Demo:设备发现,一条 UDP 报文换一个地址
Demo 不依赖网络:构造一条真的 Probe 报文,量它的字节,再把它解回来。 最后构造一条 ProbeMatch,看「回包」里那四个字段各占多少。 组播发不出去,这一点在正文里已经写清了。
# 一条 Probe:信封 + 三个头 + 一个 Types
probe = ('<?xml version="1.0" encoding="UTF-8"?>'
'<e:Envelope'
' xmlns:e="http://www.w3.org/2003/05/soap-envelope"'
' xmlns:w="http://schemas.xmlsoap.org/ws/2004/08/addressing"'
' xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery"'
' xmlns:dn="http://www.onvif.org/ver10/network/wsdl">'
"<e:Header>"
"<w:MessageID>uuid:%s</w:MessageID>"
"<w:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</w:To>"
"<w:Action>.../discovery/Probe</w:Action>"
"</e:Header>"
"<e:Body><d:Probe>"
"<d:Types>dn:NetworkVideoTransmitter</d:Types>"
"</d:Probe></e:Body></e:Envelope>")
print(len(probe.encode("utf-8"))) # 582
r = ET.fromstring(probe)
D = "{http://schemas.xmlsoap.org/ws/2005/04/discovery}"
typ = r.find(".//" + D + "Types")
print(typ.text, len(typ.text)) # dn:...VerTransmitter 28
实跑输出:
--------------------------------------------------------------------
设备发现,一条 UDP 报文换一个地址
--------------------------------------------------------------------
**WS-Discovery 干的事:往组播地址喊一句「谁在?」,设备各自回一句「我在,这是我的地址」。**
**本机没有真设备、也不一定能发组播**,所以这一节只做报文本身:**构造、量字节、解析。**
**先看它的几个固定参数:**
项 值 说明
--------------------------------------------------------------------------------
传输 **UDP** 不是 TCP,发完不等回执
组播地址 **239.255.255.250** IPv4 的本地链路组播段
端口 **3702** WS-Discovery 的固定端口
格式 **SOAP over UDP** **和 HTTP 那套是同一个 SOAP**
**第四行是理解它的关键**:
**WS-Discovery 不是「另一套协议」,是「把 SOAP 塞进 UDP」**——
**没有 HTTP 头,没有 `Content-Length`,一个 UDP 包里就是一个完整信封。**
**所以它的大小必须控制在一个 MTU 内**(一般 1400 字节左右), **否则会被分片、容易丢。**
**一条 Probe 报文(这就是「谁在?」的全部内容):**
段 内容 作用
------------------------------------------------------------------------------------------
Header `MessageID` **这条消息的编号,回包要带它**
Header `To` 固定的 discovery URI
Header `Action` **`…/Probe`,说明这是「寻找」**
Body `d:Types` **`dn:NetworkVideoTransmitter`(找摄像机)**
**第四行那个类型是关键**: **`NetworkVideoTransmitter` 就是「网络视频发送端」(NVT)**,
**也就是摄像机。**
**换成别的类型就找别的设备**:
**`NetworkVideoDisplay` 找解码器,`NetworkVideoStorage` 找 NVR。**
**所以「发现不到摄像头」有时是类型写错了**,不是网络问题。
**实测这条报文:**
**总长 582 字节**(一个 UDP 包,远小于 MTU)
**MessageID**:`uuid:12345678-1234-1234-1234-1234567890ab`
**Action**:`http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe`
**Types**:`dn:NetworkVideoTransmitter`
**582 字节里,真正带信息的只有 Types 那 28 个字符**——
**其余全是命名空间和固定 URI。**
**这和 07.01.02 的结论一致:ONVIF 的成本在「格式」,不在「内容」。**
**设备怎么回?回一个 ProbeMatch,带自己的 XAddr:**
字段 本例的值 用途
----------------------------------------------------------------------------------------------
`RelatesTo` uuid:12345678-1234-1234-1234-1234567890ab **对上那条 Probe**
`EndpointReference` urn:uuid:ipc-2000-0001 设备唯一标识
`Scopes` onvif://…/type/video_encoder … **自报类型和名字**
`XAddrs` http://192.168.1.64/onvif/device_service **关键:后续请求发这里**
**第四行是发现的全部目的**: **拿到这个地址,后面的 SOAP 请求才有地方发。**
**第一行的 `RelatesTo` 是配对机制**:
**设备可能同时收到多台客户端的 Probe,靠这个字段区分回给谁。**
**所以「MessageID 随便填」是可以的,但「不能不填」**——
**不填的话设备的 `RelatesTo` 没法对上,客户端只能靠猜。**
**本机能验证什么、不能验证什么:**
项 本机 原因
----------------------------------------------------------------------
报文格式 **能** **上面就是真解析出来的**
组播可达性 **不能** **沙箱通常禁组播,也没有真设备**
发现耗时 **不能** 没设备可发现,测不出
**这一张表是这一节的诚实边界。**
**「报文该长什么样」我给了实物,「能不能发现设备」我没测**——
**这两件事经常被混为一谈,写清楚比糊过去有用。**
这条 Probe 是我在 Demo 里手拼的字符串,不涉及任何网络操作——
只负责「拼一份合法的报文」和「把它解回来」。
所以这个 Demo 在没有网络、也发不出组播的沙箱里照样能跑。
「能拼出报文」和「能发现设备」是两件事,这里只做前者。
第二个打印那个 28 是 Types 的字符数,
注意它是「字符数」不是「字节数」——dn:NetworkVideoTransmitter 全是 ASCII,两者相等,
但换成中文就会不一样。
这种口径不写清,下次换一个含中文的字段就会对不上。
ProbeMatch 也是同样手拼的,回包里那四个字段分属两个命名空间:
RelatesTo 和 MessageID 在 WS-Addressing 里,XAddrs 和 Scopes 在 WS-Discovery 里。
所以一个 find 找不全,得按命名空间分开找——
这也是「前缀只是别名」的又一个印证:真正决定去哪找的是 URI。
30 秒实验
- 把
Types改成dn:NetworkVideoDisplay,看字节数变不变、变多少。 - 把
MessageID那一行删掉,看报文还能不能被解析(能,但设备没法回)。 - 算一下「582 字节里命名空间声明占多少」:把所有
xmlns:的长度加起来比一比。
常见误区
- 以为 WS-Discovery 是另一套协议。 它就是把 SOAP 塞进 UDP, 信封格式和 HTTP 那套一模一样。
- 以为报文能拼出来就能发现设备。 中间隔着组播、防火墙、IGMP, 沙箱里发不出去,真实现场也常被封。
- 以为 MessageID 可以随便省。 设备靠
RelatesTo配对,没有 MessageID 就没法配对, 回包会被当成陌生报文丢掉。
自测三问(点开对答案)
1. WS-Discovery 用的是什么传输、哪个端口? —— **UDP,端口 3702,组播地址 239.255.255.250。** **报文必须控制在一个 MTU 内**,否则会被分片。 2. Probe 报文里真正带信息的是哪一段? —— **`Types`**,也就是「找什么类型的设备」,**其余全是命名空间和固定 URI。** 3. ProbeMatch 里哪个字段是发现的最终目的? —— **`XAddrs`**——后续所有 SOAP 请求都要发到这个地址。下一节讲设备类型:NVT/NVD/NVS/NVA 四种缩写,以及「判类型」和「判 Profile」为什么是两件事。
- ☐ (本节完,下一节 07.02.05 设备类型)