📹 第十章 GB/T 28181协议原理
📄 一句话概括

同一台摄像机在三个系统里有三个标识:国标 20 位编号、ONVIF 的 token、平台内部的 ID。 数据交换的绝大部分工作量,就是把这三套标识对上——协议本身反而不是最难的。

📝 本节和前面几节的关系

前十节讲的都是「一路流怎么走」。这一节往外看一层:多个系统之间怎么交换数据。 交换的内容有三类:目录(有哪些设备)、状态(在不在线)、媒体(流和录像)。

一、三套标识各管一段

国标编号是 20 位数字,不是随机数,是带结构的: 1~8 位中心编码、9~10 位行业编码、11~13 位类型编码,14 位网络标识,15~20 位序号。 所以编号本身携带信息:看一眼就知道「这台设备属于哪个域、是摄像机还是平台」。 这也是为什么不能随便改编号——改一位就换了归属。 ONVIF 的 token 是一串不透明字符串:它对内容不作任何承诺,只是「这个服务里的唯一名字」。 设备重启后 token 可能变,所以不能当业务主键。 平台内部 ID 一般是自增整数:查得快、占得少,但一出本平台就没意义。 三套标识的分工很清楚: 国标编号用来跨系统对话,token 用来跟设备对话,内部 ID 用来跟数据库对话。

二、目录同步是主战场

级联对接时第一件事不是调点播,是同步目录: 上级平台要知道「下级有多少设备、编号是什么、在不在线」。 国标的做法是「查询+订阅」: 先用 Catalog 查一次全量,再订阅变化;下级有设备上下线就主动 Notify 推上来。 这里最容易出两种问题:一是「全量太大」——一万路设备的目录报文能到几兆,要分批传; 二是「增量丢包」——通知没收到,两边目录就对不上。 所以工程上通常会「定时全量校准」:哪怕增量机制工作正常,也每隔几小时重拉一次全量。 这不是不信任协议,是不信任网络。 分页这件事标准里没写死,各家实现差别很大,对接时要先确认对方支不支持 SumNum 分页。

三、幂等与去重

数据交换一定会重复:网络超时重发、订阅推两遍、级联多层转发,都会让同一条消息到两次以上。 所以接收方必须幂等:同一台设备被通知上线十次,数据库里也只能有一条记录。 判断幂等的键不能用「消息序号」——序号在重发时会变。 要用「设备编号+事件类型+时间」这种业务键。 还有一个反复出现的坑是「删除后又出现」: 设备离线被删、又上线被加,如果业务表里有外键关联录像,这一删一加就把历史录像的归属弄断了。 正确做法是「软删除」——标记不用真删。

四、动手 Demo:五千路设备,编号和内部 ID 怎么对齐

先算三套标识各占多少内存,再看「一次点播要查几次」。 最后列四种多对一场景,它们是对接时真正的坑。

n = 5000
for name, key in (("国标编号", 20), ("ONVIF token", 12),
                   ("内部自增 ID", 8)):
    print(name, key, "%.0f KB" % (key * n / 1024.0))

print("线性查平均比较", n // 2, "次、", n // 2 * 20, "字节")
print("哈希表:一次哈希加一次比较,与路数无关")

实跑输出:

--------------------------------------------------------------------
五千路设备,编号和内部 ID 怎么对齐
--------------------------------------------------------------------
**国标用 20 位数字编号,ONVIF 用一串不透明的 token,平台内部还得有自己的 ID。**
**三方各说各话,中间那张映射表就是数据交换的全部工作量。**

  标识                单个长度        五千路合计     类型
  ---------------------------------------------------------------
  **国标编号**        **20 字节**     **98 KB**      **文本**
  **ONVIF token**     **12 字节**     **59 KB**      **文本**
  **内部自增 ID**     **8 字节**      **39 KB**      **整数**

**光看这张表会觉得「才几百 KB,无所谓」**——**真正的成本不在存储,在查找。**

**一次点播要查三次**:**先按国标编号找到设备,再按设备找到 ONVIF token,最后按 token 取出会话。**
**三次字符串比较,每次二十个字节左右。**

  按字符串线性查:平均比 2500 次、每次 20 字节 → 50000 次字符比较
  换成哈希表:一次哈希 + 一次比较,与路数无关

**这就是为什么所有国标平台的编号表都是哈希表而不是数组**——
**编号是「带结构的字符串」,不能当连续整数用。**

**多对一是另一个坎**:
  场景                    映射关系                 麻烦在哪
  ---------------------------------------------------------------------------------
  **一个平台多个域**      编号前 10 位不同         **同一台设备可能有两个编号**
  **级联上下级**          上级重新编号             **上下级编号必须翻译**
  **一台 NVR 多通道**     设备 1 个、通道 N 个     **NVR 编号不能直接点播**
  **设备换新**            编号变、位置不变         **业务表要按位置而不是编号**

**第二行是最容易出事的地方**:**上级平台往往会给下级设备重新编一套号**,
**于是同一台摄像机在两个平台上有两个编号。**
**做级联对接时,第一件事不是调协议,是把这张对照表建起来。**
**没有这张表,所有「点播失败」都会变成玄学。**

三行加起来不到两百 KB,所以「映射表占内存」这个担心是多余的。 真正的成本在查找次数上。 一次点播要查三次这件事很关键:按编号找设备、按设备找 token、按 token 取会话。 如果三次都线性查,一万路就是几万次字符串比较,并发一上来延迟就顶不住了。 所以所有国标平台的编号表都是哈希表—— 编号是带结构的字符串,不能当连续整数用,也就不能按下标寻址。 最后那张表里第二行最容易出事:上级平台往往会给下级设备重新编一套号。 同一台摄像机在两个平台上有两个编号,这在对账时是灾难—— 因为「编号」这个看起来最像主键的东西,恰恰不唯一。

30 秒实验

  1. 算一下:二十位编号打 ASCII,五千路占多少 KB?(98 KB)
  2. 想一下:为什么 ONVIF 的 token 不能当业务主键?
  3. 想一下:为什么目录同步要「定时全量校准」?

常见误区

  1. 以为「国标编号可以随便改」。 它带结构,前 10 位是域,改了就换了归属。
  2. 以为「目录同步一次就够」。 增量会丢,必须定时全量校准。
  3. 以为「消息序号能用来去重」。 重发时序号会变,要用业务键。
自测三问(点开对答案) 1. 国标编号为什么要带结构? —— **因为它同时承载了「归属哪个域、什么类型」这些信息**,**看一眼就能判断归属**。 2. 目录同步的两种机制是什么? —— **全量查询(`Catalog`)和订阅通知(`Notify`)**,工程上还要加定时全量校准。 3. 为什么删除设备要用软删除? —— **硬删会切断录像等历史数据的归属**,**设备再上线时就对不上了。**

下一节讲一个最小的坑:一个汉字三字节,可 Content-Length 有人按字符数算。

相关笔记