📹 第八章 ONVIF框架代码案例应用
📄 一句话概括

设备搜索就是四步:组播问、单播收、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 秒实验

  1. 把 D.harvest() 里所有字段都打出来,看除了 XAddrs 还有什么有用的。
  2. 用 D.Device() 起两台,看 Probe 会收到几条、怎么区分它们。
  3. 把 GetDeviceInformation 连续调 5 次,看耗时是不是每次都 13 毫秒左右。

常见误区

  1. 用 sendto 发完 Probe 就关 socket。 回应是单播回来的,socket 关了就没人在听, 表现是「发了没回应」。
  2. 把 XAddrs 里的端口写死成 80。 有的厂家用 8080 或 8000,所以要从字符串里解析端口, 不能假设。
  3. 看到「发现成功、取信息失败」就去查密码。 第一检查项是 URL 里的协议—— https 而客户端没配 SSL,连都连不上。
自测三问(点开对答案) 1. 发现之后为什么必须换成单播? —— **因为组播是一对多**,管理操作是一对一。**继续用组播等于每次操作都吵醒全网。** 2. 「发现 0.73 毫秒、取信息 13.48 毫秒」说明设备慢吗? —— **不说明。那 13 毫秒里大半是 TCP 建连**,**gSOAP 默认每个请求都 `Connection: close`。** 3. 发现能告诉你设备有没有故障吗? —— **不能。能应答 Probe 只说明网络通、服务活着。**镜头被挡住、编码器卡死,它照样应答。

下一节 08.03:把设备信息、能力、服务清单三样一次问齐。

相关笔记