前面介绍了RFC3261里定义的六种SIP方法。接下来,我们看看SIP扩展的方法有哪些。
SUBSCRIBE
UA 使用SUBSCRIBE方法来建立订阅关系,以获取特定事件的通知(通过NOTIFY方法),SUBSCRIBE和NOTIFY都定义于RFC6665。订阅成功后在UAC和UAS间建立一个dialog。订阅请求包含一个Expires头域,它说明订阅存在的持续时间。期满之后,订阅关系自动终止。期满之前,可以再发一条SUBSCRIBE请求来刷新。服务端接受订阅时返回一条200 OK应答,它也包含一个Expires头域。限定的时间可以和请求一致,服务端可以缩短时间,不能延长时间。SIP中没有定义UNSUBSCRIBE方法,如果需要提前终止订阅,还是要通过SUBSCRIBE方法完成,Expires:0就表示终止。订阅终止时(不论是时间到期还是通过请求提前终止),都需要最后发一条NOTIFY消息来说明订阅已经终止。对SUBSCRIBE请求回应202 Accepted,并不表示订阅已经通过授权,它仅说明服务端已经了解订阅信息。
下图显示了订阅的基本流程。客户端发出SUBSCRIBE请求,收到成功应答。服务端事件触发时,客户端会收到NOTIFY。在订阅到期之前,客户端重新发SUBSCRIBE请求来展期,以便获知更多通知。
注意:客户端在收到SUBSCRIBE对应的200 OK应答之前,就应该准备好接收NOTIFY。此外,因为下游可能有分支,客户端必须准备好接收不同服务端的NOTIFY。尽管SUBSCRIBE只会收到一条对应的200 OK应答,但是,由于NOTIFY的To tag不同,因此需要建立独立的dialog。

SUBSCRIBE request请求消息实例:
SUBSCRIBE sip:ptolemy@rosettastone.example.com SIP/2.0
Via SIP/2.0/UDP proxy.elasticity.example.org:5060;branch=z9hG4bK348471123
Via SIP/2.0/UDP parlour.elasticity.example.org:5060;branch=z9hG4bKABDA ;received=192.0.3.4
Max-Forwards: 70
To: <sip:Ptolemy@rosettastone.example.com>
From: Thomas Young <sip:tyoung@elasticity.example.org>;tag=1814
Call-ID: 452k59252058dkfj34924lk34
CSeq: 3412 SUBSCRIBE
Allow-Events: dialog
Contact: <sip:tyoung@parlour.elasticity.example.org>
Event: dialog
Content-Length: 0
Event头域中说明订阅的事件类型,它是SUBSCRIBE请求的必要头域。每个SIP事件框架应用程序都通过一个唯一的事件tag定义数据包。每个数据包都定义了以下内容:
• 默认订阅到期时间
• 预期的SUBSCRIBE消息体
• 什么事件触发NOTIFY 的发送,以及NOTIFY中预期的消息体
• NOTIFY中包含完整的状态或增量
• 最大通知率
有一个协议叫PSTN与互联网互通(PSTN and Internet Interworking (PINT),RFC2848) ,它定义了SUBSCRIBE、NOTIFY和UNSUBSCRIBE方法,其语义与SIP相似。PINT请求中没有Event头域,服务器可以据此区分PINT SUBSCRIBE请求与SIP SUBSCRIBE。服务器应该通过Allow-Events头域说明它所支持的事件包。
如果在dialog内发SUBSCRIBE刷新但收到481 Dialog Does Not Exist应答,这说明服务端已经中止订阅。客户端应该认为dialog与订阅已经终止,如果需要,可以发SUBSCRIBE建立新的订阅dialog。
packet一种特殊的类型描述,它说明event消息里的事件类型。下表列出当前定义的SIP event与packet。
event packet name |
用途 |
规范 |
call-completion | Call Completion [8] | RFC 6910 |
certificate | Certificate (public key) [9] | RFC 6072 |
credential | Credential (private key) [9] | RFC 6072 |
conference | Conferencing [10] | RFC 4579 |
consent-pending-additions | Consent Framework [11] | RFC 5362 |
dialog | SIP Dialog Information [12] | RFC 4235 |
http-monitor | Web Page Monitoring [13] | RFC 5989 |
kpml | Key Press Markup Language[14] | RFC 4730 |
load-control | Load Filtering Policy [15] | RFC 7200 |
message-summary | Voicemail [16] | RFC 3842 |
poc-settings | Push-to-Talk over Cellular [17] | RFC 4354 |
presence | Presence [18] | RFC 3845 |
reg | Registration [19] | RFC 3680 |
refer | Refer [20] | RFC 3515 |
session-spec-policy | Session-Specific Policy [21] | RFC 6795 |
ua-profile | User Agent Profile (Configuration) [22] | RFC 6808 |
vq-rtcpxr | RTCP VoIP Summary [23] | RFC 6035 |
winfo | Watcher template [7] | RFC 3857 |
xcap-diff | Changes in XCAP Files [24] | RFC 5875 |
SUBSCRIBE请求的必要头域
Via |
To |
From |
Call-ID |
CSeq |
Max-Forwards |
Contact |
Event |
Allow-Events |
NOTIFY
UA使用NOTIFY方法来传递某种特定事件发生的信息。NOTIFY通常在订阅者与能和者之间建立的dialog内发送。然而,可以使用非SIP方法建立订阅(不发SUBSCRIBE请求),还可以通过其它SIP请求建立隐式的订阅(比如说,REFER请求所建立的隐式订阅)。因为它是dialog内请求,所以NOTIFY携带To tag, From tag, 和 Call-ID。
NOTIFY请求通常会收到200 OK应答,它说明消息已经被接收。如果收到481 Dialog/Transaction Does Not Exist应答,那么订阅自动终止,不再发后续NOTIFY消息。
NOTIFY请求包含一个Event头域,它说明事件包类型;此外,Subscription-State头域说明当前的订阅状态。Event头域携带的是订阅时指定的事件名。Subscription-State头域可选值是active, pending, 或 terminated。
订阅开始与结束时,总是要发一条NOTIFY消息。如果NOTIFY消息包含增量状态信息,那么消息体中应该有状态版本号,每发一条NOTIFY消息,这个版本号加1。通过这种方式,NOTIFY消息的接收方就能识别出信息丢失或接收乱序。
NOTIFY请求消息实例:
NOTIFY sip:tyoung@parlour.elasticity.example.org SIP/2.0
Via SIP/2.0/UDP cartouche.rosettastone.example.com:5060;branch=z9hG4bK3841323
Max-Forwards: 70
To: Thomas Young <sip:tyoung@elasticity.example.org>;tag=1814
From: <sip:ptolemy@rosettastone.example.com>;tag=5363956k
Call-ID: 452k59252058dkfj34924lk34
CSeq: 3 NOTIFY
Contact: <sip:ptolemy@cartouche.rosettastone.example.com>
Event: dialog
Subscription-State: active;expires=180
Allow-Events: dialog Content-Type: application/xml+dialog
Content-Length: ...
(XML Message body not shown...)
NOTIFY请求的必要头域:
To |
Via |
To |
From |
Call-ID |
CSeq |
Max-Forwards |
Event |
Allow-Events |
Subscription-State |