SIP协议详解(中文)-3

 
7.3.2 头域分类。
有一些头域是仅仅在请求(或者应答)中有效的。这些头域叫做请求头域或者应答头域。如果消息中的头域与这个消息的类型不匹配(比如在应答消息中出现的请求头域),这个头域必须被忽略。20节定义了每一个头域的分类。
7.3.3 缩写格式
SIP提供了一个用缩写格式来表达通用头域名字的机制。这个有助于避免消息过大而导致通讯层无法传输(比如在UDP传输的时候超过了最大传输单元(MTU))。这个缩写格式在20节定义。
缩写格式的消息头域名字可以在不改变消息语义的情况下替代较大的消息头域名字。在单个消息中,头域名字既可以用长的格式,也可以用缩写格式。在实现中,必须同时支持对长名字和缩写名字的处理。
7.4包体
请求信息,包括这个规范以后的扩展的新请求,都可以包含一个消息正文体。对消息正文体的解释依赖域请求的方法(请求类型)。对于应答消息来说,请求方法和应答状态(response status code)决定了消息正文体的格式。所有的应答消息都可以有一个消息正文体(body)。
7.4.1 消息正文类型(MessageBodyType)
消息中的internet媒体类别必须在Content-Type头域中指明。如果消息正文(body)通过某种形式的编码(encoding),比如压缩等等,都必须在Content-Encoding 头域中指明,否则Content-Encoding域必须忽略。如果可行,消息体的字符集作为Content-type头域的值的一部分表达。

在RFC2046[11]中定义的多部分”multipart” MIME类型可以在消息体中应用。在由多部分组成的消息体发送的时候,如果接受方的实现中,包头域的Accept域中,不包含多部分的标记,那么发送方必须发送一个非多部分的session description。
SIP消息可以包含二进制的包体或者部分包体。如果发送方没有其他显示的字符集参数指出,媒体的文本”text”子类型会是缺省的字符集”UTF-8”。
7.4.2 消息体长度
在Content-Length头域中存放了包体的字节长度。第20.14节讲述了本域的详细解释。
HTTP/1.1的“chunked”传输编码方式并不适用于SIP。(备注:chuncked编码传输方式是通过把消息正文体分为一系列的块来传输的,每一块有它自己的大小标记)

7.5 分帧的SIP消息(Framing SIP Messages)
不同于HTTP的是,SIP实现可以使用UDP或者其他非可靠传输协议。每一帧包括一个请求或者应答。第18节讲述了非可靠传输的应用。
在处理以面向流的通讯为基础的SIP消息的时候,必须忽略在开始行之前的CRLF[H4.1]。
Content-Length头域用来确定每一个SIP消息在通讯流中的结束位置的。在基于面向流通讯基础上的SIP消息一定要使用这个头域。

8 一般用户代理行为
一个用户代理代表了一个终端系统。它包含一个用户代理客户端(UAC),用来产生请求的,它包含一个用户代理服务端(UAS),用来响应请求的。UAC可以由一些外部的东西来发出请求和处理应答(比如用户按了一个按钮,或者按下了一个电话键产生了一个音频信号等等)。UAS是一个能够接收请求,并且产生应答的东西,它可以根据用户输入,外部输入,程序执行结果或者其他什么机制来产生应答。

当一个UAC发送一个请求,这些请求可能通过一些PROXY(代理服务器)传递到UAS上。当UAS产生一个应答,那么这个应答就会同样的被传送到UAC。UAC和UAS的处理由两个特点。第一,基于请求或者应答是否在一个对话里,第二,基于请求的方法(method)。会话的彻底描述在第12节;哪里描述了点对点的用户代理之间的关系,并且通过一些SIP方法建立了会话,比如INVITE方法等。

