设备搜索就是四步:组播问、单播收、HTTP 请求、解析。
第一、三步是两套协议(UDP 与 HTTP),中间靠 XAddrs 这个字符串衔接。
前两节讲原理,这一节把闭环跑通:从一句「谁在」到拿到厂家型号。 这是第八章的分水岭——前面都在准备,这里第一次看到「数据从设备回来」。 跑通之后,08.03 到 08.07 都是「换一个操作名」的事。
一、四步与两套协议
| 步 | 做什么 | 协议 | 谁发起 |
|---|---|---|---|
| 1 | 组播发 Probe | UDP 组播 | 客户端 |
| 2 | 收 ProbeMatches,抽 XAddrs | UDP 单播 | 设备 |
| 3 | 按 XAddrs 发 SOAP | HTTP 单播 | 客户端 |
| 4 | 解析响应 | HTTP | 设备 |
第一行与第三行是两套完全不同的协议,
而它们之间只靠一个字符串传递信息——这就是为什么 XAddrs 解析错了后面全错。
第二行那个「UDP 单播」容易被忽略:
Probe 是组播发出去的,但回应是「回给源地址」的单播——
所以客户端必须用一个「绑定了端口的 socket」去发,否则收不到回应。
很多「发了 Probe 没回应」的问题就出在这里:
用 sendto 发完就关 socket,回应回来时没人在听。
二、XAddrs 长什么样
它就是一条 URL,但有几个细节值得看:
http://192.168.1.64/onvif/device_service
http://192.168.1.64:8080/onvif/device_service
https://192.168.1.64/onvif/device_service
第一行是标准形态:80 端口、/onvif/device_service 路径。
第二行说明「端口可能是别的」——有的厂家用 8080、有的用 8000,所以别把端口写死成 80。
第三行那个 https 是个坑:它说明设备开了 HTTPS,
但 typemap.dat 里没配 SSL 的话,gSOAP 会直接连不上——表现是「发现了但取不到信息」。
所以「发现成功、取信息失败」的第一检查项不是密码,是 URL 里的协议。
三、为什么发现便宜、取信息贵
实测数据:发现 0.73 毫秒,取信息 13.48 毫秒。 差了近二十倍,原因不是设备慢。
| 阶段 | 开销在哪 | 能不能省 |
|---|---|---|
| 发现 | 一个 UDP 包 | 省不了,但很便宜 |
| 取信息(首次) | TCP 建连 + 请求 + 响应 | 能省:复用连接 |
| 取信息(后续) | TCP 复用 + 请求响应 | 能省:批量 |
第二行那个「首次」是关键:13 毫秒里有大半是 TCP 三次握手,
不是设备处理慢——这一点必须说清,否则很容易得出「这台设备性能不行」的错误结论。
gSOAP 默认每个请求都是 Connection: close,所以「每次调用都重新握手」是默认行为。
如果客户端要频繁轮询,应该打开 keep-alive——
但那要改 soap_set_omode(soap, SOAP_IO_KEEPALIVE),属于优化范畴。
第三行那个「批量」指的是:一次请求里问多个东西(有的操作支持),比调十次便宜得多。
四、发现的三个边界
这一节最后要说清「发现能做到什么、做不到什么」:
| 能做到 | 做不到 | |---|---|---| | 同一网段(或组播可达)内找设备 | 跨公网找设备 | | 拿到服务地址与基本类型 | 拿到「画面好不好」 | | 知道设备在不在线 | 知道设备有没有故障 |
第一行右边那条是硬限制:组播不跨公网,
所以「云端管设备」必须靠设备主动上报(反向注册),
这也是为什么很多厂商要做「设备主动连平台」那一套。
第二行右边是「发现只给类型不给质量」:它只能告诉你「这是网络视频发送器」,
不能告诉你「分辨率多少、码率多少」——那些要 GetProfiles 才知道。
第三行右边是「在线不等于健康」:能应答 Probe 只说明网络通、服务活着,
镜头被挡住、编码器卡死,它照样会应答。
五、动手 Demo:从一句「谁在」到拿到设备信息
Demo 跑完整闭环:组播问、收 XAddrs、按地址发 SOAP、解析出五个字段。 并且分开计时——看发现和信息各花多少。
# ① 组播问,抽出 XAddrs
_x, _mid, got, ms = D.probe(dev.port)
xaddr = D.harvest(got[0])["xaddrs"]
# ② 按 XAddrs 发 SOAP(地址里的端口就是 HTTP 端口)
_a, body, n = S.call(port, "device", "GetDeviceInformation")
# ③ 解析:只取「有文本、没子元素」的那些节点
for el in ET.fromstring(body).iter():
if el.text and len(el) == 0:
print(el.tag.split("}")[1], "=", el.text)
实跑输出:
--------------------------------------------------------------------
从一句「谁在」到拿到设备信息
--------------------------------------------------------------------
**这一步是第八章的分水岭**:前面都在准备代码,这里第一次跑完整个闭环。
**四步**:
步 做什么 走什么协议
----------------------------------------------------
1 组播发 Probe **UDP 组播**
2 收 ProbeMatches,抽 XAddrs UDP 单播回来
3 按 XAddrs 发 SOAP 请求 **HTTP 单播**
4 解析响应 HTTP
**第一行和第三行是两套协议**,这是新手最容易混的地方:
**发现走 UDP,管理走 HTTP**,中间靠一个字符串(XAddrs)衔接。
**所以「ping 得通但发现不了」是正常的**——
**ping 是 ICMP,发现是 UDP 组播,两者走的不是一条路。**
**第 1、2 步实测**:
Probe 发出 630 字节,0.75 毫秒后收到 1 条应答
抽出来的 XAddrs:`http://127.0.0.1:52705/onvif/device_service`
**第 3、4 步:拿这个地址去问设备信息。**
请求 424 字节,13.38 毫秒回来
Manufacturer = ACME
Model = IPC-2000
FirmwareVersion = 5.6.1
SerialNumber = SN20240501
HardwareId = HW-1
**整条链 14.21 毫秒**(第 1 步到最后一步),其中:
**发现占 0.75 毫秒,取信息占 13.38 毫秒。**
**这 13 毫秒里有大半是 TCP 建连**,因为每个请求都带 `Connection: close`。
**不是设备慢**——所以别拿单次耗时判断设备性能。
**比例本身说明一件事**:**发现是「一次广播」很便宜**,
**真正慢的是后面的 HTTP 往返**——所以客户端要缓存 XAddrs,
**别每次取信息都重新 Probe 一遍。**
第三段那个过滤条件值得看:
el.text and len(el) == 0——「有文本」且「没有子元素」的节点才是叶子字段。
只判断 el.text 不够:父节点的 .text 可能是空白字符,于是你会打出一堆空行。
只判断 len(el) == 0 也不够:空元素(<x/>)也会被选中。
两个条件都要,才能精准捞出那五个字段。
输出里那 14.31 毫秒的总时间是「本机加上一个 Python 服务端」的结果,
真实设备上会更大(设备 CPU 弱、
网络有延迟)——所以这个数只能用来比「发现 vs 取信息」的相对关系,不能当绝对值。
最后那句「缓存 XAddrs」的结论就是从这组数据来的:
发现很便宜但会吵全网,取信息贵但只涉及一台——
所以「发现一次、缓存地址、之后只取信息」是最优策略。
30 秒实验
- 把
D.harvest()里所有字段都打出来,看除了 XAddrs 还有什么有用的。 - 用
D.Device()起两台,看 Probe 会收到几条、怎么区分它们。 - 把
GetDeviceInformation连续调 5 次,看耗时是不是每次都 13 毫秒左右。
常见误区
- 用
sendto发完 Probe 就关 socket。 回应是单播回来的,socket 关了就没人在听, 表现是「发了没回应」。 - 把 XAddrs 里的端口写死成 80。 有的厂家用 8080 或 8000,所以要从字符串里解析端口, 不能假设。
- 看到「发现成功、取信息失败」就去查密码。 第一检查项是 URL 里的协议——
https而客户端没配 SSL,连都连不上。
自测三问(点开对答案)
1. 发现之后为什么必须换成单播? —— **因为组播是一对多**,管理操作是一对一。**继续用组播等于每次操作都吵醒全网。** 2. 「发现 0.73 毫秒、取信息 13.48 毫秒」说明设备慢吗? —— **不说明。那 13 毫秒里大半是 TCP 建连**,**gSOAP 默认每个请求都 `Connection: close`。** 3. 发现能告诉你设备有没有故障吗? —— **不能。能应答 Probe 只说明网络通、服务活着。**镜头被挡住、编码器卡死,它照样应答。下一节 08.03:把设备信息、能力、服务清单三样一次问齐。
- ☐ (本节完,下一节 08.03 获取设备基本信息)