SIP里面的To和From字段是用来显示请求的方向,而不是消息的方向,方向是从请求方指向服务方。
上图显示了两个启用了SIP的设备之间的 SIP 消息交互。 这两个设备可以是 SIP 电话、 手持设备、 掌上电脑或手机。 它假定两个设备已经连接到 IP 网络比如互联网,并且已经知道彼此的 IP 地址。
主叫方Tesla通过发送的一条SIP INVITE给被叫方Marconi来启动信息交互。 在 这条INVITE请求消息中包含了关于主叫方请求的会话或呼叫类型方面的细节。 它可以是一个简单的语音 (音频) 会话、类似于视频会议的多媒体会话或者它可能是一个游戏会话。
这条INVITE 消息包含以下内容:
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4bKfw19b
Max-Forwards: 70
To: G. Marconi
From: Nikola Tesla ;tag=76341
CSeq: 1 INVITE
Subject: About That Power Outage...
Contact:
Content-Type: application/sdp
Content-Length: 158
v=0
o=Tesla 2890844526 2890844526 IN IP4 lab.high-voltage.org
s=Phone Call
c=IN IP4 100.101.102.103
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000
因为SIP消息是基于文本编码的协议,所以这使得SIP消息看起来像UDP数据报在以太网上传输那样的在线传输。
INVITE消息中列出来的区域被称为头部区域。
它们都有着这样的形式:
头标记:值 CRLF。
第一行被称为开始行,该行标记了一种称为INVITE的方法,后面跟着的是请求的URI(Request-URI),最后是SIP版本号码2,它们之间使用空格来加以区分。SIP消息的每一行都用过CRLF来终结。请求的URI是SIP URI的一种特殊形式,它指明了请求要被发送到的资源,它也被称作请求目标。
第二行的第一个字段是VIA,
每一个SIP设备产生或者转发一条SIP消息的时候都会在Via字段里面加上自己的地址
,一般都是可以通过DNS解析的IP地址。Via字段包含了SIP版本 2.0,紧跟一个“/”,之后的UDP表示通过UDP进行传输,然后接着一个空格,接着是主机名或者IP地址,接着分号,最后是端口值。在上面的这个例子中是通用的SIP端口号5060。SIP的传输采用TCP、UDP、TLS和SCTP。端口号将在章节后面些的内容进行描述。
Branch参数是一个传输标记符。针对这条SIP信息的后续响应可以被相互关联上就是因为它们包含一样的传输标记。
下一行的头标记是Max-Forwards,它被初始化为一个整数值,每个SIP服务器在接受和转发这个请求的过程中都会增加这个值,这个将简化环回检测。
下一行就是To和From行了,它们标识了SIP请求的发起者和目标。如同本例一样,在名字标签被使用的情况下,SIP URI就被放在了括弧内,它将被使用来路由请求。在提醒过程中,名字标签将会被使用,但是却不会被协议本身所使用。
Call-ID行是用来保持对特定SIP会话进行记录的标识符。SIP请求的发起者创建了本地唯一的字符串,然后通常会添加@和它的IP地址以便让该标识全球唯一。针对Call-ID,会话中的每一方都会贡献一个随机的标识符。这些标识符在每一次呼叫中都不一样。这些标识符被称为tag-(标签),在每一个会话建立之后,这些标签会被包含在To和From字段。最初的INVITE中包含了一个From 标签,但是在To中没有标签。
用户代理(User Agent)产生一条INVITE来建立会话,同时也产生了唯一的Call-ID和From标签。回应这个INVITE的用户代理也将产生一个标签求。最终本地标签(包含在From)、远程标签(包含在To)以及Call-ID三个合在一起来唯一地标识建立起的会话,也被称作“对话”,对话的标识符被参与会话的双方用来识别特定会话,因为在同一时间,在它们之间可能会建立很多的会话。在该建立好的会话之后的后续请求也将使用这个会话标示符。它们将会在下面的实例中展示。
下一行的头标记是CSeq,或者是command sequence(命令队列),它包含有一个数字,以及一个方法。在本例中是INVITE。在每一个新的请求被发送的时候,这个数字就会被增加。在本例中,它被初始化为1,但是也有可能从一个其它整数开始。
Via、Max-Forwards、To、From、Call-ID和CSeq构成了任何一条SIP请求语句里面的最小组成部分。其它的部分就可以作为可选附加信息或者针对于特别请求的必要信息。在这条INVITE消息里面,头标记Contact也是需要的,因为它包含了Tesla的通讯设备的SIP URI,也称作UA(用户代理),这个URI可以被使用来直接路由信息到Tesla。可选的头标记Subject(主题)也出现在这个例子里,它没有被协议所使用,但是却可以在振铃被叫方的时候显示出来以帮助被叫方决定是否接受这个呼叫。这点有点类似于电子邮件里面的From(发件人)和Subject(主题)。其它出现在这条INVITE消息内的头标记则包含了建立呼叫所必须的媒体信息。
Content-Type 和Content-Length头标记字段标识了消息体是SDP,并且包含了158个字节的数据。
关于158个字节的基本知识包含在了表2.1中。每行结尾的CRLF显示为??。每一行的字节数据值显示在右边。在消息主体和消息头部之间有一行空行把二者隔开。而消息头是以Content-Length结尾的。在本例中,有7行SDP数据描述了呼叫者Tesla希望建立呼叫的媒体属性。这些媒体信息是必须的,因为SIP不知道将要建立的媒体会话的类型,所以呼叫者必须指明它想建立会话的类型(音频、视频、游戏),SDP字段的名字在表2.2中,并且在7.1章节会讨论,但是我们将快速的预览一下必要的基本信息。
表2.1: Content-Length Calculation 例子
|
LINE
|
TOTAL
|
v=0??
|
05
|
o=Tesla 2890844526 2890844526 IN IP4 lab.high-voltage.org??
|
59
|
s=Phone Call??
|
14
|
c=IN IP4 100.101.102.103??
|
26
|
t=0 0??
|
07
|
m=audio 49170 RTP/AVP 0??
|
25
|
a=rtpmap:0 PCMU/8000??
|
22
|
|
158
|
Table 2.2: SDP 实例数据
|
SDP 参数
|
参数名称
|
v=0
|
Version number
版本号码
|
o=Tesla 2890844526 2890844526 IN IP4 lab.high-voltage.org
|
Origin containing name
原始包含名字
|
s=Phone Call
|
Subject
主题
|
c=IN IP4 100.101.102.103
|
Connection
连接
|
t=0 0
|
Time
时间
|
m=audio 49170 RTP/AVP 0
|
Media
媒体
|
a=rtpmap:0 PCMU/8000
|
Attributes
属性
|
表2.2包含了
连接的IP地址:100.101.102.103
媒体格式:音频
端口号:49170
媒体支持的协议:RTP
媒体编码:PCM μ Law
采样率:8000Hz
INVITE 只是SIP请求消息的一个例子,在RFC 3261和其它一些扩展RFC里共定义了5种方法或者其它的SIP请求。
图2.1中的另外一条消息是回应INVITE 的180 Ringing消息。这条消息说明了被叫方已经收到了INVITE并且提醒正在进行。提醒可能是振铃、在屏幕上显示一条消息,或者其它吸引被叫方Marconi注意的方法。
180 Ringing是SIP回应消息的一个例子。回应是数字化的并且由数字的第一个数字来分类。一条180回应是消息类的,通过第一个位数字为1来标识。消息类的回应被用来传递呼叫过程中的一些非关键的信息。很多SIP回应代码是基于HTTP 版本1.1的回应代码,但是做了一些扩展和增加。任何一位浏览过网页的用户在他们想要浏览的网页不存在的时候应该都接到过来自于WEB服务器的“404 Not Found”回应。404 NOT FOUND也是一个有效的SIP“客户端错误类”的回应,如果请求的是一位未知的用户,那么也会返回404。
在SIP中,单一的决定了回应方式的回应代码是由服务器或者用户来解释的。在本例中的回应,Ringing,就是标准的建议。但是可以使用任何文本来提示更多信息,比如说180,稍等,我将试图叫醒他,就既是一个非常合理的SIP回应,并且它有着和180 Ringing回应相同的意思。
180 Ringing回应有着如下的结构:
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4bKfw19b
;received=100.101.102.103
To: G. Marconi ;tag=a53e42
From: Nikola Tesla >;tag=76341
CSeq: 1 INVITE
Contact:
Content-Length: 0
该消息是大部分通过复制INVITE消息里面的内容来的,包含了Via、To、From、Call-ID和CSeq行,然后添加了一行包含有SIP版本号、回应代码、原因短语的回应行。这种方法简化了对回应的消息处理。
Via行
包含了原始的branch参数,
但是增加了额外的received参数
。
这个参数包含了IP地址表明了请求是由100.101.102.103所接受的
。
这个IP地址也是Via行里面的URI (lab.high-voltage.org)通过DNS所解析来的
。
注意在回应消息里面大家认为可能会被修改的To和From字段其实没有被修改。从消息里面看出,消息是从Tesla发送给Marconi,头部区域读取将读取相反的信息。这是
因为SIP里面的To和From字段是用来显示请求的方向,而不是消息的方向,方向是从请求方指向服务方。
因为Tesla发起了这个请求,所有的回应将读取To为Marconi,From为Tesla。
To行现在包含了一个由Marconi产生的标签,在这次会话或者对话的所有后续请求和回应都将包含由Tesla产生的标签和Marconi产生的标签。
这个回应包含了一个Contact行,里面包含了一个地址。一旦会话建立起来之后,通过里面这个地址,Marconi就可以被直接联系到。
当被呼叫的Marconi决定接受这个呼叫(比如接听呼叫),那么一条包含200 OK的回应将会被发送。这条回应同时也表明了呼叫发起者所提出的媒体会话类型是可以接受的。这条200 OK是成功类的回应的一个例子。 200 OK的消息体包含有Marconi的媒体信息:
SIP/2.0 200 OK
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4bKfw19b
;received=100.101.102.103
To: G. Marconi ;tag=a53e42
From: Nikola Tesla ;tag=76341
CSeq: 1 INVITE
Contact:
Content-Type: application/sdp
Content-Length: 155
v=0
o=Marconi 2890844528 2890844528 IN IP4 tower.radio.org
s=Phone Call
c=IN IP4 200.201.202.203
t=0 0
m=audio 60000 RTP/AVP 0
a=rtpmap:0 PCMU/8000
这条回应采用和180 Ringing回应一样的方式构建的,它也包含同样的To标签和Contact URI。但是媒体支持能力却一定要通过附加在SDP进行告知。和表2.2一样的SDP字段,该SDP包含:
端点IP地址: (200.201.202.203);
媒体格式 (audio);
端口 (60000);
媒体传输协议: (RTP);
媒体编码:(PCM μ-Law);
采样率 (8,000 Hz).
最后一步就是通过一个“确认”消息来确认媒体会话。确认意思就是Tesla成功的接到了Marconi的回应。媒体信息的交换可以让媒体会话使用其它协议来建立会话,在本例中是RTP。
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4bK321g
Max-Forwards: 70
To: G. Marconi ;tag=a53e42
From: Nikola Tesla ;tag=76341
CSeq: 1 ACK
Content-Length: 0
命令顺序-CSeq有着和INVITE一样的号码,但是方法却被设置成了ACK。到了这一点,媒体会话就将使用SIP消息上携带的媒体信息了。 媒体会话使用其它的协议开始了,典型的是RTP。Via行的branch参数包含一些不同于INVITE的新传输识别标识,因为确认的200 OK的ACK被认为是一个独立的传输。
这个信息交互展示了SIP是一个端到端的行令协议。一个SIP网络,或者SIP服务器是不要求被使用的协议的。两个运行SIP协议族的端点如果知道对方的IP地址的话就可以使用SIP来建立会话。虽然不是很直观,但是这个例子也展示了SIP协议的客户端-服务器的特性。当Tesla产生INVITE请求,它就是一个SIP客户端,当Marconi回应这个请求,它就是一个SIP服务器。当媒体会话建立好了之后,Marconi产生一个BYE请求,这个过程它又是一个SIP客户端,而Tesla回应这个请求的时候,它则成了为SIP服务器。这也就是为什么SIP服务器和SIP客户端必须同时都包含SIP服务器和SIP客户端软件,因为在一个典型的会话过程中,二者都是不可获取的。
这个特点不同于其它客户端-服务器端的HTTP或者FTP之类的Internet协议。WEB浏览器永远都是HTTP客户端,而WEB服务器永远都是HTTP服务端,FTP也是一样的道理。在SIP内,在会话过程中,一个端点将在客户端和服务器端进行来回切换。
在图2.1中Marconi发送了一个BYE请求来结束会话:
Via: SIP/2.0/UDP tower.radio.org:5060;branch=z9hG4bK392kf
Max-Forwards: 70
To: Nikola Tesla ;tag=76341
From: G. Marconi ;tag=a53e42
CSeq: 1 BYE
Content-Length: 0
本例中的Via行包含有Marconi的主机地址并且也包含了一个新的传输标示符,因为BYE被认为是不同于上面的INVITE和ACK传输的单独的传输。To和From行反应了这个请求时产生于Marconi。他们已经将上面的传输信息颠倒了过来。但是Tesla能够通过和INVITE中一样的本地标签、远程标签和CALL-ID识别出该会话,然后结束相应的媒体会话。注意例子中所有的branch ID都使用z9hG4bK字符串开始,这是一个特殊的字符串,它说明了branch ID是使用了RFC 3261中严格定义的规则计算而来,也说明作为一个传输识别标记,是可用的结果的[1]。
针对BYE请求的是一个200 OK的回应:
SIP/2.0 200 OK
Via: SIP/2.0/UDP tower.radio.org:5060;branch=z9hG4bK392kf
;received=200.201.202.203
To: Nikola Tesla ;tag=76341
From: G. Marconi ;tag=a53e42
CSeq: 1 BYE
Content-Length: 0
这个回应应答了原始请求的CSeq :1 BYE
[1]这个字符串是必须的,因为用户代理通过RFC 3261 所产生的branch ID有可能不适合来做传输标示符。在本例中,客户端必须使用To标标签、From标签、Call-ID和CSeq来创建自己的传输标识符