以对这个URIs,用任意顺序来产生新的请求。常见机制是通过在Contact头域的值中设置”q”参数来指定这个顺序。对这些URIs的请求可以是并行产生的也可以是串行产生的。有一个实现是按照q参数值递减的方法顺序处理URIs,并且对相同q参数值的URIs进行并行处理。还有一种就是直接按照顺序的方法处理URIs,对于q参数值不同的按照递减的顺序处理,对于q参数值相同的按照随机顺序处理。
如果发送给连接表上的地址失败了,(在下一段我们有定义),那么就选择列表中的下一个地址进行发送,直到列表全部遍历一遍。如果列表遍历完了还没有,那么请求就失败了。
通过响应码(大于399)我们可以知道请求的失败;对于网络错误来说,客户transaction会给transaciton user报告transport层的通讯错误错误。注意有一部分响应码(8.1.3.5)表示请求可以被重试;这个时候,请求可以重发而不是简单的当作一个错误。如果某个contact地址发送失败,那么client应该尝试下一个contact地址。这个会导致创建一个新的客户事务来处理这个新的请求。
为了在处理3xx应答中创建一个基于一个contact地址的请求,UAC必须首先从target set中拷贝除了”method-param”和”header”URI参数之外的整个URI到Request-URI(见参数的定义见19.1.1)。通过使用”header”参数来创建新请求的头域值,按照19.1.5节的指引,根据重定向的请求来重写头域的值。
注意在某些情况下,在contact地址中进行通讯所需要的头域,可能代替添加到现有的在原始转发的请求的请求头域中。作为通用的规则,如果头域可以接受用逗号分割的值列表,那么新的头域值可以添加到原始转发的请求的任何值后边。如果请求头域不接收多值列表,那么原始转发请求中的头域值可以由contact地址中进行通讯所需要的头域的值所替换。比如,如果contract地址返回了如下的值:
sip:user@host?Subject=foo&Call-Info=Http://www.foo.com
那么在原始转发请求中的任何Subject头域都被重写,但是HTTP URL仅仅添加到现存的Call-info 头域值中。
我们推荐UAC重用与原始转发请求相同的To,From,和Call-ID域值,但是也允许UAC为新的请求改变Call-ID头域。
最后,当一个新的请求构造好以后,这个请求将通过一个新的客户transaction发送,应此在最开始的Via头域中必须有一个新的branch ID (8.1.1.7节说明)
在其他的方面,转发的请求应该重用原始请求的头域和消息体。
在某些情况下,Contact头域的值可能在UAC中暂时或者永久保存,这个依赖于接收到的请求码和过期的时间;参见21.3.2和21.3.3节。
8.1.3.5 处理4xx应答
某个4xx应答码要求特定的UA处理,和请求的方法无关。
当接收到401(未授权)或者407(Proxy认证需要)应答的时候,UAC应该遵循在22.2和22.3中规定的认证步骤,重新发送带认证信息的请求。
当收到413(请求过大)应答的时候(21.4.11节),这说明请求包含了一个UAS所不能接收长度的消息体。如果可能,UAC应该尝试重新retry这个请求,或者去掉包体或者换一个小一点的长度。
如果收到了一个415(不支持的媒体类型)应答(21.4.13节),那么请求中包含的媒体类别是UAS所不支持的。UAC应该重发这个请求,并且这次发出的请求只包含应答中的Accept头域所指明的媒体类别,并且采用Accept-Encoding头域中指明的encoding方法,还有Accept-Language指明的语言。
如果收到了一个416(不支持的URI Scheme)应答(21.4.14节),Request-URI使用的URI Scheme(方案)是服务端所不支持的。客户端应该重新尝试这个请求,并且换用SIP URI。
如果收到了一个420(非法扩展)应答(21.4.15节),请求的Require或者Proxy-Require头域包含的option-tag中包含了UAS或者proxy不支持的特性。UAC应该尝试去掉应答中的Unsupported头域中列出的扩展以后然后再尝试。
在上述所有的情况中,所有需要重试的请求,都需要经过适当修正成为一个新的请求。这个新请求采用一个新的transaction并且应该有和上次请求相同的Call-ID,TO,From头域,Cseq应该有一个新的顺序号码(比原有顺序号码更大)。
其他的4xx应答,包括尚未制定的,是否允许请求重试,依赖于具体的方法和应用。
8.2 UAS特性
UAS在处理对话外的请求的时候,有一组规则需要遵守,这组规则与方法无关。12节指明了一个方法来判定一个请求是否在一个对话里。
注意,请求的处理是原子级别的。如果请求被处理,那么这个请求的相关状态一定是一起更新的。如果它被拒绝了,那么这个请求的所有相关状态一定是没有改变的。
UASs应当遵循本节所规定的顺序来处理请求。(就是说,首先是身份认证,然后是方法判定,然后是头域,然后按照本文规定处理剩余部分)
8.2.1 方法判定
当请求被认证(或者身份认证被忽略),UAS必须首先判定这个请求的方法。如果UAS发现自己不能处理这个请求的方法的时候,它必须给出一个405(方法不支持)的应答。产生应答的步骤在8.2.6节规定,并且UAS必须在给出的405(方法不支持)应答中增加一个Allow头域。这个Allow头域必须列明哪些方法UAS支持。Allow头域的说明在20.5节。
如果请求中的方法是服务器所支持的,那么处理将继续。
8.2.2 包头判断
如果UAS不认识请求中的包头域(就是说,包头域不在本规范中定义或者不在任何扩展中定义),那么服务器必须忽略掉这个包头域并且继续处理本请求。UAS必须忽略任何处理本请求所不需要的长得畸形的包头域。
8.2.2.1 TO 和Request-URI
To头域包含了由From域描述的发送者发出的请求的原始接受者。原始接受者可能是也可能不是正在处理这个请求的UAS,取决于呼叫转移或者其他的proxy操作。当TO域值和自身不相符的情况下,UAS可以自行决定是否接收这个请求。但是,我们依旧是建议UAS处理这个请求,甚至TO这个头域是以他们不认识的URI方案表达的(比如一个tel:URI),或者To头域并非指向这个自身处理的UAS。当然,另外一方面来说,如果UAS决定拒绝这个请求,它应该产生一个403(禁止访问)的状态码,并且交给服务器的transaction层来发送。
但是,Request-URI确定UAS来处理这个请求。如果Request-URI使用了一个UAS所不支持的方案(比如tel:URI),那么UAS应当拒绝这个请求,并且给出拒绝代码416(不支持的URI方案)。如果Request-URI并没有指明本UAS来处理这个请求,那么UAS应当给出一个404(未找到)的应答。比如,一个UA使用REGISTER方法来绑定它的address-of-record到一个特定的联系地址,将会收到Request-URI等于那个特定联系地址的请求。
其他潜在的Request-URI资源包括建立和刷新对话的UA发出的请求和应答的Contact头域。
8.2.2.2 合并的请求
如果请求的To头域中没有tag标志,UAS的处理核心必须检查基于正在进行的transactions上的请求。如果接收到的请求和正在处理的transaction的请求中的头域From tag,Call-ID,CSeq精确匹配了,但是请求并不匹配那个事务(基于事务匹配机制17.2.3节),UAS核心应该产生一个482(检测到循环)应答并且给服务器的transaction层发送。
这是由于相同的请求通过不同的路径到达UAS,很多情况下是由于分支的原因。UAS处理了第一个请求并且给其他所有这个请求以482(检测到循环)应答。
8.2.2.3 Require
如果请求的各项要素通过了UAS的判定,那么如果存在Require头域,接下来就是检查Require头域。Require头域是UAC用来通知UAS应该用什么样的SIP扩展来处理本请求的。Require的格式在20.32节中有介绍。如果UAS不支持请求的Require头域中的option-tag列表,那就必须产生一个420应答(错误的扩展)。并且UAS必须添上Unsupported头域,里边填上刚才接收到的请求的Require头域中,哪些options是自己所不支持的。
需要注意的是,Require和Proxy-Require禁止出现在SIP CANCEL请求中,或者回应给非2xx应答的ACK请求中。就算出现了在处理的时候也必须被忽略。并且回应给2xx应答的ACK请求必须只能包含在初始请求(在这个ACK请求之前的请求)中包含的Require和Proxy-Require所规定options,这样才能保证服务端能够正确处理。
例子:
UAC->UAS: INVITE sip:
[email protected] SIP/2.0
Require: 100rel
UAS->UAC: SIP/2.0 420 Bad Extension
Unsupported: 100rel
这个特性(Unsupported)是为了保证客户-服务端都能够无阻碍的交互,除非是options对方不支持(就像上边的例子说明的一样)。对于相互匹配的客户-服务端(相互匹配的意思就是客户端Require的正好是服务端支持的),那么这些请求、应答将会处理的非常迅速,减少了一个请求的往返协商的浪费。另外,这个也避免了客户端不知道服务端到底不支持那些特性扩展。
某些特性扩展只对终端(endsystem)有效例如呼叫处理域等等。
8.2.3 内容处理
当UAS支持客户端请求中要求的扩展支持后,UAS要检查消息头域描述的消息体部分。如果UAS并不支持消息体部分的类型(Content-Type指明),语言(Content-Language指明),编码(Content-Encoding指明),并且这个消息体部分并非可选的消息体(Content-Disposition头域指明),UAS必须回应一个415错误应答(不支持的媒体类型)应答。并且如果不支持请求中包含的消息体的正文类型,那么在应答中必须包含UAS所支持的消息体的类型列表(在Accept头域中指明)。如果不支持请求包含的消息体的encoding方式,那么应答中必须包含Accept-Encoding头域列明服务端支持的encoding方式。如果请求中的语言部分不支持,那么就必须在应答中包含Accept-Language头域列明支持的语言。在这些检查之外,消息体正文的处理依赖于方法和类型(method and type)。关于处理内容相关的头域的进一步的资料在7.4、20.11到20.15节。
8.2.4 应用扩展
如果UAS希望应用一部分SIP扩展,那么不可以在产生应答的时候做SIP扩展,除非这个扩展是在请求中的Supported头域中指明了的。如果这个扩展并没有在本请求的Supported头域中指明,那么服务端必须基于基准SIP给出应答,或者给出已知客户端支持的扩展应答。在极少数情况下,如果服务端不用扩展就无法处理请求,那么服务端应该发送421(需要扩展支持)应答。这个应答说明如果没有适当的扩展就无法给出正确的应答。在应答中需要的扩展必须在应答中的Require头域中指出。我们并不推荐这个方法,因为它会降低协同工作的能力。
除了421应答之外的其他应答中,如果需要应用扩展,那么这些扩展需要在421的应答中的Require头域中列明。当然,服务端不允许使用没有在请求中的Supported头域中列明的扩展。因此,应答中的Require头域只会包含标准的RFCs的扩展option tags。
8.2.5 处理请求
当所有的检查都通过了以后,UAS根据方法决定如何处理请求。第10节讲述了REGISTER请求的处理,11节讲述了OPTIONS请求的处理,13节讲述了INVITE的处理,15节讲述了BYE的处理。
8.2.6 产生应答
UAS产生应答,需要遵守接下来的几个小节中讲述的步骤产生一个应答。
在本节没有描述的应答代码的其他的行为,也可能会跟据应答代码的不同而要求。当创建应答的步骤完成之后,UAS将应答交给收到这个请求的服务端的transaction去处理。
8.2.6.1 发送一个临时应答
很多情况下,在与方法无关的应答规范中,在非INVITE请求的情况下,我们都要求UAS不应该发送临时应答给请求者。在这种情况下,UAS应该尽快发送一个终结应答给非INVITE请求。
当需要产生一个100(Trying)应答的时候,所有对应请求中包头的Timestamp域必须也拷贝到这个应答包头(就是说如果请求中有Timestamp,应答中也必须有timestamp)。而且如果应答有延时,那么UAS应该在这个Timestamp上增加延时的值。这个数值必须包含了接收请求和发送应答的时间,用秒来计数。
8.2.6.2 包头和Tags
应答中的From头域必须和请求中的From头域相等。应答中的Call-ID头域必须和请求中的Call-ID头域相等。应答中的Cseq头域必须和请求中的Cseq头域相等。应答中的Via头域必须和请求中的Via头域相等,而且顺序也必须相等。如果请求中包含了To tag,那么应答中的To头域必须和请求中的To头域相等。如果请求中的To头域并不包含Tag,那么应答中的To头域的URI必须和请求中的TO头域的URI相等;此外,UAS还必须增加一个Tag到To头域上(100(trying)应答是一个例外,在100中可能已经存在了一个tag)。这就提供了一个UAS正在应答的标志,也许就是对话ID的一部分。对同一个请求来说,它的应答必须有相同的tag标志,包括终结应答和临时应答(同样100(trying)除外)。生成tag的步骤在19.3节。
8.2.7 无状态UAS行为
无状态UAS就是说UAS本身不保持事务的状态。但是它在发送对应请求的应答之后并不保存请求的状态。如果无状态UAS接收到了一个重新发送的请求,它会重新产生一个应答并且重新发送应答,就像它接收到的是一个第一个请求一样(就像是新请求而不是重发的请求一样)。只有当相同请求的处理会导致相同的应答的时候,这个UAS才会是无状态的。例如:这条规则排除了无状态的登记服务。无状态的UAS并不包含一个transaction层;他们直接从通讯层接收请求和发送应答。
无状态的UAS通常用于处理不需要身份认证的请求,而且这些请求的应答是可以预期的。如果当不需要身份认证的请求被作为有状态的UAS来处理,那么大量的不需要身份认证的请求会造成服务器大量的事务状态,可能会导致UAS的处理非常慢,甚至中断处理,这样就导致了DoS(denial of service)状态。在26.1.5节中有进一步的描述。
无状态的UAS有下列重要的特性:
o 无状态的UAS不许发送临时应答(1xx)
o 无状态的UAS必须忽略ACK请求
o 无状态的UAS必须忽略CANCEL请求
o To头域必须依据无状态的规则来产生-就是说对于相同的请求,产生相同的tag标记。19.3节有讲tag标记。
对于其他情况而言,无状态的UAS遵循有状态的UAS的规则。对于一个新请求来说,同一个UAS可以是有状态的,也可以是无状态的。
8.3 重定向服务器
在某些架构下,可以透过重定向机制,来降低proxy服务器上的路由请求的压力,从而提高消息转发的效率。重定向允许服务器在一个请求的应答中,推送路由信息到客户端,这样就可以做到把自己从后续的消息流中脱离出来,但是同时又能提供准确的请求定位服务。当请求的发起者接收到转发的应答之后,他会重新产生一个基于接收到的URI(s)的请求。从network的中央转发到最边缘,转发机制可以适用于跨度很大的网络。
转发服务器在逻辑上是通过一个服务器的transaction层建立的,他作为transaction user可以访问某些类型的位置服务(参见第10节有关注册服务器和位置服务的说明)。这个位置服务可以很方便的用数据库里边的一个URI对应多个地址URI(可能在这个地址找到对应的客户)来实现。
一个重定向服务器并不发出任何指向它自己的请求。接收到任何一个非CANCEL的请求之后,服务器要么拒绝这个请求,要么从位置服务器上找到这个请求应该去的其他位置然后返回一个3xx的终结应答。对于合法的CANCEL请求,它应该返回一个2xx的应答。这个应答将会结束掉SIP事务。重定向服务器在整个SIP事务中位置事务的状态。在重定向服务器之间检测循环死锁应该是客户端的责任。
当重定向服务器给请求方返回一个3xx应答的时候,它会在Contact头域填写一组(一个或者多个)其他位置信息。如果需要指明这个Contact数据的生存周期,可以在Contact头域添加一个”expires”参数。Contact头域包含指向新位置的URI或者可以试试看的用户名,或者就是简单的附加通讯参数等等。301(永久去除)或者302(临时去除)应答同样可以指定和原始请求一样的目的位置和用户名,但是会附加像不同的服务器或者多点传送地址、或者SIP由UDP到TCP传输等等这样的通讯参数。
重定向服务器必须不能重定向一个请求到它自己的Request-URI列表中的地址;但是倘若那个URI并没有指向自己,重定向服务器可以路由这个请求到目的URI,或者可以返回一个404拒绝。
如果一个客户端正在使用外出的proxy,并且这个proxy实际上是重定向请求,那么就可能出现无限重定向循环。
注意,Contact头域的值可能指向一个与原始呼叫者无关的资源。比如,一个SIP对PSTN(本地电话网)网关的呼叫可能被转递给一个特别的说明,比如”你所呼叫的电话号码已经改为….”。Contact应答头域可以包含任何合适的能处理这个呼叫对方的URI,并不限于SIP URI。比如,它可以是电话,传真,或者IRC(如果有支持和定义的话)或者一个mailto:(RFC 2368[32]) URL。在26.4.4节讲述了对SIPS URI到非SIPS URI的实现和限制。
在Contact头域中的”expires”参数指明了这个URI的生存周期。这个参数的值是以秒作为单位的。如果这个参数没有提供,那么这个”expires”的缺省是有Expires头域所指定的。这个参数中如果设置了非法的值,那么都应改更改成为缺省的3600。这提供了对RFC2543的向后兼容,RFC2543允许在这个头域中填写绝对时间。在本协议中,如果这个expires填写了绝对时间,那么就会当作是非法的值,就会被当作是3600。
重定向服务器必须忽略自己所不能理解的特性(包括不认识的头域,不认识的Require中的option tag,甚至是不认识的方法名),并且带着问题处理这个请求的重定向。
9 取消一个请求(Cancel)
前边几节讲述了对所有方法的处理请求和处理应答的UA的通用处理过程。在本节中,我们讨论一个通用的方法,CANCEL。CANCEL请求,就像名字所说的,是用来取消客户端发起的上一个请求的。特别是,它请求UAS去终止上一个请求并且对上一个请求产生一个错误的应答。CANCEL对UAS已经给出终结应答的请求无效。所以,CANCEL请求的最大用处是取消需要服务器长时间处理的请求。也就是说,CANCEL最常用来处理取消INVITE请求,因为INVITE通常需要花费很长时间来产生一个终结应答。在这种使用中,UAS接收到对一个INVITE请求的CANCEL请求,当这个INVITE还没有得到终结应答的时候,UAS会”停止振铃”,并且给INVITE请求一个错误的应答(487)。
CANCEL可以由proxy或者UAC发起。15节讲述了在何种情况下,UAC会CANCEL
一个INVITE请求,在16.10节讲述了proxy对CANCEL的使用。
一个有状态的proxy需要对CANCEL进行响应,而不是简单的转发从下行流中接收到的一个应答。基于这个原因,CANCEL是一个”点对点”(hop-by-hop)的请求,也就是说,CANCEL需要每一个有状态的proxy节点进行处理和应答。
9.1 客户行为(Client Behavior)
CANCEL请求不应该取消除了INVITE之外的请求。因为除了INVITE之外的请求的响应都是立即响应的,所以,发送CANCEL来取消一个非INVITE请求总是形成一种赛跑的局面(就是说,cancel先到还是被取消的请求先到)。
下列步骤用于创建一个CANCEL请求。在CANCEL请求中的Request-URI , Call-ID , To , Cseq的数字部分,From这些头域都必须和被取消的请求头域一样,包含这些头域的tags. 客户端创建的CANCEL必须只有一个Via头域值,这个头域值和被取消的请求的最上一个Via头域值相同。这些头域的值和请求的值相同可以让CANCEL请求和被取消的请求相匹配(9.2节描述了如何匹配)。在Cseq请求头域的method部分必须是一个CANCEL方法。这个让这个CANCEL请求被当作自己的事务而被正确的鉴别和处理(参见17节)。
如果被取消的请求包含一个Route头域,CANCEL请求也必须包含这个Route头域的值。这个是让无状态的proxy能够正确路由CANCEL请求。
CANCEL头域必须不能包含任何Require或者Proxy-Require头域。
一旦CANCEL请求被创建了,客户端应当检查是否收到了这个CANCEL请求取消的原始请求的任何应答(临时的或者终结的应答)。如果没有任何临时应答收到,这个CANCEL请求一定不能发送,直到客户端等到了第一个临时应答。如果原始请求已经收到一个终结应答,这个CANCEL也不应当发送,因为CANCEL请求对已经产生了终结应答的请求没有任何作用。当客户端决定发送一个CANCEL,它会为这个CANCEL创建一个客户transaction,并且通过目的地址、端口、传输层来发送CANCEL请求。这个CANCEL中的目标地址、端口和传输层必须和原始请求一样。
如果允许在接收应答之前发送CANCEL请求,那么服务端必须支持在接收原始请求之前接收到CANCEL请求。
注意,原始请求的事务和CANCEL请求的事务都是互相独立的。也就是说,UAC判定一个请求的取消不能依赖原始请求的一个487(请求终止)应答,遵循RFC2543协议,UAS不会产生这样一个应答。如果原始请求经过了64*T1(T1在17.1.1.1节定义)秒还没有应答,客户端应当认为原始请求已经取消,并且应当销毁对应原始请求的客户端事务。
9.2 服务端行为(Server Behavior)
CANCEL请求要求服务端的TU取消相关的事务(transaction)。TU根据接收到的CANCEL请求,并且假定请求的方法不是CANCEL或者ACK,并且用17.2.3节描述的事务匹配方法来匹配事务,这样TU就可以决定那个事务需要被取消了,被匹配的事务就是需要被取消的事务。
服务端对CANCEL请求的处理依赖于服务器的类型。一个无状态的proxy会转发这个请求,一个有状态的proxy可能会响应这个请求,并且自己再产生一些CANCEL请求,UAS会响应这个CANCEL请求。16.10节讲述了proxy怎样处理CANCEL请求。
当UAS收到CANCEL请求,首先按照8.2节的UAS通用处理方法进行处理。不过,既然CANCEL请求是基于”点对点”(hop-by-hop)的,也是不能再提交的,他们不能由服务器为了获得Authorization头域中正确的认证而反复尝试。注意,因此CANCEL请求也不能包含Require头域。
如果根据上边的步骤,UAS不能找到与CANCEL请求相匹配的事务,它应该给CANCEL一个481应答(调用的Leg/Transaction不存在 会话/事务不存在)。如果对应原始请求的事务存在,那么UAS在接收到CANCEL请求的处理就依赖于是否已经给这个原始请求发出了终结应答。如果已经发出了,不会对CANCEL请求对应的原始请求做任何处理,不会更改任何会话状态,不会对原始请求的应答做任何处理。如果UAS没有发出对原始请求的终结应答,它会依赖于CANCEL所取消的原始请求方法。如果原始请求方法是INVITE,UAS应当立刻响应INVITE一个487(请求终止)。本协议中,对CANCEL取消的其他本协议中定义的方法没有约定。
不管原先请求的方法是什么,只要CANCEL匹配一个事务,UAS就响应CANCEL请求一个200(OK)应答。这个应答根据8.2.6节约定构造。注意,给CANCEL应答的To tag和给原始请求的应答的To tag应该是一样的。对CANCEL的应答会通过服务端transaction来传送。
10 注册(Registrations)
10.1 概览
SIP提供了一个搜索机制,如果一个用户希望建立和其他用户的会话,SIP必须查找能够找到对方用户正在使用的当前主机(hosts)。这个搜索机制经常被SIP网络基本元素使用,比如proxy服务器,重定向服务器等等。他们在接收、以及响应一个请求的时候,会基于这个用户的位置信息来判定这个消息应该发送到哪里。要实现这个,SIP网络部件考虑了一个抽象的服务:位置服务;位置服务是通过对特定地区提供地址绑定来实现的。这些地址绑定转换输入的SIP或者SIPS URI,比如sip:
[email protected],转换到一个或者一组更加”接近”目标用户的URI,比如sip:
[email protected]。基本上,一个proxy会从把输入的URI转换到用户实际位置的位置服务中得到最终用户的位置。
注册服务为特定地区的位置服务创建绑定关系,这个绑定关系是用来建立包含一个或者多个联系地址的address-of-record URI。因而,当那个地区的proxy接收到一个请求,这个请求的Request-URI和address-of-record的记录匹配,那么这个proxy会转发请求到这个address-of-record中登记的联系地址中去。通常,只有当对那个address-of-record的请求会被路由到这个区域的时候,登记这个address-of-record 到这个这个区域的位置服务才是有意义的。在大多数情况下,这个就要求登记服务所覆盖的区域和URI中的address-of-record所覆盖的区域相同。有很多种方法来建立位置服务。一个方法是administratively(管理)。在上述的例子种,Bob我们通过查询公司数据库知道他是一个工程部职员。不过,SIP提供了一个让UA能够创建精确绑定的机制。这个机制就是登记服务。
登记服务需要向一个特殊的UAS服务器(注册服务器registrar)发出REGISTER请求。注册服务器(registrar)为一个区域的位置服务作为前端接入,根据REGISTER请求的内容读写位置对照表。这个位置服务通常为处理这个区域的proxy服务器提供位置服务。
总的登记服务处理流程在图2中说明。需要注意的是,登记服务器(registrar)和proxy服务器都是逻辑上的角色,可以在网络中用一个设备来部属;在例子图中是为了能够清楚的表示所以分开描述。还需要注意的是如果他们(登记服务器和proxy)本身是分开的,那么UA可以通过proxy服务器发送注册请求。
SIP本身对实现位置服务器(location service)没有特别的要求。唯一的要求是某些区域的注册服务器(registrar)必须能够对位置服务的数据进行读写,并且这个区域的proxy或者重定向服务器必须能够兼容读取相同的数据。注册服务器(registrar)可能和某一个区域的proxy服务器部署在一起。
10.2 构造一个REGISTER请求
REGISTER请求用来增加、删除、查询绑定资料。一个REGISTER请求可以增加一个address-of-record和一个或者多个联系地址之间的绑定。在合适的第三方认证的情况下,可以做address-of-record的登记。客户端同样可以删除前边绑定的内容也可以查询address-of-record的当前绑定地址。除了特别说明以外,REGISTER请求的构造以及客户端如何发送REGISTER请求和通常的8.1节描述的和17.1节描述的UAC发出请求是一致的。
一个REGISTER请求并不建立一个对话。当基于事先给定路由集(8.1节)的情况下,一个UAC可以在REGISTER请求中包含一个Route头域。在REGISTER请求和应答包中,Record-Route头域并没有任何意义,如果这个头域存在,必须被忽略。另外,UAC一定不能基于REGISTER请求的应答包中的任何Record-Route头域来创建新的路由集合。
下面这些头域,除了Contact,必须在REGISTER头域中包含。Contact头域可选。
Request-URI: 这个头域指明了登记服务所指明的位置服务所在的区域(比如sip:chicago.com)。”userinfo”和”@”元素在SIP URI中不能出现。
To: 这个头域包含了被查询、增加、修改的address-of-record。to头域和Request-URI头域通常是不同的,因为这个由用户名组成。这个address-of-record必须是一个SIP URI或者SIPS 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头域值,而不是重发)。
bob
+----+
| UA |
| |
+----+
|
|3)INVITE
|
[email protected]
chicago.com +--------+ V
+---------+ 2)Store |Location|4)Query +-----+
|Registrar|=======> | Service|<======= |Proxy|sip.chicago.com
+---------+ +--------+=======> +-----+
A 5)Resp |
| |
| |
1)REGISTER| |
| |
+----+ |
| UA | <-------------------------------+
cube2214a| | 6)INVITE
+----+
[email protected]
carol
图2:REGISTER例子
下边的Contact头域参数在REGISTER请求中有特别的意义:
action: 在RFC2543中的“action”参数已经废弃了,UAC不能使用”action”参数。
expires: “expires”参数表明UA的绑定的有效时间。以秒为单位的整数。如果本参数没有制定,那么这个参数的值就是Expires头域的值。实现中,可以把超过2**32-1的值(4294967295秒或者136年)认为是2**32-1。非法的值应当视同3600。
10.2.1 增加绑定
REGISTER请求是向注册服务器(registrar)发送一个包含对某一个address-of-record的地址的SIP请求应当发送的实际联系地址。address-of-record包含在REGISTER请求的To头域中。
请求中的Contact头域通常包含了SIP或者SIPS的URI,这些URI表明了特定的SIP端点(比如sip:
[email protected]),他们也可以使用其他的URI表示方法。一个SIP UA可以选择注册一个电话号码(比如使用tel URL, RFC 2806[9])或者一个email地址(比如用mailto URL, RFC2368[32])来作为address-of-record的联系地址Contact域。
例如,Carol,有一个address-of-record”sip:
[email protected]”,将会在区域chicago.com的注册服务器上注册。她的注册服务信息将会被chicago.com区域的proxy服务器使用,用来路由和转发到Carol的address-of-record请求到她的SIP终端。
当客户端在注册服务器(registrar)上建立好了绑定以后,它可以根据需要发送后续的注册请求,包含新的绑定信息或者修改以前的绑定信息。给REGISTER请求的2xx应答中,在Contact头域中是在这个注册服务器(registrar)上登记的完整的这个address-of-record的绑定列表。
如果REGISTER请求中的To头域中的address-of-record是一个SIPS URI,那么任何在REGISTER请求中的Contact头域都应当是SIPS URI。客户端只有在有其他手段保证非SIPS URI的安全性的情况下,才能在SIPS 的address-of-record的地址上注册非SIPS URI。这个也可以适用域使用非SIP协议的URI,或者用非TLS来加密的SIP设备。
注册并不需要更新所有的绑定。一般情况下,UA只更新它现在的联系地址。
10.2.1.1 设置Contact地址的过期参数
当一个客户端发出一个REGISTER请求,它可能包含一个过期参数用来表示这个注册的地址的有效期。(就像在10.3节讲述的那样,注册服务器(registrar)根据自己的策略选取实际的时间间隔来计算有效期)。
客户端设置有效期的方法有两种:一个是通过设置Expires头域,一个是通过设置”expires”contact头域的参数来设置。 后一种允许针对同一个REGISTER请求中的多个绑定联系地址中的每一个联系地址单独设定有效期,然后所有没有带”expires”参数的Contact头域的值都使用Expires的设置。
如果REGISTER中没有两种有效期都没有设置,这就表明这个有效期由服务器来决定。
10.2.1.2 Contact Adress的参数选择
如果在一个REGISTER请求中包含多个Contact,着说明UA想要把这些Contact头域的内容都和To头域中制定的address-of-record地址绑定起来。这个列表可以用”q”参数来区分Contact头域的优先级。”q”参数用来标志特定Contact头域值和其他绑定的address-of-record的联系地址之间的优先级。16.6节讲述了一个proxy服务器如何使用优先级。
10.2.2 删除绑定
注册信息是一个纯粹软件的状态,并且如果不刷新会过期。如果需要,也可以被删除。一个客户端可以设置注册服务器(registar)的有效期(10.2.1)。一个UA可以通过发出有效期为”0”的REGISTER请求,使某一个联系地址立刻失效。UAS都需要实现这个机制使得在联系地址过期前能够被删除。
REGISTER规范中的Contact头域如果设置成为”*”则表示需要操作所有的注册项。但是也只能在具有一个Expires头域并且这个头域为’0’的情况下能够使用。(这就是说,只能够在要删除所有的注册项的时候使用”*”)。
用”*”来删除所有的注册项有一个好处,就是使得UA不需要知道每一个注册项的精确值。
10.2.3 访问绑定
无论请求是否包含了Contact头域,给任何REGISTER请求的成功应答都包含了一个完整的绑定列表。如果REGISTER请求头域中不包含Contact头域,那么注册服务器的绑定列表将不会改变。
10.2.4 刷新绑定
每一个UA都对先前它建立的绑定信息由刷新的义务。禁止对其他UA建立的绑定信息进行刷新。在注册服务器(registrar)给出的200(OK)应答中,包含了的Contact头域中列明了所有的当前绑定信息。UA需要挨个比较这些联系地址,看看是否这个地址是可以建立联系的,这个比较是通过19.1.4节中的比较规则来进行的。如果是,它通过更新expires参数来更新过期时间(或者Expires头域)。于是在这些绑定信息过期前,UA为每个绑定发出REGISTER请求来刷新绑定。也可以通过一个REGISTER请求来刷新数个绑定请求。
UA在一个刷新周期中,应该使用相同的Call-ID来进行注册调用。刷新的注册信息应该是和原来登记的信息一致。
10.2.5 设置内部时钟
如果REGISTER请求的应答中包含一个Date头域,客户端可以用这个头域来校正当前内部的时钟。
10.2.6 寻找注册服务器
UA有3种方法来决定向哪里发出注册请求:通过配置,使用address-of-record,广播方式。一个UA可以用非本文档规定的方式,配置一个注册服务器的地址。如果UA没有配置任何注册服务器的地址,UA应该用请求的Request-URI部分种的address-of-record的服务器部分(host part),用普通的SIP服务器定位机制[4]。比如,用户”sip:
[email protected]”地址的注册服务应该是”sip:chicago.com”。
最后,UA可以通过监听广播的形式来获得注册服务器地址。监听广播的注册服务器是通过监听著名的”全部SIP 服务器”广播地址”sip.mcast.net”(224.0.1.74)。没有Ipv6的广播地址。SIP 的UA可以监听这个地址,并且用这个来知道其他本地用户的地址(见附件[33]);不过他们并不对请求做响应。通过监听广播的登记服务可能在某些环境下不能用,比如,基于同一个本地网络的多个商业应用的情况。
10.2.7 传送一个请求
当REGISTER请求被构造好以后,并且也有了登记服务起地址,UAC遵循8.1.2节的说明来提交transaction层来发送REGISTER请求。如果transaction层返回一个由于REGISTER请求没有应答的超时错误,UAC不应该立刻重新尝试对同一个注册服务器的注册请求。
立刻重新尝试很有可能导致再次超时。等待一个合理的时间在尝试可以降低网络的负载,在这里并没有一个约定的等待时间间隔。
10.2.8 错误响应
如果UA接收到一个423(间隔太简略)应答,它可能需要更改REGISTER请求中的所有有效期,使得这些有效期必须大于等于423应答头中的Min-Expires头域中的有效期,并且重新尝试发送这个REGISTER请求。
10.3 处理REGISTER请求
一个注册服务器(registrar)就是一个UAS,这个UAS用来响应REGISTER的请求,并且维持一个绑定表,这个绑定表用来提供给它所管理的区域中的proxy服务器和重定向服务器的。一个注册服务器根据8.2和17.2中的规定来处理请求,但是它仅仅处理REGISTER请求。一个注册服务器禁止产生6xx应答。一个注册服务器可以适当的转发REGISTER请求。通常用于一个注册服务器(registar)监听一个多点广播,并且通过302应答(临时转移)转发这个多点广播的REGISTER请求到它正确的处理位置。
注册服务器必须忽略在REGISTER请求中的Record-Route头域,并且也不能在REGISTER请求的应答中包含任何Record-Route头域。注册服务器可能收到一个有proxy转发过来的REGISTER请求,这个请求中由于proxy处理这个请求当作未知请求所以添加了Record-Route头域。
一个注册服务器必须知道(例如,通过配置)它所管理的区域。注册服务器一定需要按照接收到的REGISTER请求顺序进行处理。
REGISTER请求必须当作原子请求来处理,意味着给定的REGISTER请求要么就完整处理,要么就完全拒绝。每一个REGISTER信息的处理都和其他的注册和绑定信息的处理无关。
当接收到一个REGISTER请求,注册服务器(registrar)按照如下步骤处理:
1、 注册服务器(registrar)检查Request-URI来决定是否它属于本注册服务器所管理的区域的Request-URI。如果不是,并且如果这个服务器同时也作为一个proxy服务器,那么这个服务器应当转发这个请求到指定的区域。这个转发是根据16节所规定的proxy通用信息处理规则来进行的。
2、 为了保证注册服务器能够支持所需要的扩展,注册服务器必须遵循8.2.2节描述的情况来处理Require头域。
3、 一个注册服务器应当对UAC进行身份认证。SIP UA的身份认证机制在22节描述。注册这个动作需要遵循SIP的通用的身份认证框架。如果没有任何认证机制,注册服务器可以使用From地址来作为原始请求的信任依据。
4、 注册服务器应当检查认证的用户是否通过认证来更改这个address-of-record的登记权限。比如,一个登记服务起(registrar)可以通过访问一个认证数据库,这个认证数据库映射了用户名和一个address-of-record列表,这些列表是用户可以更改绑定信息的列表。如果认证用户并没有权利修改绑定信息,注册服务器应当返回一个403(禁止访问)并且跳过后续步骤。在支持第三方认证和注册的架构下,一个实体可以被授权来更新多个address-of-record的注册信息。
5、 注册服务器(registrar)从REGISTER请求的To头域中解出address-of-record。如果这个address-of-record并非在这个Request-URI指明的区域中合法,那么注册服务器必须发出一个404(没有找到)的应答,并且跳过后续步骤。接着URI必须转换成为标准的格式。所有的URI参数都必须删去(包括用户参数user-param),并且任何非法(escaped)字符必须转换成为合法字符(unescaped)格式。最后形成一个可以用于绑定的列表。
6、 注册服务器(registrar)检查是否请求包含了一个Contact头域。如果没有包含,它跳过到最后一步。如果Contact头域包含了,注册服务器检查是否有一个Contact头域值是”*”,并且包含了一个Expires头域。如果请求有其他的Contact头域或者任何有效期的值是非0的,这个请求就是非法请求,并且服务器必须送回一个400(非法请求)的应答,跳过后续步骤。如果没有,那么注册服务器检查是否Call-ID复核每一个绑定的值。如果不符合,它必须删除绑定。如果复核,它必须仅仅删除保存的绑定表中CSeq值小于请求中的Cseq值的记录。否则,更新必须终止,请求失败。
7、 现在注册服务器(registrar)可以依次处理Contact头域中的联系地址了。对于每一个地址,它根据下边的规则进行有效期检查。
- 如果含有”expires”参数,这个参数值就是最终的有效期。
- 如果没有这个参数,并且请求有一个Expires头域,那么这个值就是有效期。
- 如果没有Expire头域也没有参数,那么本地的缺省配置应当作为有效期。
注册服务器可以选择一个小于请求中的有效期值作为最后的有效期。当且仅当请求的有效期大于0 并且小于1个小时并且小于一个注册服务器配置的最小有效期的时候,注册服务器可以响应一个423(有效期过小)的错误来拒绝。这个应答必须包括了一个Min-Expires头域来表明本注册服务器所接收的最小有效期,然后跳过所有后续步骤。
允许注册服务器设置注册服务器自己的有效期防止了过分频繁的刷新注册信息,并且也降低了维持和管理注册信息的工作量。在服务的创建的时候,注册信息中的有效期会经常用到。一个例子是follow-me(跟随我)服务,在这个服务中,一个用户可能在一个终端上只停留一小会儿。因此,注册服务器应当接收简略的注册;一个请求只有当它的有效期过短的时候(短到可能降低注册服务器性能的时候)才应当被拒绝。
对于每一个地址,注册服务器在当前绑定列表中用URI比较规则进行搜索。如果绑定不存在,它会暂时性的添加进去。如果绑定存在,注册服务器检查Call-ID值。如果Call-ID值在这个已经存在的绑定中和本次请求的Call-ID值不一样,那么如果绑定的有效期为0就删除这个绑定,否则就刷新这个绑定。如果Call-ID值一样,那么注册服务器比较Cseq值,如果请求中的这个值比现存的绑定值中的Cseq高,那么必须更新或者删除这个绑定(如果有效期为0 就删除,否则就刷新)。如果这个Cseq值比现存的Cseq值小,那么必须终止更新并且请求失败。
这个规则确保了从同一个UA过来的请求顺序处理,对于非顺序的请求跳过处理。
每一个绑定记录都包含了当时请求的Call-ID和Cseq的值。
只有当且仅当所有的绑定更新和绑定添加都完成的时候,绑定才可以做COMMIT处理(就是说,这次修改对proxy和重定向服务器可见)。如果任何更新或者添加失败了(比如,由于后台的数据库commit失败),必须给出一个500(服务器错误)的应答,并且中间进行的临时更新都必须删除。
8、 注册服务器(registrar)返回一个200(OK)应答。这个应答必须包含Contact头域,并且这个头域的值中列举了所有当前绑定的注册信息。每一个Contact值都必须包含一个”expires”参数,用来标志还有多久这个绑定信息就过期了。应答也必须包含一个Date头域。
11 查询能力
SIP方法OPTIONS允许一个UA来查询另外一个UA或者proxy服务器的能力。这个提供个客户端一个手段来查询服务端支持的方法,内容类型,扩展,codecs等等。这些都不用”ringing”对方。比如,在客户端试图在INVITE请求头中增加一个请求字段选项的时候,它并不知道对方UAS能否支持这个选项,它就可以用OPTIONS来查询一下UAS,通过检查OPTIONS返回的Supported头域,就可以知道是否支持这个选项。所有的UA都必须支持OPTIONS方法。
OPTIONS请求的目标是用Request-URI指明的,这个既可以是一个UA也可以是一个SIP服务器。如果OPTIONS指向一个proxy服务器,Request-URI设置成为一个没有用户部分(user part)的,类似REGISTER请求中的Request-URI一样。或者,一台服务器收到一个OPTIONS请求并且Max-Forwards头域值是0的时候,它就需要响应这个请求而不需要关心Request-URI的内容。
这个机制就像HTTP/1.1一样。这个机制可以用来实现类似”traceroute”功能来通过发出一系列的有着增量Max-Forwards头域的OPTIONS请求来检查每一个途径节点的能力。
就像对一般UA机制来说,如果OPTIONS没有应答,transaction层能够返回一个超时错误。这个可能标志着对方无法到达因此无响应。OPTIONS请求可以作为建立会话的一部分,用来查询对方的能力使用,这样在后续对话中可以使用双方兼容的方式。
11.1 构造OPTIONS请求
一个OPTIONS请求可以根据8.1.1节中的标准构造方法来进行构造。
Contact头域在OPTIONS请求中可以存在,也可以不存在。
Accept头域应当包含在请求中,用来标志UAC希望接收应答中的消息体的类型。通常情况下,这个设置成为UA的多媒体兼容能力,比如SDP(应用/SDP)格式。
对于一个OPTIONS请求的应答是假定是在原请求中的Request-URI范围内的。但是,仅当一个OPTIONS请求作为建立对话的一部分而发送的时候,后续的请求应当由收到并且响应这个OPTIONS请求的服务器进行处理。(就是说如果在建立会话的时候使用OPTIONS请求,那么OPTIONS之后的这些请求都应该由这个OPTIONS查询的服务器处理,这样才能保证使用的特性和OPTIONS查询出来的能力是一样的)
OPTIONS请求的例子:
OPTIONS sip:
[email protected]
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9Hg4bKhjhs8ass877
Max-Forwards: 70
To: