📹 第九章 SIP及eXosip开源库应用
📄 一句话概括

SIP 的地址不是 IP,是 URI——所以设备换 IP 只要重新注册,号码不变。 国标那 20 位编码不是流水号,它每一段都有含义,其中第 14 位错了最坑。

📝 本节和前面几节的关系

09.01.01 说了「注册把号码翻译成地址」,这一节讲号码本身。 07.02.01 讲 ONVIF 的 token 时说过「地址是设备告诉你的」, SIP 的 URI 是同一件事的另一套做法。

一、六种形状,一个语法

SIP URI 的通用形状是:

sip:用户@主机:端口;参数?头

五个部分只有「主机」是必需的,其余都可省:

部分 能不能省 省了会怎样
用户部分 能 只能用于 REGISTER
主机 不能 —
端口 能 默认 5060
参数 能 用默认传输

第一行那个「只能用于 REGISTER」是硬规矩:sip:3402000000 这种地址没有用户部分, 它表示「这个域的注册服务器」,所以只能在注册时用。 对 INVITE 用这种地址会被拒——因为「呼叫一个域」没有意义,必须指明叫谁。 第三行那个默认端口 5060 是 SIP 的标准端口,和 RTSP 的 554、HTTP 的 80 一样属于「约定」。

二、五种元素

元素 干什么 能不能省
UAC 发起请求的一方 不能
UAS 接收请求的一方 不能
代理服务器 转发请求 能(点对点)
注册服务器 登记号码与地址 大规模必需
重定向服务器 只回地址不转发 能

前两行是「角色」不是「设备」: 同一台摄像机在点播时是 UAS,在主动上传时又是 UAC——角色随「谁发请求」变,不随设备变。 第四行那个注册服务器是大规模系统的基础:没有它,主叫必须事先知道每台设备的 IP, 而 IP 会变(DHCP、换网段)。 国标平台就是「注册服务器 + 代理服务器」的组合:下级设备向它注册,上级平台向它请求转发。

三、20 位编码的结构

这一串数字不是流水号,它按位置分五段:

位 长度 含义
1~8 8 中心编码
9~10 2 行业编码
11~13 3 类型编码
14 1 网络标识
15~20 6 序号

第三行「类型编码」决定设备是什么: 132 是网络摄像机、131 是模拟摄像机加编码器、118 是报警输入。 国标附录有整张类型表,这一段的取值直接决定平台把它当「视频通道」还是「报警通道」。 所以「编码填错」的后果不是报错,而是「设备上线的类型不对」—— 平台照样能收到它,只是点播按钮是灰的。

四、第 14 位为什么最坑

这一位只有 0 和 1 两个值,含义却差很远:

| 值 | 含义 | 后果 | |---|---| | 0 | 走 SIP 域 | 正常注册 | | 1 | 走 ID 直连 | 注册能过、点播失败 |

第二行那种情况最难查:设备照样能注册成功、平台也能看到它在线, 但一点播就失败,而且失败原因往往只说「目标不可达」。 因为「ID 直连」模式要求平台知道设备的真实 IP, 而平台这边是按 SIP 域去找的——两边的寻址方式对不上。 排查办法很土但有效:把编码第 14 位改成 0 试一次,通了就说明是这个问题。 这一条是从现场案例里来的,规范里只写「网络标识」,不会告诉你「填 1 会这样」。

五、为什么用 URI 而不用 IP

这是 SIP 架构上最关键的一个选择:

用 IP 用 URI
换 IP 就要改所有配置 只需重新注册
NAT 后面用不了 注册能穿透
一台设备只能有一个身份 可多号

第二行那个 NAT 穿透是实际原因之一: 内网设备注册到公网服务器时,服务器记下的是它看到的公网地址,设备自己不用知道。 第三行那个「可多号」也是有用的:一台编码器可以有好几个 SIP 号(对应不同通道), 用 IP 就表达不了这种关系。 代价是「多了一次翻译」: 每次呼叫都要先查号,多一跳延迟——这就是注册服务器存在的必要,也是它成为单点故障的原因。

六、动手 Demo:六种 URI 形状逐个解析

Demo 用一个正则把六种真实形状的 URI 拆开,看每个部分落在哪。 然后把 20 位编码按位切开。

uris = ["sip:34020000001320000001@3402000000",
        "sip:34020000001320000001@3402000000:5060",
        "sip:[email protected]:5060;transport=udp",
        "sips:34020000001320000001@3402000000",
        "sip:3402000000",
        "sip:[email protected];user=phone"]

