我的音视频/流媒体开源项目(github)
GB28181系列目录
目录
一、SIP消息Header字段
二、SIP URI(URL)
三、SIP路由机制
1、路由机制介绍
2、严格路由(Strict Routing)与松散路由(Louse Routing)
3、总结
四、SIP消息示例
1、注册-带鉴权
2、呼叫
3、MESSAGE示例
一、SIP消息Header字段
上一篇文章介绍了SIP的各种信令和两个用户之间通话的SIP信令交互流程,本篇将详细介绍SIP的消息格式。常见的SIP方法有:INVITE、ACK、PRACK、BYE、CANCEL、REGISTER、OPTIONS。在这些类型的SIP消息头部中包含的常见字段如下表所示:
Header | 含义说明 | 举例 |
Call-ID | 由本地设备(Client)生成,全局唯一,每次呼叫这个值唯一不变 | Call-ID: asd88asd77a@1.2.3.4 |
From | 表示请求的发起者 | From: sip:user1@domain.com;tag=49583 |
To | 表示请求的接收者 | To: sip:user2@domain.com |
Via | Via头域是被服务器插入request中,用来检查路由环的,并且可以使response根据via找到返回的路 | Via: SIP/2.0/TCP user1pc.domain.com;branch=z9hG4bK776sgdkse |
Max-Forwards | 用于表示这个包最多可以传送多少跳,每经过一跳都会减一当Max-Forwards==0系统会返回483。默认为70 | Max-Forwards: 70 |
Contact | 包含源的URI信息,用来给响应方直接和源建立连接用 | Contact: sip:192.168.100.1:1111 |
Content-Type | 指明消息体的类型 (SDP会话描述协议) | Content-Type: text/plain;Content-Type: application/sdp; Content-Type: application/cpim; |
Content-Length | 指明消息体的字节大小 | Content-Length: 18 |
下面解释一下其中比较难理解的几个字段,之前已经解释了dialog(对话)、session(会话)、transaction(事务),忘了的可以去上一篇再看一下。
1、Via:
Via头域是收集经过的设备信息。一个SIP消息每经过一个Proxy(包括主叫),都会被加上一个Via头域,当消息到达被叫后,Via头域就记录了请求消息经过的完整路径。被叫将这些Via头域原样copy到响应消息中(包括各Via的参数,以及各Via的顺序),然后下发给第一个Via中的URI,每个Proxy转发响应消息前都会把第一个Via(也就是它自己添加的Via)删除,然后将消息转发给新的第一个Via中的URI,直到消息到达主叫。
如下图所示,表示了Via的作用:
2、tag:
用于在SIP消息中的 To 和 From 头部区分和标识不同的参与者。区分同一个会话中不同的SIP实体,尤其是当一个用户(或服务器)有多个可能的通信终端时。Tag 允许SIP协议区分从同一源发起的多个请求,通常是随机字符串或特定标识符。
From: From: Alice <sip:alice@atlanta.com>;tag=client1
To: To: Bob <sip:bob@biloxi.com>;tag=client2
在这个例子中,发起方 Alice 使用了 client1 作为 From 的Tag,接收方 Bob 则使用了 client2 作为 To 的Tag。
Call-ID、From(tag)、To (tag)这三个字段相同代表是同一个dailog对话。
3、branch:
branch值相同,代表同一个 transaction(事务) Branch,每发起一个transaction(事务)都要重新生成一个branch;对于遵循RFC3261规范的实现,这个branch参数的值必须用magic cookie”z9hG4bK”打头. 其它部分是对“To, From, Call-ID头域和Request-URI”按一定的算法加密后得到。
二、SIP URI(URL)
再来看一下SIP URI格式,SIP消息中有很多地方都用到了SIP URI,其中包括To、From、Contact这些头域,还有说明请求目的端的Request-URI。SIP URI里的信息说明了联系SIP资源(用户)的具体方式。
SIP URI以方案名sip打头,紧接着是一个冒号“:”,然后是用户名加@,@后面是主机名或IPv4/ IPv6地址,端口是可选的,如果有端口,用冒号“:”分隔,最后是URI参数列表,参数列表中的参数以分号“;”作为分割符。示例如下:
sip:joseph.fourier@transform.example.org:5060;transport=udp;user=ip;method=INVITE;ttl=1;maddr=240.101.102.103?Subject=FFT
示例中的?用于分隔查询参数(Query Parameters)与 URI的路径部分。它后面的内容通常是一些附加的参数,用于为SIP请求提供额外的信息。查询参数是键值对形式的,多个参数之间用 & 分隔。
有些SIP URI没有用户名,只有主机名或IP,比如说REGISTER请求的Request-URI。在上面实例中,端口使用的是SIP的标准端口5060。对于SIP URI来说,如果没有描述端口,那么就假定它使用的是默认端口5060;对于SIPS URL来说,则是5061。transport参数说明传输协议选用UDP,这也是SIP的缺省选择。transport参数的可选值还有TCP、TLS和SCTP。
user参数来说明URI的用户名部分是否为电话号码。它的缺省值为ip,说明不是电话号码。如果是电话号码,那么它的值为应该设为phone。
method参数说明所使用的方法。默认值是INVITE。这个参数对To、From头域及Request-URI来说没有意义,但对于注册的Contact头域或转移的Refer-To头域来说是有用处的。
ttl参数是生存时间,只有当maddr参数包含多播地址,而transport参数包含udp时,才能使用该参数。默认值是1。此值作用于多播会话广播。
maddr通常包含请求应当被定向到的多播地址,覆盖URI中主机部分的地址。它还可以携带备用服务器的单播地址。
method、maddr、ttl及header这几个参数不允许出现在To或From头域中,但可以在Contact头域或Request-URI使用。除了这些参数之外,SIP URI可以包含其它用户定义参数。
三、SIP路由机制
参考:
【SIP协议路由机制】_sip路由机制-CSDN博客
SIP 协议路由规则详解_sip route-CSDN博客
1、路由机制介绍
SIP的路由机制是SIP协议很重要的部分,通过路由机制,才使得一个呼叫请求在VOIP网络的众多Proxy节点中,找到正确的目的端。
SIP路由包括:请求路由(Route)与响应路由(Via),响应路由(Via)已经在上面说明,下面重点看一下请求路由(Route)。
请求路由(Route)的生成是通过头域Record-Routes进行收集。
路由收集(Record-Route):请求消息被代理转发前,由代理服务器插入请求消息中,这样可以使该对话中将来的请求仍能经过该Proxy。
路由(Route):发起端收到响应确认消息后,根据响应消息中的Record-Route头域获得一个Proxy列表,用来指示后续请求消息的路由;同样,目的端收到请求消息后,根据请求消息的Record-Route头域获得一个Proxy列表,用来指示后续请求消息的路由。
Route和Record-Route具体处理过程如下:
在一个请求消息的传输过程中,Proxy也可能会添加一个Record-Route头域,这样当消息到达被叫后里面就有会有0个或若干个Record-Route头域。被叫会将这些Record-Route头域并入自己的路由集,随后被叫在发送请求(和Record-Route属于同一个dialog里面)消息时就会使用该路由集构造一系列Route头域,以便对消息进行路由。
另外,被叫会像上面对待Via头域一样,将Record-Route头域全部原样copy到响应消息中返回给主叫。主叫收到响应消息后也会将这些Record-Route头域并入路由集,只是它会将其反序。该会话中的后续请求(和Record-Route属于同一个dialog里面)消息的Route头域就会通过路由集构造。
目的(Contact):联系人或最终的目的地地址。不管是请求端还是目的端,当发起请求是目的地址就是Contact。请求消息或响应消息必须携带Contact头域,告诉对方自己的联系地址。
如下图所示,是Contact头域示例:
2、严格路由(Strict Routing)与松散路由(Louse Routing)
- 严格路由(Strict Routing):可以理解为比较“死板”的理由机制,这种路由机制在SIP协议的前身RFC 2534中定义,其机制非常简单。
- 松散路由(Louse Routing):该路由机制较为灵活,也是SIP路由机制的灵魂所在,在RFC 3261中定义。
- 混合路由:如果在一条路由的路径中,既有严格路由又有松散路由,我们称之为混合路由。
如何判断一个Proxy是否为严格路由还是松散路由?
根据请求消息中的Route字段中设置的SIP URI一个lr的属性进行判断,当携带有“lr”属性就表明该Proxy为松散路由Proxy,否则就是严格路由。如:
Route:sip:a.b.c.d;lr
这就是表示这个地址所在的Proxy是一个Loose Router。需要注意的是"lr"属性是在Proxy添加Record-Roud时设置的。
严格路由的处理步骤如下:
- 接收到的消息的request-URI必须是自己的URI;
- 把第一个Route头域“弹”出来,并把其中的URI作为新的request-URI;
- 然后把该消息路由到新的request-URI;
松散路由的处理步骤如下:
- Proxy首先会检查消息的request-URI是不是属于自己所负责的域。如果是,它就会通过定位服务将该地址“翻译”成具体的联系地址并以此替换掉原来的request-URI;否则,它不会动request-URI;
- Proxy检查路由表中的第一个地址是否为自己,如果是则从表中删除;
- Loose Router首先会检查Request URI是否为自己插入到路由表中的地址,如果不是,则不作处理;如果是,则取出Route字段的最后一个地址作为Request URI地址,并从Route字段中删去最后一个地址;
- Loose Router检查下一跳是否为Strict Router。如果不是,不处理;否则,将Request URI插入到路由表表尾,并用下一跳地址Strict Router的地址)更新Request URI;如果路由表为空,则路由给Request URI;否则,路由到路由表表首地址。
3、总结
Via头域是为了给一个请求消息的响应消息留后路,而Record-Route就是为了给该请求消息之后的请求消息留后路。
Record-Route头域不用来路由,而只是起到传递信息的作用。
Route头域才是用来指示路由。
Contact头域是用来告诉对方我的联系地址。
再来看一个交互示例,就可以很清晰的知道路由的机制了。
四、SIP消息示例
1、注册-带鉴权
过程如下:
- 请求端192.168.2.161发送注册消息给192.168.2.89服务器
- 服务器对192.168.2.161发送“401 Unauthorized”信息给请求端,提示请求端需要带上鉴权信息重新注册
- 请求端带上鉴权信息后(带有“Authorization”头字段)重新向服务器注册
- 服务器验证鉴权头的正确性,如果鉴权成功,给请求端发送200 OK消息。若失败,继续发送401消息。
请求端192.168.2.161发送注册消息给192.168.2.89服务器:
REGISTER sip:192.168.2.89 SIP/2.0
Via: SIP/2.0/UDP 192.168.2.161:8021
Max-Forwards: 70
From: <sip:01062237493@192.168.2.89>;tag=efca469543ce4788a6a6a2c7b66cd01f;epid=de4504430d
To: <sip:01062237493@192.168.2.89>
Call-ID: c88a247a74b54a8c9e676bdde3bba6c9@192.168.2.161
CSeq: 1 REGISTER
Contact: <sip:192.168.2.161:8021>;methods="INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER"
User-Agent: RTC/1.2.4949 (BOL SIP Phone 1005)
Event: registration
Allow-Events: presence
Content-Length: 0
服务器对192.168.2.161发送401 Unauthorized信息给请求端,提示请求端需要带上鉴权信息重新注册:
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.2.161:8021
From: <sip:01062237493@192.168.2.89>;tag=efca469543ce4788a6a6a2c7b66cd01f;epid=de4504430d
To: <sip:01062237493@192.168.2.89>;tag=-001893-38ba013ba3dde36e
Call-ID: c88a247a74b54a8c9e676bdde3bba6c9@192.168.2.161
CSeq: 1 REGISTER
Allow: INVITE,ACK,OPTIONS,BYE,CANCEL,REGISTER,INFO,UPDATE,PRACK,REFER,SUBSCRIBE,NOTIFY,MESSAGE
Contact: <sip:192.168.2.89:14010>
Content-Length: 0
WWW-Authenticate: Digest realm="192.168.2.89", qop="auth", nonce="e17d377c3d2d9c343e26576a7fd04738481dfc10", opaque="", stale=FALSE, algorithm=MD5
请求端192.168.2.161通过Authorization头字段带上鉴权头信息,发送一个新的REGISTER消息:
REGISTER sip:192.168.2.89 SIP/2.0
Via: SIP/2.0/UDP 192.168.2.161:8021
Max-Forwards: 70
From: <sip:01062237493@192.168.2.89>;tag=efca469543ce4788a6a6a2c7b66cd01f;epid=de4504430d
To: <sip:01062237493@192.168.2.89>
Call-ID: c88a247a74b54a8c9e676bdde3bba6c9@192.168.2.161
CSeq: 2 REGISTER
Contact: <sip:192.168.2.161:8021>;methods="INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER"
User-Agent: RTC/1.2.4949 (BOL SIP Phone 1005)
Authorization: Digest username="01062237493", realm="192.168.2.89", qop=auth, algorithm=MD5, uri="sip:192.168.2.89", nonce="e17d377c3d2d9c343e26576a7fd04738481dfc10", nc=00000001, cnonce="12660455546344082314666316435946", response="f57e47ce03162293b9ced07362ce2b79"
Event: registration
Allow-Events: presence
Content-Length: 0
服务器验证鉴权头的正确性,如果鉴权成功,给请求端发送200 OK消息。若失败,继续发送401消息:
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.2.161:8021
From: <sip:01062237493@192.168.2.89>;tag=efca469543ce4788a6a6a2c7b66cd01f;epid=de4504430d
To: <sip:01062237493@192.168.2.89>;tag=-001894-a5eb977c8969aa51
Call-ID: c88a247a74b54a8c9e676bdde3bba6c9@192.168.2.161
CSeq: 2 REGISTER
Allow: INVITE,ACK,OPTIONS,BYE,CANCEL,REGISTER,INFO,UPDATE,PRACK,REFER,SUBSCRIBE,NOTIFY,MESSAGE
Contact: sip:192.168.2.161:8021
Content-Length: 0
Expires: 3600
关于鉴权的方式参考我的另外一篇文章RTSP系列二:RTSP协议鉴权_rtsp鉴权-CSDN博客
2、呼叫
主叫端发起INVITE请求:
INVITE sip:33010602001310019325@10.64.49.218:7100 SIP/2.0
Via: SIP/2.0/UDP 10.64.49.44:7100;rport;branch=z9hG4bK1839167633
From: <sip:130909115229300920@10.64.49.44:7100>;tag=868569348
To: <sip:33010602001310019325@10.64.49.218:7100>
Call-ID: 2074790969
CSeq: 20 INVITE
Contact: <sip:130909115229300920@10.64.49.44:7100>
Content-Type: Application/SDP
Max-Forwards: 70
User-Agent: Hikvision
Subject: 33010602001310019325:0,130909115229300920:0
Content-Length: 216
v=0
o=33010602001310019325 0 0 IN IP4 10.64.49.44
s=Play
c=IN IP4 10.64.49.44
t=0 0
m=video 5494 RTP/AVP 96 97 98
a=rtpmap:96 PS/90000
a=rtpmap:97 MPEG4/90000
a=rtpmap:98 H264/90000
a=recvonly
y=0999999999
被叫端振铃 100 Trying:
SIP/2.0 100 Trying
Via: SIP/2.0/UDP 10.64.49.44:7100;rport=7100;branch=z9hG4bK1839167633
From: <sip:130909115229300920@10.64.49.44:7100>;tag=868569348
To: <sip:33010602001310019325@10.64.49.218:7100>
Call-ID: 2074790969
CSeq: 20 INVITE
User-Agent: Hikvision
Content-Length: 0
被叫端返回200 OK:
SIP/2.0 200 OK
Via: SIP/2.0/UDP 10.64.49.44:7100;rport=7100;branch=z9hG4bK1839167633
From: <sip:130909115229300920@10.64.49.44:7100>;tag=868569348
To: <sip:33010602001310019325@10.64.49.218:7100>;tag=3330812776
Call-ID: 2074790969
CSeq: 20 INVITE
Contact: <sip:130909113319427420@10.64.49.218:7100>
Content-Type: Application/SDP
User-Agent: Hikvision
Content-Length: 162
v=0
o=33010602001310019325 0 0 IN IP4 10.64.49.44
s=Play
c=IN IP4 10.64.49.218
t=0 0
m=video 5514 RTP/AVP 96
a=rtpmap:96 PS/90000
a=sendonly
y=0060205514
主叫端收到200 OK,向被叫端返回ACK确认连接:
ACK sip:130909113319427420@10.64.49.218:7100 SIP/2.0
Via: SIP/2.0/UDP 10.64.49.44:7100;rport;branch=z9hG4bK3589109049
From: <sip:130909115229300920@10.64.49.44:7100>;tag=868569348
To: <sip:33010602001310019325@10.64.49.218:7100>;tag=3330812776
Call-ID: 2074790969
CSeq: 20 ACK
Contact: <sip:130909115229300920@10.64.49.44:7100>
Max-Forwards: 70
User-Agent: Hikvision
Content-Length: 0
主叫端结束通话 BYE:
BYE sip:130909113319427420@10.64.49.218:7100 SIP/2.0
Via: SIP/2.0/UDP 10.64.49.44:7100;rport;branch=z9hG4bK2928302365
From: <sip:130909115229300920@10.64.49.44:7100>;tag=868569348
To: <sip:33010602001310019325@10.64.49.218:7100>;tag=3330812776
Call-ID: 2074790969
CSeq: 21 BYE
Contact: <sip:130909115229300920@10.64.49.44:7100>
Max-Forwards: 70
User-Agent: Hikvision
Content-Length: 0
被叫端返回200 OK:
SIP/2.0 200 OK
Via: SIP/2.0/UDP 10.64.49.44:7100;rport=7100;branch=z9hG4bK2928302365
From: <sip:130909115229300920@10.64.49.44:7100>;tag=868569348
To: <sip:33010602001310019325@10.64.49.218:7100>;tag=3330812776
Call-ID: 2074790969
CSeq: 21 BYE
User-Agent: Hikvision
Content-Length: 0
3、MESSAGE示例
以上面的流程为例:
步骤1:User1发送MESSAGE请求到代理服务器。
MESSAGE sip:user2@domain.com SIP/2.0
Via: SIP/2.0/TCP user1pc.domain.com;branch=z9hG4bK776sgdkse
Max-Forwards: 70
From: sip:user1@domain.com;tag=49583
To: sip:user2@domain.com
Call-ID: asd88asd77a@1.2.3.4
CSeq: 1 MESSAGE
Content-Type: text/plain
Content-Length: 18
user2, come here.
步骤2:代理服务器转发User1的MESSAGE请求给USER2。代理服务器收到步骤1请求,到数据库中查找User2(注册过程中生成数据库),随后生成步骤2的数据。
MESSAGE sip:user2@domain.com SIP/2.0
Via: SIP/2.0/TCP proxy.domain.com;branch=z9hG4bK123dsghds
Via: SIP/2.0/TCP user1pc.domain.com;branch=z9hG4bK776sgdkse;
received=1.2.3.4
Max-Forwards: 69
From: sip:user1@domain.com;tag=49394
To: sip:user2@domain.com
Call-ID: asd88asd77a@1.2.3.4
CSeq: 1 MESSAGE
Content-Type: text/plain
Content-Length: 18
user2, come here.
步骤3:User2收到User1的消息后,回复200 OK给代理服务器。直接回应(200-OK) 没有Body,也不携带Contact头域。
SIP/2.0 200 OK
Via: SIP/2.0/TCP proxy.domain.com;branch=z9hG4bK123dsghds;
received=192.0.2.1
Via: SIP/2.0/TCP user1pc.domain.com;;branch=z9hG4bK776sgdkse;
received=1.2.3.4
From: sip:user1@domain.com;tag=49394
To: sip:user2@domain.com;tag=ab8asdasd9
Call-ID: asd88asd77a@1.2.3.4
CSeq: 1 MESSAGE
Content-Length: 0
步骤4:代理服务器转发200 OK回复给User1。代理服务器收到回复后,去掉最顶端的Via,转发如下消息给User1。
SIP/2.0 200 OK
Via: SIP/2.0/TCP user1pc.domain.com;branch=z9hG4bK776sgdkse;
received=1.2.3.4
From: sip:user1@domain.com;;tag=49394
To: sip:user2@domain.com;tag=ab8asdasd9
Call-ID: asd88asd77a@1.2.3.4
CSeq: 1 MESSAGE
Content-Length: 0
系列四将会对SIP的其他内容进行讲解。