一个简单的SIP呼叫例子(中文版)

此文展示了SIP在两台SIP-enabled设备中的信令交换。这两个设备可以是SIP电话,手持设备,掌上电脑,或是手机。这里假设两个设备都连接到一个IP网络,如因特网。并且都知道对方的IP地址。

图:一个简单的SIP会话建立例子

Tesla作为呼叫方,通过发送一个SIP INVITE信令给被呼叫方Marconi开始了信令交换。INVITE信令包含了被请求的会话或是呼叫类型的细节部分。能够是一个简单的音频会话,一个多媒体会话甚至是一个视频会议,或者可能是一个游戏会话。

INVITE信令包含了以下字段:

INVITE sip:marconi@radio.org SIP/2.0
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4bKfw19b
Max-Forwards: 70
To: G. Marconi <sip:Marconi@radio.org>
From: Nikola Tesla <sip:n.tesla@high-voltage.org>;tag=76341
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 INVITE
Subject: About That Power Outage...
Contact: <sip:n.tesla@lab.high-voltage.org>
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信令的字段称为头部字段,结构为 Header: Value CRLF。请求信令的第一行称为开始行,列出了INVITE这种方式,Request-URI和SIP的版本号为(2.0),它们都以空格相隔开来。SIP信令的每一行都用一个CRLF结束。Request-URI是SIP URI的一种特殊形式,它表明了请求将要被发送到的地方,即请求目标。SIP URIs会在以下章节讨论更多的细节。

开始行接下去的第一个头部字段是一个Via。每个发送或是转发SIP信令的SIP设备都会在一个Via头部字段标记它自己的地址,地址通常为它的主机名,通过DNS询问可以把此主机名转换为IP地址。Via头部字段包含了SIP版本号(2.0),一个"/",一个UDP字符串(表明使用UDP协议传输),一个空格,然后是主机名或地址,再接着就是一个冒号,最后就是一个端口地址了,默认情况下是为5060。SIP的传输协议使用TCP,UDP,TLS和SCTP,而端口号的用途将会在这一章的后面涉及到。Branch参数是一个事务标识符。与请求相关联的响应之所以能被识别是因为它们都包含了相同的事务标识符。

下一个头部字段是Max-Forwards,它被初始化为一个大整数,并被每个接收和转发请求的SIP服务器递减,以提供简单的回路检测。

接下去的头部字段是To和From,分别表示了SIP请求的发起者和目的的。当使用一个名字标签时,就像这个例子,SIP URI就会被两个尖括号括起来,SIP URI用来路由请求。名字标签可以在信号提示期间被显示(也可以做为其它用途),但对协议来说并没有用处。

Call-ID头部字段是一个用来保持一个特定的SIP会话的标识符。请求的发起者创建了一个局部唯一的字符串,然后通常会在字符串后面加上一个"@"和它的主机名,以使它成为一个全局唯一的标识符。除了Call-ID之外,会话的各方都会贡献一个随机生成的标识符,这对于每次呼叫来说都是独一无二的。这些标识符被称为tag,当会话被建立时它们包含在To和From头部字段。初始化的INVITE信令只有From字段包含了tag,而To字段没有。

生成初始化INVITE信令以建立会话的用户端会产生一个唯一的Call-ID和From tag。作为对INVITE的回应,应答请求的用户端将会生成一个To tag。局部tag(包含在From头部字段中的),远程tag(包含在To头部字段中的)和Call-ID三个结合唯一地标识了建立的会话,即所谓的“对话”。这个对话标识符被对方双话所使用来标识这次呼叫,因为他们之间可能存在多次呼叫。在已建立的会话期间的请求都会使用这个对话标识符,这将会在接着的例子中被展示。

接下去的头部字段是CSeq,或者说命令序列。它包含了一个数字,紧接的是方法名,在这个例子里是INVITE。随着每个新的请求被发送,这个数字也会递增。在这个例子中,这个命令序列数字被初始化为1,但是它也可以被初始化为其它整数。

在一个最小的SIP请求信令中,仅仅需要Via,Max-Forwards,To,From,Call-ID和CSeq这几个头部字段。其它的头部字段都可以做为可选的附加信息,或是做为特殊请求类型需要的信息被包含进去。在这个INVITE信令当中,Contact头部字段也是必需的,它包含了Tesla的通信设备(即用户端UA)的SIP URI;这个URI能够把信令直接路由给Tesla。可选的Subject字段在当前例子也存在。协议并不需要这个字段,但是可以在信号提示期间被显示以让被呼叫方决定是否要接受这次呼叫。在电子邮件中,可以根据Subject和From这两个字段来对邮件优先级进行区分以及决定是否要被过滤掉,SIP INVITE请求包含的两个相同字段也可以被用来决定是否接受呼叫。这个INVITE信令存在其它附加头部字段,这些都包含了对于建立呼叫所必需的媒体信息。