pat = re.compile(r"(sips?):([^@;]+)(?:@([^;:]+))?(?::(\d+))?(.*)$")
for x in uris:
    sch, u1, host, port, rest = pat.match(x).groups()
    if host is None:
        u1, host = None, u1
    print(sch, u1 or "-", host, port or "-", rest or "-")

code = "34020000001320000001"
print(code[0:8], code[8:10], code[10:13], code[13], code[14:])

实跑输出:

--------------------------------------------------------------------
六种 URI 形状逐个解析
--------------------------------------------------------------------
**SIP 的地址就是 URI,但它比 HTTP 的 URL 花哨得多**:

  方案     用户部分                 主机             端口     参数
  ---------------------------------------------------------------------------
  sip      34020000001320000001     3402000000       —        —
  sip      34020000001320000001     3402000000       5060     —
  sip      34020000001320000001     192.168.1.64     5060     ;transport=udp
  sips     34020000001320000001     3402000000       —        —
  sip      —                        3402000000       —        —
  sip      alice                    example.com      —        ;user=phone

**六行覆盖了所有形状**:**带不带用户、带不带端口、带不带参数。**
**第一行与第二行是同一个地址**——**5060 是默认端口,写不写等价。**
**第四行的 `sips` 是「强制 TLS」**,
**GB/T 28181 的早期版本用过,现在的项目基本还是 `sip` + UDP。**
**第五行那种「只有域名」的写法专门给 REGISTER**,
**因为注册的 Request-URI 只写域**(下一个 Demo 会看到)。

**20 位编码不是随便凑的,它有结构**:

  位        本例         含义
  ------------------------------------
  1~8       34020000     中心编码
  9~10      00           行业编码
  11~13     132          **类型编码**
  14        0            网络标识
  15~20     000001       序号

**第三行「132」表示网络摄像机(IPC)**,
**「131」是模拟摄像机加编码器、「118」是报警输入**——
**这套编码在 GB/T 28181 里叫「设备类型」,国标附录有整张表。**
**第 14 位那个「0」是网络标识位**,
**0 表示走 SIP 域、1 表示走 ID 直连**——**这一位错了,注册能过、但点播失败。**

输出第一行的 sip:34020000001320000001@3402000000 拆成: 方案 sip、用户 34020000001320000001、主机 3402000000。端口和参数都是 —,因为用的默认值。 第五行那个 sip:3402000000 用户部分是 —,只有主机——这正是 REGISTER 用的形状。 正则里那个「用户部分不能含 @ 和 ;」是关键,因为 @ 分隔用户与主机、; 开始参数; 而「主机不能含 ; 和 :」是因为冒号后面是端口。 代码里那个 if host is None 是个补丁:没有用户部分时,正则会把它当成主机匹配掉, 所以要交换一下。 这种「一个正则处理两种形状」的写法很常见,但也容易出错—— 生产代码里最好分两条正则,别省这几行。 最后一行输出的五段就是编码结构:34020000 00 132 0 000001。

30 秒实验

  1. 把编码第 14 位从 0 改成 1,看解析结果哪一段变了。
  2. 给 URI 加一个 ?subject=xxx,看参数和头的分界在哪。
  3. 用 sip:3402000000 发一条 INVITE,看 UAS 回什么码。

常见误区

  1. 以为 SIP 地址里必须带用户部分。 REGISTER 的地址就只有域, 而且对 INVITE 用这种地址会被拒。
  2. 以为 20 位编码的后六位才是唯一标识。 前面几段决定「它是什么」,类型编码填错, 设备上线类型就不对。
  3. 以为第 14 位填什么都一样。 填 1 走 ID 直连,注册能过但点播必失败, 而报错只说「目标不可达」。
自测三问(点开对答案) 1. 为什么 `sip:3402000000` 只能用于 REGISTER? —— **它没有用户部分,表示「这个域的服务器」**。 呼叫必须指明叫谁,所以对 INVITE 用这种地址会被拒。 2. 20 位编码的哪一段决定「平台把它当什么设备」? —— **第 11~13 位的类型编码。** 132 是网络摄像机、131 是模拟加编码器、118 是报警输入, 取错则平台按错误类型收录。 3. 用 URI 而不用 IP 的代价是什么? —— **多一次号码到地址的翻译**,即多一跳延迟,而且注册服务器成了单点故障。

下一节讲三元组——SIP 靠三样东西把一堆报文认成一次会话。

相关笔记