在本节,我们将讨论在处理对话外的请求时,UAC和UAS的方法无关的规则。这些当然也包括用于建立会话的请求。在26节讲述了对在对话外的请求和应答的安全处理。特别时,UAS和UAC之间的互相认证的机制。通过用S/MIME加密的消息体可以提供有限的隐私保证。
8.1 UAC特性
本节讲述UAC在会话外的特性。
8.1.1 产生一个请求
一个合法的SIP请求必须至少包含如下头域:TO,FROM,Cseq,Call-ID,Max-Forwards, Via;这些字段在所有SIP请求中必须包含。这6个字段是SIP消息的基本组成部分,他们提供了用于路由用的核心信息,包含了消息的地址,响应的路由,消息传递次数,详细的顺序,事务的唯一标志。
这些头域字段是必须包含在请求行之后的,请求行包含了请求的方法,Request-URI,SIP的版本号码。
有两个在对话外的发送请求的示例(通过INVITE请求建立连接,第13节),(通过OPTIONS请求查询负载,第11节)。
8.1.1.1 Request-URI
最开始的Request-URI头域应该是TO头域的的值。但是在REGISTER方法中,有一个值得注意的不同;REGISTER方法的Request-URI头域在第10节中指出。出于隐私的原因而把这些字段的值设置成为同一个值并不太合适(尤其是如果初始的UA期望Request-URI会在传输中改变的话)。
在一些特定的情况下,预先设置的路由表(route-set)会影响消息中的Request-URI。一个预置路由表是由一串server的URI组成,这些服务器是UAC往外发送会话外请求所需要经过的。通常,他们是由用户或者服务提供商手工在UA上设置的,或者通过一些非SIP的方法自动设置。当一个提供商希望配置一个出口proxy给一个UA,我们强烈建议通过一个预置一个单个URI路由表的方式来实现,这个单个路由就是出口proxy。
当要使用预置路由表(route set),必须提供Request-URI和Route头域(在12.2.1.1节中有详细描述)(甚至在没有对话存在的时候也必须提供),并且把Request-URI当作远端目标URI。
8.1.1.2 TO
To头域是第一个并且也是最先指定请求的”逻辑”接收地,或者是这个请求的用户或者资源的address-of-record。这个域内的地址可以是也可以不是请求的最终接收者。TO头域可以用SIP或者SIPS URI,也可以用其他方式的URI(比如电话URL (RFC2806[9]))。所有的SIP实现必须支持SIP URI的实现。任何支持TLS的实现必须支持SIPS URI的实现。
To头域允许有一个显示用的姓名。
UAC可以通过无数的方法来知道在一个给定请求的时候该如何填写TO头域。通常用户会建议采用人工界面中输入的To头域,可能手工输入这个URI或者从地址本中选择(就好像outlook邮件中的to一样)。用户通常不会输入完整的URI,可能只是一个简单的字串(比如”bob”)。这就要求UA能够判断用户输入的这个到底是那个URI。一般使用用户输入的字串加上”@”标志和主机的名字组合成为SIP URI(比如sip:[email protected])。如果希望通讯在保密机制下进行,那么就用用户输入的字串组成SIPS URI的部分,用户输入的将加上”@”和主机的名字作为整个SIPS URI。这个主机的名字通常是请求方的主机名字,这个主机允许处理外发请求。这个很像”缩位拨号”的机制,这个机制要求请求者自身的主机能够解释这个缩位拨号一样。
如果UA不希望指定主机,那么就需要将用户输入的电话号码解释成为一个电话的URL。相当于,每一个请求经过的主机都会有机会来处理这个请求。比如,一个用户在机场可能登陆机场的代理服务器,并且通过机场的代理服务器发出一个请求。如果他输入”411”(美国本地电话本查询服务号码),这个就需要机场的外发的代理服务器进行解释和处理,而不是解释成有主机的用户。在这里,tel:411是一个正确的解释。
在会话外的请求中,不能包含To tag字段,在to头域中的tag是用来在对话中做标志的。既然对话还没有建立,那么tag就不能存在。
20.39节有进一步的描述。
下边这个例子是一个To头域的例子:
To: Carol
8.1.1.3 From
From头域包含了请求发起者的逻辑标志,可能是用户的address-of-record。就像To头域一样,From头域也包含一个URI并且可以包含一个显示的姓名。SIP可以用这个头域来实现对请求的检查和选择一个规则进行对请求的处理(比如,自动的呼叫拒绝,凡是x人发过来的东西,一律无视)。同样的,因为From头域包含的是逻辑名字,所以From URI也可以不包含IP地址或者UA对应的主机名字FQDN。
From头域可以包含一个显示姓名。在客户身份隐藏的情况下,一个UAC应该使用显示名字”Anonymous”,连通一个语法正确,但是没有意义的URI(比如:sip:[email protected])。通常,用户或者用户的本地主机的管理人员会事先规定请求头域中的From头域的值。如果给定的UA是多个用户共同使用的,那么必须有一个URI对应身份的profile,这样才能够切用户的profile。收到请求的服务方可以根据这个用于分辩身份的URI来区分同一个UA上的不同的用户,并且根据他们的From头域来判定他们的身份。(22节有更多的验证说明)。
From域必须包含一个由UAC产生的新的”tag”参数。19.3节有tag的详细描述。20.20节有更深入的资料。
例子:
From: “Bob” ; tag=a48s
From: sip:+ [email protected];tag=887s
From: Anonymous ;tag=hyh8
8.1.1.4 Call-ID
Call-ID是一个在一系列消息中,区分一组消息的唯一标志。在对话中的任一UA的所有请求和所有应答的Call-ID必须一致。在UA的每次注册中,都应该是一样的。在会话外的时候,UAC发起一个新的请求,这个Call-ID头域必须由UAC产生一个全局(在时间和空间上都是)唯一的Call-ID, 除非是请求头的方法(method)指明了别的产生方式。所有的SIP UA都必须保证自己产生的Call-ID不会和其他UA产生的Call-ID重复。注意,如果是请求的重新尝试,则重新尝试的请求不被当作一个新的请求,所以不需要新的Call-ID(重新尝试的请求例如:认证冲突等等)。(见8.1.3.5)
我们强烈建议用密码乱序随机串(RFC 1750[12])来产生Call-ID。实现中,可以用类似”localid@host”这样的格式产生。Call-ID是大小写敏感的,并且通过简单字节/字节的来进行比较。
采用密码乱序随机串可以降低会话被窃听的机会,并且降低Call-ID重复的冲突。不规定或者要求使用用户界面来选择输入Call-ID头域的值。参见20.8节。
例子:
Call-ID: [email protected]
8.1.1.5 Cseq
Cseq 头域是用来区分和做位事务的顺序使用的。他由一个方法(method)和一系列的顺序号码组成。方法(method)必须和请求的方法一致。对于对话外的非REGISTER请求来说,顺序号码可以是任意的。这个顺序号码必须可以由32位的无符号整数表达,必须小于2^31。只要遵循了上述指导方针,客户端可以用任意的方法来产生这个Cseq头域。12.2.1.1节讲述了在对话中如何创建Cseq
例子:
Cseq: 4711 INVITE
8.1.1.6 Max-Forwards
Max-Forwards头域用来限制请求到他的目的地中间的跳转。它包含一个每隔一个跳转就自动减一的数字。如果Max-Forwards在到达目的之前就减到0,他会报告一个483(太多的路由)错误回应。
一个UAC必须为每一个请求填写一个Max-Forwards头域,这个字段的缺省值应该是70。这个数字是保证了请求在没有环路的SIP网络中都能够送达,也保证了在有环路的时候,尽量少消耗proxy的资源。如果这个数字要变小,则必须保证UA知道整个网络的拓扑结构。
8.1.1.7 Via
Via头域是标志了用于事务传输的传输设备,并且也标志了应答送回的地址。只有当需要通过选择传输设备到达下一个节点(hop)的时候,才需要在头域中包含Via域。当UAC创建一个请求,它必须在头域中添加一个Via域。protocol 名字和protocol版本必须分别是SIP和2.0。Via头域必须包含一个分支(branch)参数。这个参数用于区分请求创建的事务。这个参数客户端和服务器都会使用。除了CANCEL和给非2xx应答的ACK以外,branch参数对UA发出的所有的请求来说,在时间和空间上必须唯一。在下边的讲解中,CANCEL请求的branch参数必须和他所取消的请求的branch参数一致。在17.1.1.3节中讲述了给非2xxx(non-2xx)应答的ACK必须和其对应的的INVITE请求有相同的branch ID。
利用branch ID参数的唯一性来作为事务的ID(transaction ID),并非RFC 2543的一部分。根据本标准产生的branch ID必须用”z9h64bK”开头。这7个字母是一个乱数cookie(定义成为7位的是为了保证旧版本的RFC2543实现不会产生这样的值),这样服务器收到请求之后,可以很方便的知道这个branch ID是否由本规范所产生的(就是说,全局唯一的)。在这样的要求下,精确的branch ID的格式必须事先有实现的定义。
Via头域中,maddr,ttl,和sent-by字段会在transport层处理请求的时候设置(18节)。Via在proxy的处理在16.6节8段和16.7节3段描述。
8.1.1.8 Contact
Contact头域提供了访问后续请求的特定UA实例的联系方法:SIP或者SIPS URI。在任何会建立一个对话的请求中,Contact头域必须提供和包含一个SIP或者SIPS URI。在这个规范中定义的方法中,只有INVITE请求会建立一个会话。对这些请求来说,Contact的作用域是全局性的。这就是说,Contact头域中包含的URI是UA能够接收请求的,这个URI必须是有效的,甚至在对话外的请求中的Contact头域中的URI也必须是有效的。
如果Request-URI或者头上的Route头域包含了SIPS URI,Contact头域也必须是一个SIPS URI。在20.10节有更进一步的说明。
8.1.1.9 Supported 和 Require
如果UAC支持服务端响应请求的SIP扩展,UAC应该在请求的时候包含一个Supported头域说明options tags(19.2节)描述那些SIP扩展。option tags中出现的扩展说明必须是遵循RFCs的标准扩展说明。这样可以防止服务端支持非标准的客户端扩展实现。在Support头域中的对于SIP扩展定义中,严格遵守不支持试验性质的或者说明性质的RFCs扩展,这个也是由于这些扩展是描述提供商定义的扩展说明。如果UAC要求UAS能够支持扩展,以便UAS能够处理UAC的特定请求,那么它必须在请求头中增加一个Require头域来说明处理本特定请求需要什么样的一个扩展option tags。如果UAC需要请求经过的所有proxy都支持它发出的某个请求的扩展部分,它必须增加一个Proxy-Require头域来说明需要Proxy支持何种option tag扩展。
如同在Supported头域指出的,Require和Proxy-Require头域中的option字段必须限定于RFCs的标准扩展。
8.1.1.10 附加信息部分
在一个新请求创建以后,以上的头域都被正确初始化了以后,就可以位这个请求增加它所需要的附加头域了。SIP请求允许包含一个MIME-encoded消息正文。无论请求包含哪种消息正文,都必须引入头域来指出这个正文的类型,以及这个正文的一些其他说明。关于这些头域的详细说明,请参见20.11节到20.15节。
8.1.2 发送一个请求
于是,我们就开始查找请求发送的目标。除非有其他的特定说明,目标必须是通过DNS来查找的(参见[4]说明)。如果路由表(route set)中的第一个元素表明这是一个严格路由(strict router,在12.2.1.1节中讲述),那么这些过程必须在请求的Request-URI中说明。否则,这些过程在请求中被应用于第一个Route头域中(如果存在),或者在请求的Request-URI中(如果Route头域不存在)。这样一些过程产生了一系列的地址,端口,和用于传输的传输器。无论那个URI用在这个[4]中描述的过程的输入,如果Request-URI指明了SIPS,那么UAC必须按照[4]中描述的说明来认为输入的URI是SIPS的URI。
本地策略可以指定一套额外的目的地用于发送。如果Request-URI包含一个SIPS URI,任何额外的目的地都必须用TLS来表达。除此之外,如果请求没有包含Route头域,那么就没有对额外的目的地有什么其他的限制了。这个就提供了一个简单的外发(outbound)proxy的事前路由的选择。但是,用这样的方法配置一个外发proxy是不推荐的;应该由单个UPI规定的预先设定的路由集来指定外发proxy。如果请求包含了Route头域,请求应该发送到Route头域最上边的一个位置,但是请求也可能被发给由本文档约定的Route或者Request-URI所指定的服务器(同RFC2543定义的相反)。特别的,一个配置了外发proxy的UAC应该首先尝试把请求发送给由第一个Route头域值指定的位置,而不是采用把所有消息都发给外发proxy的策略。这就保证了外发的proxy通过不增加Record-Route头域而不参与后续请求的路径。这个也允许让不能分析第一个Route URI的终端,把请求交给外发proxy来发送。UAC应该遵循[4]中定义的过程来实现有状态的元素,尝试每一个地址直到连接到一个服务器。每一个尝试都是一个事务,因此,每一个都有一个不同的Via头域值和一个新的branch参数值。
此外,在Via头域中的transport的值被设置成为要到目标服务器所必须的transport。
8.1.3 处理应答
应答首先是被transport层处理,并且被transport层发送给上一层transaction层处理。transaction层处理完成之后将应答发送给更上一层TU处理。在TU层进行的对应答的主要处理是方法相关的。但是也有集中通用的处理原则:
8.1.3.1: transaction 层的错误
在某些情况下,从transaction层返回的应答不一定是一个SIP消息,而是一个transaction层的错误。当从transaction层收到一个timeout错误的时候,必须将这个timeout错误当作是收到了一个状态码是408(请求timeout)的应答。如果transport层报告了一个严重错误(通常取决于UDP传输中的严重的ICMP错误,或者是TCP连接中的错误),必须把这个错误当作是状态码503(服务未提供)的错误。
8.1.3.2 未知的应答
UAC必须把自己不认识的所有最终应答当作是x00的那类应答,当然UAC也必须能够处理所有的类别应答的x00的应答。比如,如果UAC收到了不认识的应答代码431,他可以很安全的假设在他发出的请求中有什么地方弄错了,并且可以很简单的把这个应答错误当作是接收到了一个应答代码是400(非法请求)的错误应答。并且,UAC必须能够处理所有的不认识的非终结应答响应当作是183(session progress)。一个UAC必须能够处理100和183应答。
8.1.3.3 Vias
如果在应答中,有不只一个Via头域值存在,那么UAC应该丢弃这个消息。包含超过一个Via头域值的消息是因为被错误的路由或者消息被破坏。
8.1.3.4 处理3xx应答
由于接收到一个重定向的应答(比如,状态码是301的应答),客户端应该用在Contact头域中的URI(s)来组织一个或者多个基于重定向以后的新请求,这个处理过程同proxy处理一个3xx类别的应答很类似,相关资料在16.5节和16.6节中有描述。客户发起请求的时候只有一个目标URI,就是原始请求中的Request-URI。如果客户端想在这个请求基础上重构一个基于3xx类别应答的新请求,那么就需要把这个需要尝试的URIs放到目标集合中去。基于本规范的规定,一个客户端可以选择放置那个Contact URIs到目标集合中(target set)。同proxy会递归一样,客户端处理3xx应答的时候必须不能重复添加任何URI到target set。如果原始请求的Request-URI头域中包含了一个SIPS URI,那么客户端可以选择改乘一个非SIPS URI,但是需要知会客户转发到一个非安全的URI去。任何新的请求都可能导致接收到这些请求的3xx应答,并且在Contact中包含原始的URI。可以通过对两个位置的配置来形成互相重定向。只要保证在target set中任何URI都只出现1次就能避免无穷的重定向循环。当target set增长了,客户端可
以对这个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:

你可能感兴趣的:(SIP协议详解(中文)-3)