Content-Type和Content-Length这两个字段分别说明了信令体是SDP,以及包含了158个字节的信息。关于详细信息都列在表1,注意是以字节为计数单位的。每行结尾的CRLF显示为©®,而每行的字节数都列在表1的右手边。信令体和头部字段列表用一个空白行隔开,字段列表最后一个为Content-Length。在这个例子中,一共有七行的SDP数据,它们描述了呼叫者Telsa所想要建立的呼叫的媒体属性。这些媒体信息不可缺少,因为SIP不能对要建立的媒体会话类型做出假设,所以呼叫者必须详细地指定它所要建立的会话的类型(音频,视频,游戏)。表2列出了SDP字段的名字,这里只做出一些简介,标明了各行在建立会话当中所起的作用。

表1:Content-Length计算例子

Content-Length

Calculation ExampleLINE

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

表2:例子中的SDP数据

SDP Parameter Parameter Name
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

表2包含了:
连接IP址地址(100.101.102.103);
媒体格式(音频)
端口号(49170);
媒体传输协议(RTP);
媒体编码(PCM μ Law)
采样率(8,000 Hz)
INVITE是SIP请求信令中的一种。目前SIP规格书RFC3267和其它扩展的RFCs除INVITE以外还总共定义了五种SIP请求类型或请求方式。文章中最前面的图的一个发送的信令是180 Ringing信令,其用来响应INVITE。这条信令表明了被呼叫方Marconi已经接受了INVITE,并且提示信号已经开始了。提示信号可以是一个电话响铃,也可以在屏幕中闪烁信息,或是其它能引起被呼叫方Marconi注意的方式。

180 Ringing是SIP响应信令的一种。响应信令的前面都是用数字表示的,并用数字的首位(即最高位)来对响应信令进行分类。180信令是属于“信息类”的响应信令,通过数字第一位的1可以标识出。信息响应用来传输关于呼叫过程那些不重要的信息。许多SIP响应码都是基于带有扩展和附加字段的HTTP version 1.1的。许多浏览过万维网的人都可能在请求页面不存在时得接到Web服务器返回的"404 Not Found"响应。404 Not Found也是SIP中一个合法的"客户端错误类型",当一个请求的被呼叫方是一个未知用户时,就会接收到此响应。

在SIP中的响应编号独自决定了响应的具体含义。从Ringing的意思可看出这个响应的含义,但也可以使用更多文本来传输更多的信息。例如,180 Hold your horses, I'm trying to wake him up!就是一个完美且合法的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 <sip:marconi@radio.org>;tag=a53e42
From: Nikola Tesla <sip:n.tesla@high-voltage.org>>;tag=76341
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 INVITE
Contact: <sip:marconi@tower.radio.org>
Content-Length: 0

这个信令从INVITE信令中复制了许多头部字段,包括Via,To,From,Call-ID和CSeq。但开始行不同,这里的开始行包含了SIP版本号,响应码和解释短语。这个方法简化了响应信令的处理过程(指从INVITE信令中复制相同字段)。

Via头部字段不仅包含了起始branch参数,还有一个附加的received参数。这个附加的参数包含了一个IP地址(也就是发起请求的地址,即呼叫方的IP地址),通过DNS解析Via中的URI可以得到与其相同的IP地址。

注意到响应信令中To和From头部字段的内容并没像期待的那样被交换。尽管这个信令是由Marconi发送给Tesla的,但是头部字段里却是相反的,这是因为To和From这两个字段表明的是发起请求的方向,而不是信令的发送方向。因为Tesla发起了这次会话,所以所有的响应里的To字段为Marconi,而From字段则为Tesla。

To字段在这时包括了一个由Marconi生成的tag。在这个会话或是对话中接下去的所有请求和响应都会包括这两个分别由Tesla和Marconi生成的tag。

响应还包含了一个Contact头部字段,它包含了一个地址,当会话被建立时,可以通过此地址直接联系Marconi。

当被呼叫方Marconi决定接受这次呼叫时,一个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 <sip:marconi@radio.org>;tag=a53e42
From: Nikola Tesla <sip:n.tesla@high-voltage.org>;tag=76341
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 INVITE
Contact: <sip:marconi@tower.radio.org>
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 Ring一样,并包含了一样的To tag和Contact URI。媒体要求必须通过附加在响应信令的SDP信令体进行协商。与表2列出的相同的SDP字段包含了:

端点IP地址(200.201.202.203);
媒体类型(音频);
端口号(60000);
媒体传输类型(RTP);
采样率(8,000 Hz)

