1、SIP协议简单介绍:
一、SIP基本概念
1.1 定义
SIP(Session Initiation Protocol,会话发起协议)是由IETF(Internet工程任务组) 提出的IP电话信令协议, 是采用UTF-8字符集来进行编码的文本协议。
SIP是一种通信协议,定义了如何在通信设备(计算机,电话,手机,PDA等)之间相互连接和信息交换。
SIP是一种信令控制协议,可以配置和管理任何类型的 peer-to-peer 通信会话, 但并不关心媒体类型 (语音、短信、游戏、视频等)。
1.2 SIP实体
SIP协议定义了多个实体,理解它们在使用SIP协议的体系结构中所起的不同作用是至关重要的。
1.2.1 用户代理
用户代理(UA,User Agent)表示一个终端系统。它可以是SIP电话机或者电脑上的SIP软终端。它包括两部分,用户代理客户端(UAC,User Agent Client)和用户代理服务器端(UAS,User Agent Server),前者产生请求,后者产生对应的响应。
UAC和UAS是逻辑上的两个部分,每个终端系统都包含了UAC和UAS的功能。
图1.2.1 一个简单的SIP呼叫的例子
如图1.2.1所示,Tesla发起INVITE(请求),Marconi接收INVITE请求,因此,此时Tesla就是用户代理客户端 (UAC),Marconi是用户代理服务器端(UAS);会话建立后,Marconi发起BYE(结束)请求,Tesla发送对应的响应,因此,此时 Marconi就是用户代理客户端(UAC),Tesla是用户代理服务器端(UAS)。
1.2.2代理服务器
代理服务器(Proxy)是将请求消息路由到UAS以及将相应消息路由到 UAC的实体。一个请求消息在到达UAS之前可能要经过若干个代理服务器的转发,每个代理服务器都要进行路由决策,并在将请求信息转发到下一个实体之前对 其进行修改。响应消息将遍历请求信息所经的那些服务器,但顺序却完全相反。
代理服务器是一个逻辑SIP实体。当一个请求消息到来时,一个能作为代理服务器的SIP实体首先决定是否需要由自己来应答这个请求,例如请求消息中 可能有格式错误,或者在执行代理功能之前需要先获得客户端的鉴权证书等,而该实体亦可用任何适当的错误码来响应。SIP实体直接应答一个请求时,它承担的 角色就是UAS。
按照工作模式,代理服务器分为有状态代理服务器(Stateful Proxy)和无状态代理服务器(Stateless Proxy)两种类型(《SIP揭密》中分得更细,分为保留呼叫状态PROXY,保留状态PROXY和无状态PROXY)。
无状态代理服务器只是作为一个简单的消息转发实体,它根据请求消息来做转发目的地和路由决策,然后把请求转发到下游的某个实体;对于响应消息,则只简单地将其往上游方向转发。一旦消息转发完毕,无状态代理服务器将丢弃所有与此消息相关的信息。
有状态代理服务器会记住它所收到的每个请求的信息,如事务状态,以及作为某一请求的处理结果而发送的任何请求的信息。这些信息将影响它对后续的、与先前接收的某一请求相关的信息的处理。
1.2.3重定向服务器
重定向服务器(Redirect Server)是一个SIP实体,它接受用户代理或代理服务器的请求,对这些请求发送3xx(重定向响应)响应,响应消息中包含了请求的目标用户的可能地址的列表,以便用户代理或代理服务器重新发送请求消息。
1.2.4注册服务器
注册服务器(Registrar Server)是一个接收注册的SIP服务器,用以进行管理以及特定的服务。一个注册服务器通常伴同一个重定向服务器或者一个代理服务器同时出现。
1.2.5定位服务器
定位服务器(Location Server)不是SIP实体,但是它们是任何实用SIP协议的体系结构中非常重要的一部分。位置服务器存储并且向用户返回可能的位置信息。它可以利用从 注册服务器或者其他数据库得来的信息。大部分的注册服务器接收到位置信息时即刻将这些信息上载到定位服务器。但是,在定位服务器和SIP服务器之间并不实 用SIP协议。一些定位服务器实用轻量目录访问协议(LDAP,Lightweight Directory Access Protocol)[RFC1777]和SIP服务器进行通信。
1.3 SIP方法(method)
在SIP的REQUEST中,核心的方法(method)定义了6种:INVITE、ACK、BYE、CANCEL、OPTIONS和REGISTER。
INVITE消息用于发起一个新的会话;
ACK消息用于完成会话的建立;
BYE消息用于结束一个会话;
CANCEL消息用于取消一个请求(一般是针对INVITE);
OPTIONS消息用于查询服务器的能力;
REGISTER消息用于发送注册请求消息。
除了以上方法以外,还有其他扩展的方法,如INFO、NOTIFY等等。
1.4 SIP响应
响应消息的起始行为状态行(Status-Line),状态行由协议版本、状态码和状态原因短语组成,各个部分之间用一个空格字符进行分隔。下面介绍其中的状态码。
SIP协议中共定义了6类状态码,其中状态码的第1位数字用于指示响应类型,后两位数字表示具体响应。下面用“1xx”标识状态码为“100-199”之间的响应。
1xx:临时响应,表示请求消息正在被处理;
2xx:成功响应,表示请求已被成功接收,完全理解并被接受;
3xx:重定向响应,表示需采取进一步以完成该请求;
4xx:客户机错误,表示请求消息中包含语法错误信息或服务器无法完成客户机请求;
5xx:服务器错误,表示服务器无法完成合法请求;
6xx:全局故障,表示任何服务器无法完成该请求;
二、SIP Message
2.1 Message Struct
SIP消息分为请求和响应两类,其中请求消息是UAC(客户端)发往UAS(服务器端),响应消息是UAS发往UAC。但是,不管请求消息还是响应消息,它都是由一个起始行、若干个头字段和一个消息体组成,其中的消息体是可选。SIP消息的格式如下所示:
起始行
消息头部(若干个头字段)
空行
消息体(SDP)
其中的起始行对于请求是请求行,对于响应是状态行。
下面分别给出请求消息和响应消息的完整的例子。
一个完整的SIP请求消息如下所示:
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
|
Call-ID: 123456789@lab.high-voltage.org
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
对应完整的200OK的响应消息:
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
From: Nikola Tesla
Call-ID: 123456789@lab.high-voltage.org
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
2.2 Requests
2.2.1 请求行
请求消息的起始行为请求行(Request-Line)。请求行的格式如下所示,由方法名、请求URI和协议版本组成,各部分之间均用一个空格字符进行分隔。除此之外,请求行必须用回车换行字符表示行终结。请求行的格式如下:
格式:Request-Line = Method Request-URI SIP-Version
Method:方法表示请求的类型,核心的类型有6种,INVITE,ACK,BYE,CANCEL,REGISTER和OPTIONS
Request-URI:请求的URI表示此请求将要被发送的目标地址
SIP-Version:一般为SIP/2.0
例子:INVITE sip:Bob.Johnson@company.com SIP/2.0
2.2.2 请求头字段
见2.4节。
2.2.3 消息体
见2.5节。
2.3 Responses
2.3.1 状态行
响应消息的起始行为状态行(Status-Line),状态行由协议版本、状态码和状态原因短语组成,各个部分之间用一个空格字符进行分隔。状态行的格式如下所示:
格式: Status-Line = SIP-Version Status-Code Reason-Phrase
SIP-Version:一般为SIP/2.0
Status-Code:该参数为一个3为的十进制整数,来报告事物的状态,如早先描述的,状态码是从100~699的整数并且分成代表不同意义的6类
该参数用于对Status-Code参数进行简单的文本描述
例子:SIP/2.0 180 Ringing
2.3.2 响应头字段
见2.4节。
2.3.3 消息体
见2.5节。
2.4 Header Fields
头字段提供了关于请求(或应答)的消息和关于这些消息所包含的消息体的信息。一些头字段可以在请求或应答两种消息中使用,而其他的头字段只能单独特定地用于请求(或应答)。头字段由头字段名、后边跟着一个冒号、再后面跟着头字段值组成。头字段格式如下所示:
field-name:field-value
核心协议中定义的SIP头字段如下表所示:
Where | Header fields | Exam |
可出现在请求与响应中
| Alert-info 通知信息 | Alert-Info: |
Allow 允许 | Allow: INVITE, ACK, OPTIONS, CANCEL, BYE | |
Call-ID 呼叫标识 | Call-ID: 34a5d553192cc35@15.34.3.1 | |
Contact 联系 | Contact: sip:bell@telephone.com | |
Content-disposition 内容部署 | Content-Disposition: session | |
Content-encoding 内容编码 | Content-Encoding:gzip | |
Content-language 内容语言 |
| |
Content-length 内容长度 | Content-Length:349 | |
Content-type 内容类型 | Content-Type: application/sdp | |
Cseq 命令序列 | CSeq: 1 INVITE | |
From 源 | From: tag=3342436 | |
Organization 组织 | Organization: MCI | |
Retry-after 此后重试 | Retry-After: 3600 | |
Record-route 记录路由 | Record-Route:
| |
Subject 主题 | Subject: More good info about SIP | |
Supported 支持 | Supported: rel100 | |
To 目标 | To: sip: babage@engine.org; tag=2443a8f7 | |
Via 通过 | Via: SIP/2.0/UDP 100.101.102.103; branch=z9hG4bK776a | |
只出现在请求中
| Accept 接受 | Accept: application/sdp |
Accept-encoding 接受的编码 | Accept-Encoding: text/plain | |
Accept-language 接受的语言 | Accept-Language: en | |
Authorization 授权 | Authorization: Digestusername="Cust1",realm="company.com", nonce="9c8e88df84f1cec4341ae6e5a359", opaque="",uri="sip:user2@company.com", response="e56131d19580cd833064787ecc" | |
Call-info 呼叫信息 | Call-Info: m/my_picture.jpg>;purpose=icon | |
In-reply-to 在答复中 | In-Reply-To: a8-43-73-ff-43@company.com | |
Max-forwards 最大转发次数 | Max-forwards: 70 | |
Priority 优先级 | Priority: emergency | |
Proxy-authorization 代理授权 | Proxy-Authorization: Digest username="Customer1", realm="company.com", nonce="9c8e88df84f1cec4341ae6e5a359", opaque="", uri="sip:user@company.com", response="e56131d19580cd833064787ecc" | |
Proxy-require 代理需求 | Proxy-Require: timer | |
Require 需求 | Require: rel100 | |
Route 路由 | Route: | |
只出现在响应中 | Authenticaton-Info 鉴别信息 | Authentication-Info: rspauth="9105jr98li459jgfp" |
Proxy-authenticate 代理鉴别 | Proxy-Authenticate: Digest realm="example.com", nonce="9c8e88df84f 1cec4341ae6e5a359", opaque="", stale=FALSE, algorithm=MD5 | |
Unsupported 不支持 | Unsupported: rel100 | |
Warning警告 | Warning: 302 proxy "Incompatible transport protocol" | |
WWW-authenticate WWW鉴别 | WWW-Authenticate: Digest realm="example.com", nonce="9c8e88df84f1 cec4341ae6e5a359", opaque="", stale=FALSE, algorithm=MD5 |
下面重点介绍以下头字段:
Via:
Via头字段指定目前请求消息经过的路径,同时指定响应也要按该路径返回。该字段值中的branch ID参数是一个事务标识符,代理服务器用它来检测环路。
Via头字段包含一个用来发送消息的传送协议和客户端的主机名或者网络地址,该头字段还可能包含一个接收响应的端口号。本字段还可以包含下列参 数:"maddr"、"ttl"、"received"和"brance"。具体实现时,brance参数的值必须从"z9hG4bk"这个字符串开始。
Via头字段的缩写形式为v,例如:
Via: SIP/2.0/UDP erlang.bell-telephone.com:5060;brance=z9hG4bK87asdks7
Max-forwards:
在RFC3261中规定,Max-Forwards(最大转发次数)头字段必须和任何方法一起规定向下游转发消息的代理服务器和网关的个数。当某客户端沿着某条链路发送请求消息的时候,使用该字段可以有效地防止链路中出错或者发生回环。
Max-forwards头字段的值是一个0-255的整数,指示了该请求还允许被转发的次数。转发该请求时,每经过一个服务器该值就减一。一般默认为70。例如:
Max-Forwards: 70
To:
该头字段指定了请求的逻辑接收者。"display-name"参数用于人机接口,为可选。"tag"参数一般用于标识对话。
To头字段等价性的判定同From头字段。它的缩写形式为t。例如:
To: The Operator
t: sip:+12125551212@server.phone2net.com
另外,将一个请求的To头字段和Request-URI区分开来非常重要。在整个会话过程中,To头字段含有同样的内容,它是打算用于远端用户代理的。它不能被代理改变。
而Request-URI含有在信令路径中下一跳的地址,并因此在路途中被每个代理改变。
From:
From头字段用于指示请求的发起者,也就是发送请求(不是对话)的源地址。这可能与对话的发起者并不同。当被叫发出请求时,From字段中就是使用被叫的地址。From的缩写形式是f。例如:
From:"A. G. Bell"
f: Anonymous
Call-ID:
Call-ID头字段唯一的标识某个客户端的某个特定的会话或所有的注册请求。一个多媒体会议可以发起几个Call-ID不同的呼叫,例如,一个发 起者A可以邀请用户B参与一个会议,随后又邀请用户C参与同一个会议,那么,A与B有一个Call-ID,A与C有另一个Call-ID。那么,A发送 BYE请求时,就是通过不同的Call-ID来选择结束那个会话。Call-ID区分大小写并逐字节比较。缩写形式为i。例如:
Call-ID:f81d4fae-7dec-11d0-a765-00a0c91e6bf6@biloxi.com
i:f81d4fae-7dec-11d0-a765-00a0c91e6bf6@192.0.2.4
Cseq:
命令序列头字段Cseq位于请求消息中,包含两个字段:一个无符号整数字段和一个方法名。该头字段用于把某对话中的事务进行排序且提供了一种唯一标 识某事务的方法(即INVITE、ACK等method),并能够区分某请求是新的请求还是从发的请求。如果两个Cseq的数字序列以及方法都相等那么这 两个Cseq就是等价的。
例如:Cseq: 4711 INVITE
200 OK中的Cseq:完整地复制对应请求中的Cseq,不做任何修改;
ACK中的Cseq:和对应的最终响应有相同的整数序列,只是方法名变为ACK;
CANCEL中的Cseq:和对应的请求(一般是INVITE)由相同的整数序列,只是方法名变为CANCEL;
如下图:
Contact:
Contact字段的值含有一个URI,UA可根据这个地址,直接找到另一个UA,从而避开SIP服务器。
例如:Alice通过SIP Proxy呼叫Bob。Alice发送“INVITE sip:bob@biloxi.com SIP/2.0”到Proxy,中间过程不用管它,在Bob送回的200 OK的响应里包含了Contact字段“Contact:
sip:bob@192.0.2.4 SIP/2.0”到Bob。
Contact头字段的值中还可以包含一个显示名称、含有URI参数的URI和头字段参数。在RFC3261中定义了Contact参数“q”和“expires”。这些参数只用于REGISTER请求及其响应以及3xx响应。
当Contact头字段包含一个显示名称的时候,带有所有的URI参数的URI应放于三角括号<>中,否则,URI后面的参数都认为是头字段参数而不是URI参数。
解析显示名称、URI、URI参数以及头字段参数的规则同样适用于To和From头字段。Contact头字段的缩写是m(“moved”)。
例如:Contact:"Mr. Watson"
m:
Record-Route和Route:
Record-Route头字段由代理服务器插入请求消息中,这样可以使该对话中将来的请求仍能经过该代理服务器。举例如下:
Record-Route:
Route头字段有一个代理服务器列表,用来指定请求消息的路由。举例如下:
Route:
2.5 Bodies(SDP)
请求和应答都可能含有消息体,它被一个空行和消息头分开。被SIP消息携带的消息体通常是会话描述符(即SDP),但它也可以由任何不透明物体组成,它就象附件一样包含在SIP消息中。在这里我们主要介绍会话描述符。
2.5.1 SDP介绍
会话描述协议(SDP,Session Description Protocol)规定了对描述会话的必要信息怎样进行编码。两个SIP实体可以通过携带SDP消息体来使它们之间的多媒体会话达成一致。但SDP不支持媒体编码方案的协商,这些功能均由下层传送协议完成。
SDP用于构建INVITE和200 OK响应消息的消息体,供主\被叫用户交换媒体信息。
SDP包括以下一些方面:
1) 会话的名称和目的
2) 会话存活时间
3) 包含在会话中的媒体信息,包括:
媒体类型(video, audio, etc)
传输协议(RTP/UDP/IP, H.320, etc)
媒体格式(H.261 video, MPEG video, etc)
多播或远端(单播)地址和端口
4) 为接收媒体而需的信息(addresses, ports, formats and so on)
5) 使用的带宽信息
6) 可信赖的接洽信息(Contact information)
2.5.2 SDP语法
SDP会话描述是基于文字的,一个会话描述由一些类似如下形式的文字行组成:
Tyte=value
类型域为一个单独字符,而值域的格式则取决于它前面的类型语。一个SDP描述含有会话级信息和媒体级信息。会话级信息应用于整个会话。例如,它能成 为会话始发者或者会话的名字。媒体级信息作用于特殊的媒体流。例如,它能作为一个编码器给音频流编码或者是给视频流发送端口号。
一个SDP会话描述以会话级信息和媒体级信息开始,如果任意一个出现,另外一个就接着在后面出现。会话级部分以v=0开始,v代表类型,0为值,意思是协议版本号为0(SDP版本0)。接下来的行直到媒体流部分或者会话描述的终点,提供了整个会话的信息。
媒体级部分以m行开始。下面的行直到下一个m行出现,或者直到会话描述的终点,提供了特定媒体流的信息。
SDP具体语法如下:
会话描述:
v= (protocol version) //SDP版本,默认为0
o= (owner/creator and session identifier) //<用户名><会话id><版本><地址>
s= (session name) //会话名
i=* (session information) //会话信息
u=* (URI of description) //u=http://www.zte.com.cn/staff/sdp.ps
e=* (email address)
p=* (phone number)
c=* (connection information -如已经包含在所有媒体中则该行不需要)
b=* (bandwidth information)
z=* (time zone adjustments) //时区调整
k=* (encryption key) //<方法>:<密钥>或k=<方法>
a=* (zero or more session attribute lines) //a=<属性> 或a=<属性>:<值>
时间描述:
t= (time the session is active) //<开始时间><结束时间>,单位秒
r=* (zero or more repeat times) //<重复时间><活动持续时间
媒体描述:
m= (media name and transport address)//m=<媒体><端口><传送><格式列表>
i=* (media title媒体称呼)
c=* (connection information - 如已经包含在会话级描述则为可选)
b=* (bandwidth information)
k=* (encryption key)
a=* (zero or more media attribute lines)
注:v,o,s,t,m为必须的,其他项为可选。
如果SDP语法分析器不能识别某一类型(Type),则整个描述丢失;
如果"a="的某属性值不理解,则予以丢失
整个协议区分大小写
"="两侧不允许有空格
会话级的描述就是媒体级描述的缺省值
所有均格式为=
2.5.3 一个例子
v=0 //SDP版本
o=Tesla 2890844526 2890844526 IN IP4 lab.high-voltage.org //会话发起者是Tesla,地址是lab.high-voltage.org
s=Phone Call //会话名字是Phone Call
c=IN IP4 100.101.102.103 //接收地址是100.101.102.103
t=0 0 //在什么时候是激活的
m=audio 49170 RTP/AVP 0
//音频流,端口是49170,音频流在udp上通过RTP传输,
//值0意味音频是在单个信道中使用PCM u—law进行编码和以8kHz的频率采样
a=rtpmap:0 PCMU/8000
三、SIP协议行为
3.1 会话过程
UAC通过向服务器发送INVITE消息开始会话发起过程,该请求可能通过网络中间的服务器设备的转发,最终到UAS。如果UAS同意建立本次会 话,则返回2XX响应,如果不同意或者发生意外情况(重定向或者错误),则返回3XX、4XX、5XX、6XX响应。UAC对于这些最终响应产生ACK, 建立会话成功或者失败。在收到最终响应之前,UAS也可以发送临时响应(1XX)来通知UAC当前的处理进展情况。
在会话建立成功以后,任何一端UA可以通过发送re-INVITE请求来调整会话,修改会话的某些属性,例如:增加或删除媒体流,改变媒体发送或接受地址等。
当需要结束会话的时候,任何一端UA可以通过发送BYE请求,来结束会话。对端同意结束,则发送200 OK应答,结束会话成功。
图3.1 最基本的两个UA之间的SIP呼叫
最基本、最简单的SIP呼叫是两个UA之间之间的点对点呼叫。如上图所示。
下面就最基本的SIP呼叫,分别针对发起会话、调整会话和结束会话进行说明。
3.1.1 发起会话
图3.1.1 建立会话的三次握手
如图3.1.1所示,INVITE、200 OK、ACK三条消息为会话发起过程中的三次握手。三次握手过程的完成,唯一标识了会话的成功建立。
UAC的INVITE消息的产生
RFC3261规定,由UAC产生的一个有效的SIP请求消息必须至少包含下列头字端:Via、Max-Forwards、To、From、 CSeq和Call-ID头字端,不仅是在INVITE,它们在所有的SIP请求消息中都是必选的。这六个头字段是构建SIP消息的基本单元,它们共同提 供了大部分的关键的消息路由服务,包括消息的寻址、响应的路由、消息传播距离限制、消息排序,以及事务交互的唯一性标识等。另外,请求行 (Request-Line)也是必选的。
各头字段的说明见上文。具体地,请求行(Request-Line)中的Request-URI设置为与To头字段一值(除REGISTER以外的 所有请求的初始Request-URI都应该与To头字段一值);Via头字段设置为响应消息将要被发送的地址,其brance参数值在时间和空间上也必 须唯一(即不同的method有不同brance值,两种情况除外,CANCEL与对应的INVITE的brance值相同,对非2xx的最终响应的 ACK与对应的INVITE的brance值相同);Max-Forwards一般设置为70;To头字段包含了请求消息的逻辑接收者,可以是SIP或 SIPS URI,其tag值标识了对话中的对端,在INVITE消息中不应该出现;From头字段设置为消息发送者的地址,其tag值标识了消息的发起 者;Cseq设置为不同于其他事务的整数序列和INVITE方法;Call-ID设置为全局时间上和空间上都唯一的ID号;Contact头字段设置为 UAC(请求发送者)的地址。
另外,INVITE消息中可以包含SDP消息,这时Content-Type头字段的值应该是application/sdp。SIP协议中规定了 关于媒体协商的过程是通过在消息中携带SDP来完成的。关于SDP协商的过程需要遵循如下规定:INVITE消息中携带SDP请求,则200 OK消息中返回SDP响应;200 OK消息中携带SDP请求,则ACK消息中返回SDP响应。
SDP请求和响应这个协商过程,不能并行,只能当一次交互完成之后才能发起新的协商过程。
UAS的行为
INVITE消息根据Via头字段中的地方,发送消息。当UAS收到消息后,可以按以下几种进行:正在处理,发送1XX;重定向,UAS希望将此请求重新发送到另一地址,发送3XX;拒绝,发生了某种错误,发送4XX或5XX或6XX;接受,发送2XX。
UAS构建响应消息时,按照如下方法:构建状态行(Status-Line);复制收到请求的Via头字段,做部分修改;复制To头字段,添加 tag值(即UAS的标识);完全复制From头字段,Cseq头字段和Call-ID头字段;Contact头字段修改为UAS的地址。
如果INVITE请求中携带了SDP请求,则在200OK消息中携带SDP响应。
ACK的发送
对于最终响应(2XX,3XX,4XX,5XX,6XX),UAC将构建ACK消息。此时的ACK消息的构建方法与INVITE大体一 致:Request-Line中的Request-URI应设置为200OK中的Contact头字段的URI;Via头字段的brance值不同于 INVITE;Max-Forwards、To、From、Call-ID与INVITE相同(To中包含了tab标识);Cseq中的整数序列与 INVITE相同,method为ACK。
一个例子(图3.1.1)
1.INVITE MESSAGE
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
From: Nikola Tesla
Call-ID: 123456789@lab.high-voltage.org
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
2.180 Ringing MESSAGE
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
From: Nikola Tesla
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 INVITE
Contact:
Content-Length: 0
3.200 OK MESSAGE
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
From: Nikola Tesla
Call-ID: 123456789@lab.high-voltage.org
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
4.ACK MESSAGE
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
From: Nikola Tesla
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 ACK
Content-Length: 0
|
图3.1.2 调整会话
在会话建立后,呼叫方或被叫方可以发送一条新的INVITE消息,来调整、修改已经建立的会话的参数信息。在一个现存对话中发出INVITE请求就是re-INVITE。其过程与发起会话的过程类似,如上图3.1.2所示。
re-INVITE请求的产生
re-INVITE过程所采用的SDP协商过程与建立会话的INVITE过程相同:会话中的任意一方可以通过在re-INVITE消息中携带一个新的SDP请求来更新会话内容;或者,re-INVITE可以不携带SDP,让其对方在200OK中携带SDP。
re-INVITE消息中的To、From、Call-ID、Cseq和Request-URI头字段的生成方法采用通用的请求消息的生成规则。
re-INVITE不会被分岔(分岔成为多份INVITE,发送到不同地址),因此只可能收到一个最终应答(不会分岔的原因是,会话已经建立,那么re-INVITE消息中的Request-URI将是目标UA的地址,会准确无误地送到)。
re-INVITE过程不能重叠,如果已经有一个re-INVITE事务正在执行,就不能发起新的re-INVITE事务。
对于re-INVITE事务的ACK和2XX响应的生成,与初始INVITE过程相同。
UAS的处理
UAS收到re-INVITE消息后,必须检查其中的SDP是否更改,并对相应的会话参数做出调整。如果新的媒体描述不可接受,UAS可以返回 488(Not Acceptable Here)拒绝响应。这个响应应当包含一个Warning头域(用来提供给请求方,提供这个拒绝的原因)。
如果UAS返回了2XX响应,但是没有受到ACK,它必须发送BYE来结束本次对话。
一个例子
在例子3.1.1的基础上,发起一个增加视频请求的re-INVITE,消息如下:
1.INVITE MESSAGE
INVITE sip:marconi@tower.radio.org SIP/2.0
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4Bk412kg
Max-Forwards: 70
To: G. Marconi
From: Nikola Tesla
Call-ID: 123456789@lab.high-voltage.org
CSeq: 2 INVITE
Subject: Request for video…
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= video 53000 RTP/AVP 32
a= rtpmap:32 MPV/90000
2.200 OK MESSAGE
SIP/2.0 200 OK
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch= z9hG4Bk412kg
;received=100.101.102.103
To: G. Marconi
From: Nikola Tesla
Call-ID: 123456789@lab.high-voltage.org
CSeq: 2 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= video 61000 RTP/AVP 32
a= rtpmap:32 MPV/90000
3.ACK MESSAGE
ACK sip:marconi@tower.radio.org SIP/2.0
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4bK432km
Max-Forwards: 70
To: G. Marconi
From: Nikola Tesla
Call-ID: 123456789@lab.high-voltage.org
CSeq: 2 ACK
Content-Length: 0
|
图3.1.3 结束会话
会话的中止可以通过对INVITE请求返回拒绝响应来完成,或者对已建立的会话发送BYE请求等方式来完成。对于非2XX的最终响应,UAC收到此响应后,发送ACK中止会话。对于BYE请求,下面略作介绍。
UAC行为
会话中的任意一方可以通过发送BYE请求来结束已经建立的会话。BYE请求的生成与通用的请求消息的生成规则相同。
BYE请求对应一个新的事务。UAC发送BYE请求之后即认为本次会话已经结束了。
UAS行为
UAS收到BYE请求之后,需要查询匹配的会话。如果找不到则返回481响应。如果找到对应的会话,UAS必须结束该会话。然后对BYE返回2XX响应。对于正在处理的请求消息,UAS返回487响应。
一个例子
1. BYE MESSAGE
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
From: G. Marconi
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 BYE
Content-Length: 0
2. 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
From: G. Marconi
Call-ID: 123456789@lab.high-voltage.org
CSeq: 1 BYE
Content-Length: 0
3.2 请求的取消
图3.2 请求的取消
CANCEL用于取消客户端发送的前一个请求。当要求UAS终止对被取消请求的处理时,UAS需对那个请求产生一个错误响应(一般是487,请求终 止)。对于UAS已经给出了最终响应的请求,CANCEL是无效的。因此,CANCEL主要用于取消服务器端需要长时间才能给出响应的请求。CANCEL 请求最适合于INVITE请求,因为它会需要很长的时间来产生响应。在这种情况下,如果UAS收到了针对INVITE的CANCEL请求,但还没有为之发 出最终响应,则UAS将“停止振铃”,然后用一个特定的错误响应(一个487)来应答INVITE。
代理服务器和UAC都可以产生并发送CANCEL请求。
有状态的代理服务器应当应答收到的CANCEL,而不是简单地转发从下游实体收到的响应。因此,由于CANCEL“跳”到每一个有状态的代理服务器时都会被应答,所以它是个“逐跳”(hop-by-hop)的请求。
3.2.1 客户端行为
CANCEL请求不应当用于取消非INVITE请求(非INVITE请求一般都会立即被响应)。
下列过程用于构造一个CANCEL请求消息:CANCEL请求的Request-URI,Call-ID,To,CSeq的数字部分,以及From 头字段,包括各个标签,都必须与被取消请求的对应部分相同。客户端产生的CANCEL必须只能有一个Via头字段值,并与被取消请求的顶端Via值相匹 配。这些头字段值的一致性使得CANCEL请求能与被取消的请求相匹配。但是,CSeq头字段中“method”部分的值必须是“CANCEL”,以保证 CANCEL请求能够被识别并在一个它自己的事务中被处理。
如果被取消请求包含一个Route头字段,CANCEL请求中必须包含此Route头字段的值(这个要求是为了让无状态代理服务器能够正确地路由CANCEL请求)。
CANCEL请求不能包含任何Require或Proxy-Require头字段。
一旦CANCEL请求构造完毕,客户端应当检查是否已收到了被取消请求(下文将统称为原请求)的任何响应消息(临时的或最终的)。如果还没有收到临 时响应消息,那么CANCEL请求不能发送,而是一直等待,直到一个临时响应到来(如果允许在收到响应消息之前发送CANCEL请求,那么服务器端有可能 在收到原请求之前先收到CANCEL请求)。如果原请求已经产生了一个最终响应,由于CANCEL对已经产生了最终响应的请求是无效的,那么CANCEL 请求也不应当发送。当客户端决定要发送CANCEL时,它为该请求创建一个客户端事务,并将CANCEL消息和目的地址、端口、以及传输方式一起传递给新 创建的事务。CANCEL请求的目的地址、端口和传输方式必须与原请求的相同。
3.2.2 服务器端行为
CANCEL方法请求服务器端的事务用户(TU)取消一个未决的事务。事务用户假定原请求的方法是除了CANCEL或ACK之外的任何一个,并进行事务匹配,匹配到事务将被取消。
服务器如何处理CANCEL请求依赖于服务器的类型。无状态代理服务器将简单地转发;有状态代理服务器会会其做出应答并产生自己的CANCEL请求;UAS将对CANCEL请求做出应答。
UAS首先按照“对话中的请求”的处理的基本规则对CANCEL进行处理。由于CANCEL请求是逐跳处理的并且不能重传,所以UAS不能对它们发 出质询(Challenge)来得到在Authorization头字段中携带的证书(Credentials)。CANCEL请求不包含Require 头字段。
然后,UAS对CANCEL请求进行事务匹配。如果依据上述的过程没有找到匹配的事务,那么UAS应当用一个481(事务或呼叫方不存在)来应答 CANCEL请求。如果匹配到了原请求的事务,那么UAS的处理行为将取决于是否已经发送了对原请求的最终响应。如果最终响应没有发送,那么UAS的处理 方式将取决于原请求的类型。如果原请求是一个INVITE,UAS应立即对它发送487(请求终止)响应。而对任何其他的请求方法,CANCEL请求对它 们的事务处理都不起作用。
不管原请求的类型是什么,只要CANCEL匹配到了一个现存的事务,UAS都要用一个200OK响应来应答CANCEL请求。这个响应要遵循“对话 中的请求”的响应的过程构造,注意CANCEL响应的To标签应当与原请求响应的To标签相同。CANCEL的响应消息被传递给一个服务器端事务以便发 送。
3.2.3 一个例子(图3.2)
1.INVITE MESSAGE
INVITE sip:marconi@radio.org SIP/2.0
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4Bk5619b
Max-Forwards: 70
To: G. Marconi
From: Nikola Tesla
Call-ID: 987654321@lab.high-voltage.org
CSeq: 5 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
2.180 Ringing MESSAGE
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch= z9hG4Bk5619b
;received=100.101.102.103
To: G. Marconi
From: Nikola Tesla
Call-ID: 987654321@lab.high-voltage.org
CSeq: 5 INVITE
Contact:
Content-Length: 0
|
CANCEL sip:marconi@tower.radio.org SIP/2.0
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4Bk5619b
Max-Forwards: 70
To: G. Marconi
From: Nikola Tesla
Call-ID: 987654321@lab.high-voltage.org
CSeq: 5 CANCEL
4.200 OK MESSAGE
SIP/2.0 200 OK
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4Bk5619b
To: G. Marconi
From: Nikola Tesla
Call-ID: 987654321@lab.high-voltage.org
CSeq: 5 CANCEL
5.487 MESSAGE
SIP/2.0 487 Request Terminated
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4Bk5619b
To: G. Marconi
From: Nikola Tesla
Call-ID: 987654321@lab.high-voltage.org
CSeq: 5 INVITE
6.ACK MESSAGE
ACK sip:marconi@tower.radio.org SIP/2.0
Via: SIP/2.0/UDP lab.high-voltage.org:5060;branch=z9hG4Bk5619b
To: G. Marconi
From: Nikola Tesla
Call-ID: 987654321@lab.high-voltage.org
CSeq: 5 ACK
3.3 代理服务器行为
SIP代理服务器是将请求消息路由到UAS的以及将响应消息路由到UAC的实体。一个请求在到达UAS之前可能要经过如干个代理服务器的转发,每一 个代理服务器都要进行路由决策,并在将请求消息转发下一个实体前对其进行修改。响应消息将遍历请求消息所经的那些服务器,但顺序却完全相反。
代理服务器是一个逻辑SIP实体。当一个请求消息到来时,一个能作为代理服务器的SIP实体首先决定是否需要由自己来应答这个请求,例如请求消息中 可能有格式错误,或者在执行代理功能之前需要先获得客户端的鉴权证书等,而该实体亦可用任何适当的错误码来响应。SIP实体直接应答一个请求时,它承担的 角色就是UAS。
对每个新请求,代理服务器既可以在有状态模式下工作,也可以在无状态模式下工作。当以无状态模式工作时,代理服务器只是作为一个简单的消息转发实 体,它根据请求消息来做转发目的地和路由决策,然后把请求转发到下游的某个实体;对于响应消息,则只简单地将其往上游方向转发。一旦消息转发完毕,无状态 代理服务器将丢弃所有与此消息相关的消息。有状态代理服务器会记住它所收到的每个请求的信息,如事务状态,以及作为某一请求的处理结果而发送的任何请求的 信息。这些信息将影响它对后续的、与先前接收的某一请求相关的消息的处理。有状态代理服务器可能选择“分叉”(fork)转发一个请求,即将一个请求向多 个目的地路由。任何被转发到多个地点的请求都必须在有状态模式下处理。
有些情况下,代理服务器可能采用有状态的传输方式(比如TCP)来转发请求,这时不必保留事务状态。例如,代理服务器可能在不保留事务状态的情况下 将一个请求从一个TCP连接转发到另一个TCP连接,只要它在请求消息中放置足够的信息,使它能把对应的响应消息沿着接收请求所用的那条TCP连接往回转 发。当请求消息在不用类型的传输方式之间转发时,代理服务器必须采用有状态模式,代理服务器的事务用户必须保证消息的可靠传输。
一个有状态的代理服务器可能在请求处理过程中的任何时候转到无状态工作模式下,只要它之前无任何禁止这种转换的动作,如分叉代理或产生了100响应等动作。在做这样的转换时,所有的事务状态信息都将被丢掉。代理服务器不应主动发起CANCEL请求。
3.3.1 代理服务器的两种类型
RFC3261中规定,代理服务器分为有状态代理服务器和无状态代理服务器两种类型。
有状态代理服务器
一个工作在有状态模式下的代理服务器就是一个SIP事务处理引擎。有状态代理服务器的行为模型可根据客户端事务和服务器端事务的有关定义而建立。一 个有状态代理服务器包含一个服务器端事务,一个或多个客户端事务,以及将它们关联起来的一个被称为代理服务器核心(见下图)的高层处理模块。从外部收到的 请求由服务器端事务来处理。处理之后的请求送给代理服务器核心层,核心层决定将请求路由到何处,选择一个或多个下一跳位置。发往每个下一跳位置的请求消息 都由一个它自己的客户端事务进行处理。核心层从客户端事务收集响应,并依据这些响应来向服务器端事务发送响应。
图3.3.1 有状态代理服务器模型
有状态代理服务器为每个收到的请求创建一个新的服务器端事务。当立即回送临时性响应消息(如100Trying)到服务器端事务时,代理服务器核心必须作为UAS进行处理。有状态代理服务器不应当对非INVITE请求产生100响应。
无状态代理服务器
当作为无状态的时候,proxy就是一个简单的消息转发者。很多无状态的处理步骤和有状态的时候很类似。不同的地方在下边描述。
无状态代理不保存任何状态信息。它们接收一个请求,将它发往下一跳,并且立即删除与那个请求相关的所有状态信息。当无状态代理收到一个应答时,它仅仅基于通过Via头字段的分析来决定路径,并且它不为之维持状态。
一个无状态的proxy并没有事务的概念,或者用于描述有状态proxy行为的应答上下文。相反的是,无状态的proxy处理消息,无论是请求还是 应答,都是直接从通讯层处理的。当然,无状态proxy自己也不重发这些消息。他们只是转发他们收到的任何重发的消息(他们本身并没有能力来分辩那些消息 是重发的,那些消息是原始消息)。进一步说,当无状态的处理一个请求的时候,这个节点并不产生它自己的100(Trying)或者其他临时应答。
3.3.2 代理服务器的具体行为
在实际应用中,多数代理服务器都是有状态代理服务器。下面介绍的具体行为就是针对有状态代理服务器。
对所有新请求,包括任何方法未知的请求,一个执行代理功能的实体必须:
¨ 确认请求的有效性;
¨ 预处理路由信息;
¨ 确定请求发送的目的地;
¨ 将请求向每个目的地转发;
¨ 处理所有的响应消息;
下面就针对这些行为进行具体介绍。
1. 确认请求的有效性
一个实体在进行代理服务器之前必须确认请求的有效性。一个有效的请求消息必须通过如下的检查:
¨ 合理的语法:任何与请求的有效性相关的消息内容,或者与请求的转发相关的消息内容,其构造必须正确。对任何其他的消息内容,在消息转发时应忽略其语法构造 是否正确并保持原样。例如,SIP实体不会因为一个格式错误的Date头字段而拒绝一个请求。同样,代理服务器在转发一个请求之前也不会删除一个格式错误 的Date头字段;
¨ URI方案:如果一个请求的Request-URI使用了代理服务器不能理解的URI方案,代理服务器应当用一个416(不支持的URI方案)响应拒绝该请求;
¨ 最大转发数:检测Max-Forwards头字段。当有此头字段,并且其值等于零,那么SIP实体不可以转发此请求;并且,如果请求方法是 OPTIONS,那么SIP实体可以作为消息的最终接收者进行响应,否则,SIP实体必须返回一个483(跳数过多)响应;
¨ 回路检测(可选):当代理检测到一个循环时,发送一个482(检测到路由循环)响应,具体检查过程在此不作介绍;
¨ Proxy-Require:此头字段列出了终端希望代理服务器支持的扩展,如果代理不理解此头字段的扩展能力,SIP实体必须返回一个420(错误的扩展)响应。该响应中必须包含一个Unsupported头字段,其中列出那些不能被理解的选项标签;
¨ Proxy-Authorization:如果SIP实体在转发一个请求之前需要鉴权证书,则需要先进行消息检查。
如果任何一项检查失败,代理服务器必须象UAS那样处理,并用一个错误码来应答。
2. 预处理路由信息
代理服务器必须检查请求消息的Request-URI。如果请求消息的Request-URI是它以前插在Record-Route头字段中的值, 代理服务器则必须用Route头字段的最后一个值替换Request-URI,并且从Route头字段中删除该值。在这之后,代理服务器必须按修改过的请 求来进行处理。
上述情况只有当发送请求到代理服务器(可能是一个端点)的SIP实体为一严格路由服务器时才会发生。为了获得与这类实体的后向兼容性,此时对接收消息的修改是必要的。这也使得遵从RFC3261的实体能够在通过严格路由的代理服务器时保留Request-URI。
如果Request-URI中包含maddr参数,代理服务器必须检查其值是否在自己所负责的地址集或主域集中。如果在其中,且对应的请求消息是通 过Request-URI中指定或缺省的端口和传输方式接收的,代理服务器必须去掉maddr参数和所有非缺省的端口及传输方式参数,并按照没有这些参数 的请求消息来进行处理。
代理服务器收到的某个请求消息中可能有与该服务器相匹配的maddr参数,但接收该消息时所用的端口和传输方式却与Request-URI中指定的不同。这样的请求需要被转发到使用Request-URI中指定的端口和传输方式的代理服务器。
如果请求消息中Route头字段的第一个值指示的就是本代理服务器,那么它必须将该值从请求消息中删除。
3. 确定请求发送的目的地
代理服务器计算请求消息的发送目的地。发送的目标地址集可以由请求消息的内容预先决定,也可以是从某个抽象的定位服务获得。地址集中的每个目的地都被表示成一个URI。
如果请求的Request-URI中包含一个maddr参数,Request-URI必须被作为唯一的目标URI放在目标地址集中。
如果Request-URI多指示的域不是代理服务器所负责的域,Request-URI必须被作为唯一的发送目的地放进目标地址集中,然后该实体 必须对该消息进行转发。(许多情况下代理服务器都可能收到Request-URI所指主域不归自己负责的请求消息。比如处理外拨呼叫的防火墙代理服务器, 就是这种情况可能发生的一个例子)
如果目标地址集不能通过上述的方法确定,当前SIP实体则负责Request-URI中的主域,并且该实体可以用任何机制来决定向何处转发请求。不 论是哪中机制,其模型都可以化为访问一个抽象定位服务的过程。可能的机制有:从SIP注册服务器创建的某个定位服务获得信息;读去数据库;查询一个 presence服务器;使用其它协议;或者执行一个简单的算法对Request-URI进行替换,等等。当访问一个由注册服务器构件的定位服务时,必须 首先对Request-URI进行规范化,然后才能把它作为查询索引。这些机制的输出结果被用于构造目标地址集。
如果Request-URI没有提供足够的信息使得代理服务器能够决定目标地址集,代理服务器应当返回一个485(不明确)响应。响应中应当包含一个Contact头字段,其中列出了可供尝试的新地址的URI。
任何在请求消息中,或者与请求消息相关的,或者实体的当前环境相关的信息都可能用于目标地址集的构建。例如,根据某些头字段和消息体是否出现及其内 容如何,或请求消息到达的时间、或接收请求所用的接口、或以前请求消息的失败情况,甚至实体当前的可用性级别等等,可能会构造出不同的目标地址集。
通过上述定位服务确定了可能的目标地址之后,它们的URI就被加入到目标地址集中。目标地址只能被放置到目标地址集一次。根据URI相等的定义,一个目标URI已经在目标地址集中出现,那么它就不能被再次加入该地址集。
如果原请求的Request-URI所指定的资源位置不在本代理服务器负责的范围之内,代理服务器不得在目标地址集中加入其他地址。(代理服务器只有在请求的Request-URI是由自己所负责的情况下,才能在消息转发时改变这个URI)
如果代理服务器负责原始请求的Request-URI所指定的资源,则在请求转发开始之后它可能继续向目标地址集加入地址。它可以使用处理过程中获 得的任何信息来决定新的目标地址。比如,代理服务器可以将一个重定向响应(3XX类响应)中获得的Contact URI并入目标地址集中。如果代理服务器在构造目标地址集时使用了动态信息源(比如查询一个SIP注册服务器),它应当在请求处理过程中监视所有信息源的 变化。如果有新的可用地址,那就应当将其加入目标地址集中。同样的地址决不能被多次加入。
如果在Request-URI中指定的某个资源不存在,代理服务器必须返回一个404(找不到)响应。
按上述所有的方法处理之后,目标地址集仍然为空,代理服务器就必须返回一个错误响应,并且应当是480(临时不可用)响应。
4. 请求的转发
只要目标地址集非空,代理服务器就可以开始转发请求。
对每个目标地址,代理服务器按如下步骤转发请求(具体操作见RFC3261):
¨ 复制收到的原时请求;
¨ 更新Request-URI;
¨ 更新Max-Forwards;
¨ 添加一个Record-Route头字段值(可选);
¨ 添加其他头字段(可选);
¨ 路由信息后处理;
¨ 确定下一跳得地址、端口和传输方式;
¨ 添加一个Via头字段值;
¨ 必要时加入一个Content-Length头字段;
¨ 转发新请求;
¨ 设置定时器C;
5. 响应的处理
当一个SIP实体收到响应时,首先它会查找与响应消息相匹配的客户端事务。如果没有找到,那么该实体必须作为一个无状态代理服务器来处理这个响应。如果事务相匹配,那么这个响应由所匹配的事务来处理。
客户端事务把响应消息传给代理服务层之后,必须执行下述处理(具体操作见RFC3261):
¨ 查找正确的响应上下文;
¨ 对临时响应更新定时器C;
¨ 删除最顶端Via头字段值;
¨ 把响应消息加到响应上下文中;
¨ 检查响应是否需要立即转发;
¨ 需要时,从响应上下文中选取最优的最终响应;
¨ 必要时汇聚所有的authorization头字段值;
¨ 重写Record-Route头字段值(可选);
¨ 转发响应消息;
¨ 生成任何必要的CANCEL请求;
6. CANCEL的处理
当代理服务器转发一个最终响应,或者当它收到6XX响应时,它都必须对响应上下文的所有未决客户端事务发出一个CANCEL请求。另外,代理服务器在转发一个临时响应后,也可能收到UAC发送的CANCEL请求。
当收到CANCEL请求时,代理服务器必须取消与之匹配的响应上下文的所有未决客户端事务。
当CANCEL请求在由自己的服务器端事务处理时,有状态代理服务器不会为它创建新的响应上下文。反之,代理服务器从现有的响应上下文中查找与 CANCEL请求相关的那个请求所在的服务器端事务。如果找到了匹配的响应上下文,那么代理服务器必须立即对CANCEL请求返回一个200OK响应。这 种情况下当前实体就作为一个UAS。另外,代理服务器必须为响应上下文中的所有未决客户端事务产生CANCEL请求。
如果没有找到相匹配的响应上下文,当前实体没有任何与CANCEL请求对应的请求的信息,它必须以无状态方式转发这个CANCEL请求。
3.3.3总结
如果没有相反的本地策略,代理服务器对于包含Route头字段的请求所执行的处理可被总结为如下几步:
1。检查请求的Request-URI。如果它指示的是一个由当前代理服务器负责的资源,那么代理服务器将用从定位服务中得到的地址代替它。否则,代理服务器将不对它做改动。
2。代理服务器将检查最顶端Route头字段中的URI。如果它指向当前代理服务器,那么将其从Route头字段中删除。
3。代理服务器将把请求转发到最顶端的Route头字段值中URI所指的资源位置,如果没有Route头字段出现则转发到Request-URI所指的资源位置。
3.3.4 例子
1. 基本的SIP梯形
该示例是一个基本的SIP“梯形”结构,U1->P1->P2->U2,U1和U2表示终端,P1和P2表示代理服务器。两个代理服务器都要插入Record-Route头字段值。这里给出消息流程:
U1发送INVITE给P1:
INVITE sip:callee@domain.com SIP/2.0
Contact: sip:caller@u1.example.com
P1发送INVITE给P2:
INVITE sip:callee@domain.com SIP/2.0
Contact: sip:caller@u1.example.com
Record-Route:
说明:P1是一个外拨代理服务器,它不负责domain.com,所以它进行DNS查询并将请求发往那里。它还在请求中加了一个Record-Route头字段值
P2发送INVITE到U2:
INVITE sip:callee@u2.domain.com SIP/2.0
Contact: sip:caller@u1.example.com
Record-Route:
Record-Route:
说明:P2收到了这个请求,它负责domain.com,因此它运行定位服务并重写Request-URI。它也加入了一个Record-Route头字段值。因为请求中没有Route头字段,所以它对新的Request-URI进行解析以确定向何处转发请求。
U2发送200OK到P2:
SIP/2.0 200 OK
Contact:sip:callee@u2.domain.com
Record-Route:
Record-Route:
说明:位于u2.domain.com的被叫方收了请求,完全复制请求中的Record-Route列表;用本机地址更新Contact头字段。这个200响应沿着P2->P1->U1的路径正常回传。
此时,U2将其对话状态信息中的远端目标URI设为sip:caller@u1.example.com并将其路由集设为:(
U1发送BYE到P1:
BYE sip:callee@u2.domain.com SIP/2.0
Route:
说明:既然路由集中的所有元素都包含lr参数,则需要添加Route头字段。
P1发送BYE到P2:
BYE sip:callee@u2.domain.com SIP/2.0
Route:
说明:P1注意到自己不负责Request-URI所指的资源位置,因此它不改变其内容。它也确实发现自己是Route头字段的第一个值,所以它将该值删除,并将请求转发到P2。
P2发送BYE到U2:
BYE sip:callee@u2.domain.com SIP/2.0
说明:P2注意到它不负责Request-URI所指的资源位置(它负责的是domain.com,而不是u2.domain.com),因此它也 不改变Request-URId的值。P2也确实发现自己是Route头字段的第一个值,所以将该值删去,并通过对Request-URI进行DNS查询 将BYE请求转发到u2.domain.com。
2. 穿过一个严格路由的代理服务器
该示例中,一个对话通过4个代理服务器建立,每个代理服务器都加入了Record-Route头字段值。第三个代理服务器实现了严格路由过程。请求消息的流程如下:
U1->P1->P2->P3->P4->U2
到达U2的INVITE请求包含:
INVITE sip:callee@u2.domain.com SIP/2.0
Contact: sip:caller@u1.example.com
Record-Route:
Record-Route:
Record-Route:
Record-Route:
U2回应以200OK。之后,U2基于第一个Route头字段值发送如下的BYE请求给P4:
BYE sip:caller@u1.example.com SIP/2.0
Route:
Route:
Route:
Route:
P4不负责Request-URI所指的资源,因此保留其值不变。它注意到自己是Route头字段的第一个值所指实体,所以将之删除。然后P4根据 当前的第一个Route头字段值sip:p3.middle.com准备发送请求,但它注意到这个URI中没有lr参数,因此在发送之前,它将请求消息变 为如下格式:
BYE sip:p3.middle.com SIP/2.0
Route:
Route:
Route:
P3是一个严格路由的代理服务器,它将如下请求转发到P2:
BYE sip:p2.example.com SIP/2.0
Route:
Route:
P2看到Request-URI是自己以前放到Record-Route头字段中的值,所以在进一步处理之前,将请求消息改写为:
BYE sip:caller@u1.example.com SIP/2.0
Route:
P2检查不负责u1.example.com,所以它根据Route头字段值的解析结果将请求消息发送P1。
P1注意到自己被列在最顶端的Route头字段值中,因而它将该值删除,消息变为:
BYE sip:caller@u1.example.com SIP/2.0
既然P1不负责u1.example.com,并且消息中也没有Route头字段,P1根据Request-URI将请求消息转发到u1.example.com。
3.4 重定向服务器行为
一般地,重定向服务器负责通过数据库查询或定位服务来查询某个用户,它不转发任何请求和响应消息。下图是重定向服务器的典型的应用。
图3.4 一个重定向呼叫
1.UAC发INVITE请求到Redirect Server:
INVITE sip:bob@acme.com SIP/2.0
Via: SIP/2.0/UDP sip.acme.com:5060;branch=z9hG4bK54532
Max-Forwards: 70
To: Bob
From: Alice
Call-ID: 129ads@sip.acme.com
CSeq: 1 INVITE
Subject: Where are you exactly?
Contact:
Content-Type: application/sdp
Content-Length: 159
v=0
o=alice 2890844526 2890844526 IN IP4 100.101.102.103
s=Phone Call
t=0 0
c=IN IP4 100.101.102.103
m=audio 49172 RTP/AVP 0
a=rtpmap:0 PCMU/8000
2.Redirect Server经过查询,得知bob的实际地址是“3573572@gw.telco.com”,返回302响应:
SIP/2.0 302 Moved Temporarily
Via: SIP/2.0/UDP sip.acme.com:5060 ;branch=z9hG4bK54532
To: Bob
From: Alice
Call-ID: 129ads@sip.acme.com
CSeq: 1 INVITE
Contact: sip:3573572@gw.telco.com
Content-Length: 0
3.UAC收到302响应后,首先发送一个ACK到Redirect Server;随后,从302响应中获得bob可能存在的地址“3573572@gw.telco.com”,重新构建INVITE,并发送:
INVITE sip:3573572@gw.telco.com SIP/2.0
Via: SIP/2.0/UDP sip.acme.com:5060;branch=z9hG4bK92313
Max-Forwards: 70
To: Bob
From: Alice
Call-ID: 54-67-45-23-13
CSeq: 1 INVITE
Subject: Where are you exactly?
Contact:
Content-Type: application/sdp
Content-Length: 159
v=0
o=alice 2890844526 2890844526 IN IP4 100.101.102.103
s=Phone Call
t=0 0
c=IN IP4 100.101.102.103
m=audio 49172 RTP/AVP 0
a=rtpmap:0 PCMU/8000
4.之后的呼叫如正常流程一样。
3.5 定位服务器行为
定位服务器不是SIP实体,但它经常与代理服务器或重定向服务器结合在一起使用,用以返回目标用户可能的位置信息。3。4节重定向服务器的例子就是定位服务器的一个典型应用。
3.6 注册服务器行为
注册服务器为特定地区的位置服务创建绑定关系,这个绑定关系是用来建立包含一个或者多个联系地址的address-of-record URI(记录地址,一个address-of-record(AOR)是一个SIP或者SIPS URI,它指向了一个具有定位服务的主机,这个主机可以把URI映射成为用户真正物理位置的URI。一个AOR经常被认为是一个用户的”公共地址”)。因 而,当那个地区的proxy接收到一个请求,这个请求的Request-URI和address-of-record的记录匹配,那么这个proxy会转 发请求到这个address-of-record中登记的联系地址中去。
绑定关系是通过REGISTER请求(登记服务)来创建并更新的。
3.6.1 构造一个REGISTER
REGISTER请求用来增加、删除、查询绑定资料。一个REGISTER请求可以增加一个address-of-record和一个或者多个联系 地址之间的绑定。除了特别说明以外,REGISTER请求的构造以及客户端如何发送REGISTER请求和通常的UAC发出请求是一致的。
下面对REGISTER请求中的头字段进行简要说明。这些头字段中,除了Contact是可选的以外,其他头字段都是REGISTER请求要求必须包含的。
¨ Request-URI:这个头域指明了登记服务所指明的位置服务所在的区域(比如sip:chicago.com);
¨ To:这个头域包含了被查询、增加、修改的address-of-record。to头域和Request-URI头域通常是不同的,因为这个由用户名组成;
¨ From:这个头域包含了提交这个注册信息的用户的address-of-record资料。这个值和To头域的值相同,除非这个请求是第三方发起的注册请求;
¨ Call-ID:UAC发出的给某个注册服务器(registrar)的所有注册请求都应该有相同的Call-ID头域值。如果相同的客户端用了不同的 Call-ID值,注册服务器(registrar)就不能检测是否一个REGISTER请求由于延时的关系导致了故障;
¨ Cseq:Cseq值保证了REGISTER请求的正确顺序。一个UA为每一个具备相同的Call-ID的REGISTER请求顺序递增这个Cseq字段;
¨ Contact:REGISTER请求可以有一个Contact头域。这个头域可以有0个或者多个包含绑定地址信息的值;
UA在没有收到上一个注册请求的应答或者上一个REGISTER请求超时之前,禁止发送新的注册请求(就是说,包含一个新的Contact头域值,而不是重发)。
Contact头字段参数“expires”在REGISTER请求中有特别的意义:它表明UA的绑定的有效时间。以秒为单位的整数。如果本参数没有制定,那么这个参数的值就是Expires头域的值。
3.6.2处理REGISTER请求
一个注册服务器(registrar)就是一个UAS,这个UAS用来响应REGISTER的请求,并且维持一个绑定表,这个绑定表用来提供给它所管理的区域中的proxy服务器和重定向服务器的。一个注册服务器禁止产生6xx应答。
一个注册服务器必须知道(例如,通过配置)它所管理的区域。注册服务器一定需要按照接收到的REGISTER请求顺序进行处理。
REGISTER请求必须当作原子请求来处理,意味着给定的REGISTER请求要么就完整处理,要么就完全拒绝。每一个REGISTER信息的处理都和其他的注册和绑定信息的处理无关。
当接收到一个REGISTER请求,注册服务器(registrar)按照如下步骤处理:
¨ 注册服务器(registrar)检查Request-URI来决定是否它属于本注册服务器所管理的区域的Request-URI。如果不是,并且如果这个服务器同时也作为一个proxy服务器,那么这个服务器应当转发这个请求到指定的区域。
¨ 为了保证注册服务器能够支持所需要的扩展,注册服务器必须遵循rfc3261中的规定来处理Require头域。
¨ 一个注册服务器应当对UAC进行身份认证。
¨ 注册服务器应当检查认证的用户是否通过认证来更改这个address-of-record的登记权限。
¨ 注册服务器(registrar)从REGISTER请求的To头域中解出address-of-record。如果这个address-of- record并非在这个Request-URI指明的区域中合法,那么注册服务器必须发出一个404(没有找到)的应答,并且跳过后续步骤。接着URI必 须转换成为标准的格式。所有的URI参数都必须删去(包括用户参数user-param),并且任何非法(escaped)字符必须转换成为合法字符 (unescaped)格式。最后形成一个可以用于绑定的列表。
¨ 注册服务器(registrar)检查是否请求包含了一个Contact头域。如果没有包含,它跳过到最后一步。如果Contact头域包含了,注册服务 器检查是否有一个Contact头域值是”*”,并且包含了一个Expires头域。如果请求有其他的Contact头域或者任何有效期的值是非0的,这 个请求就是非法请求,并且服务器必须送回一个400(非法请求)的应答,跳过后续步骤。如果没有,那么注册服务器检查是否Call-ID复核每一个绑定的 值。如果不符合,它必须删除绑定。如果复核,它必须仅仅删除保存的绑定表中CSeq值小于请求中的Cseq值的记录。否则,更新必须终止,请求失败。
¨ 现在注册服务器(registrar)可以依次处理Contact头域中的联系地址了。对于每一个地址,它根据下边的规则进行有效期检查:如果含 有”expires”参数,这个参数值就是最终的有效期;如果没有这个参数,并且请求有一个Expires头域,那么这个值就是有效期;如果没有 Expire头域也没有参数,那么本地的缺省配置应当作为有效期。
¨ 注册服务器(registrar)返回一个200(OK)应答。这个应答必须包含Contact头域,并且这个头域的值中列举了所有当前绑定的注册信息。每一个Contact值都必须包含一个”expires”参数,用来标志还有多久这个绑定信息就过期了。
3.6.3 一个例子
一个最简单的REGISTER请求如下图所示。
UAC发送REGISTER请求到Registrar:
REGISTER sip:registrar.munich.de SIP/2.0
Via: SIP/2.0/UDP 200.201.202.203:5060;branch=z9hG4bKus19
Max-Forwards: 70
To: Werner Heisenberg
From: Werner Heisenberg
;tag=3431
Call-ID: 23@200.201.202.203
CSeq: 1 REGISTER
Contact: sip:werner.heisenberg@200.201.202.203
Content-Length: 0
Registrar发送200 OK到UAC:
SIP/2.0 200 OK
Via: SIP/2.0/UDP 200.201.202.203:5060;branch=z9hG4bKus19
To: Werner Heisenberg
From: Werner Heisenberg
;tag=3431
Call-ID: 23@200.201.202.203
CSeq: 1 REGISTER
Contact:
Content-Length: 0
附录:参考
[1]RFC3261(SIP)
[2]RFC2327(SDP)
[3]SIP揭密
[4]Understanding the Session Initiation Protocal