最后一步是确认媒体会话,通过"acknowledgment"响应信令完成。确认意味着Tesla已经成功接收到Marconi的响应。通过媒体信息的交换,媒体会话可以使用另外一种协议来建立,此例是RTP协议。

ACK sip:marconi@tower.radio.org SIP/2.0
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4bK321g
Max-Forwards: 70
To: G. Marconi <sip:marconi@radio.org>;tag=a53e42
From: Nikola Tesla <sip:n.tesla@high-voltage.org>;tag=76341
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 ACK
Content-Length: 0

上面的命令序列CSeq跟INVITE有同样的数字,但是方式被设为ACK。在此刻,媒体会话开始使用SIP信令携带的媒体信息。媒体会话开始使用别的协议,通常为RTP。在via字段中的branch参数包含了一个比INVITE新的事务标识符,因为被发送的响应200 OK的ACK信令被认为是分开的事务。

这次信令交换展示了SIP是一个端到端的信号协商协议。使用此协议并不需要SIP网络,或者SIP服务器。只需要两个端点都运行SIP协议栈并且知道彼此的IP地址即可利用SIP来建立一个媒体会话(在两个端点之间)。虽然不明显,但是这个例子还是显示了SIP协议的本质是C/S。当媒体会话建立后,Marconi发送了BYE请求信令,这时候它处于客户端的地位,当Telsa响应时Telsa即处于SIP服务端的地位。这就是为什么一个SIP-enabled设备在典型的会话期间必须同时包含SIP服务端和SIP客户端软件。这跟其它C/S架构的因特网协议,如HTTP或者FTP有着很大的不同。Web浏览器总是一个HTTP客户端,而Web服务器总是一个HTTP服务器,FTP也与此差不多。而在SIP中,一个端点会反复交换角色。

在上图中,Marconi发送了一个BYE请求,结束了这个媒体会话:

BYE sip:n.tesla@lab.high-voltage.org SIP/2.0
Via: SIP/2.0/UDP tower.radio.org:5060;branch=z9hG4bK392kf
Max-Forwards: 70
To: Nikola Tesla <sip:n.tesla@high-voltage.org>;tag=76341
From: G. Marconi <sip:marconi@radio.org>;tag=a53e42
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 BYE
Content-Length: 0

在这个例子中,Via字段填充了Marconi的主机地址,还包含了一个新的事务标识务,这是由于BYE被认为与前面的INVITE或ACK是不同的事务。To和From字段反应了这次请求是由Marconi发起的,这跟之前的事务是相反的。但是Telsa可以通过跟INVITE一样的当地和远程tag和Call-ID来标识出对话,并正常结束这次媒体会话。

注意到到目前为止所有在例子中出现的brach IDs都是以字符串z9hG4bK开始。这是一个特殊的字符串,它表明branch ID已经被使用定义在RFC 3261中的严格的规则计算过,并做为一个有用的结果成为一个事务的标识符。

对于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 <sip:n.tesla@high-voltage.org>;tag=76341
From: G. Marconi <sip:marconi@radio.org>;tag=a53e42
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 BYE
Content-Length: 0

响应回应了CSeq的起始请求:1 BYE

标签:
文章分类 翻译作品
8 comments on “一个简单的SIP呼叫例子(中文版)
  1. ensuenesoft说道:

    t's such a great site. fabulous, very stimulating!!!

    -------

    Opony
    Pozycjonowanie

    opony

    [回复]

  2. Cool site, I hadn't noticed http://www.colaghost.com earlier in my searches!
    Carry on the good work!

    [回复]

  3. bo说道:

    文章讲解的比较清楚。

    [回复]

  4. xiaodbojue说道:

    nice

    [回复]

  5. chismi说道:

    很好的文章,学习了。

    [回复]

  6. Meero说道:

    must check for less for more detail

    [回复]

  7. xfygx说道:

    写的很不错!

    [回复]

1 Pings/Trackbacks 对 "一个简单的SIP呼叫例子(中文版)"
  1. [...] 关于协议基本上看的都是英文,不知道看了这么多英语功力有没有提升,还翻译了一篇关于sip的呼叫例子的文章放在了博客上yy,算是成果吧。目前的工作主要是关于voip方面的,看了几个开源包,仔细阅读了其中一个开源包的代码,基本上把整个设计都摸清了,这时候才感慨外国人做事之认真,基本上每个细节都考虑到,而且用c的功力也非一般人所能及的,竟然都是用面向对象的思想来实现的,感觉自己能看懂,但是目前肯定是写不出这种水平的代码。目前实现了一个测试用的二无sip软UA,无界面且无法运行在windows上。这是我的第一个Linux程序,当时还是挺兴奋的,代码理了一遍又遍,虽然也就那么六七百行代码。 [...]

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*