本文记录开发、实现IMS项目时,整理的SIP协议基础知识;若有侵权,请告之。
SIP协议
- 1. SIP协议简介
SIP是一个应用层的控制协议,可以用来建立、修改、和终止多媒体会话(或者会议)例如Internet电话。SIP也可以邀请参与者参加已经存在的会话,比如多方会议。媒体可以在一个已经存在的会话中方便的增加(或者删除)。SIP显示的支持名字映射和重定向服务,这个用于支持个人移动业务-用户可以使用一个唯一的外部标志而不用关系他们的实际网络地点。SIP在建立和维持终止多媒体会话协议上,支持5个方面:
用户定位:检查终端用户的位置,用于通讯。
用户有效性:检查用户参与会话的意愿程度。
用户能力:检查媒体和媒体的参数。
建立会话:”ringing”,建立会话参数在呼叫方和被叫方。
会话管理:包括发送和终止会话,修改会话参数,激活服务等等。
SIP不是一个垂直集成的通讯系统。SIP可能叫做是一个部件更合适,它可以用作其他IETF协议的一个部分,用来构造完整的多媒体架构。比如,这些架构将会包含实时数据传输协议(RTP)(RFC1889)用来传输实时的数据并且提供QoS反馈,实时流协议(RSTP)(RFC2326)用于控制流媒体的的传输,媒体网关控制协议(MEGACO)(RFC3015)用来控制到公共电话交换网(PSTN)的网关,还有会话描述协议(SDP)(RFC2327)用于描述多媒体会话。因此,SIP应该和其他的协议一起工作,才能提供完整的对终端用户的服务。虽然基本的SIP协议的功能组件并不依赖于这些协议。
SIP本身并不提供服务。但是,SIP提供了一个基础,可以用来实现不同的服务。比如,SIP可以定位用户和传输一个封装好的对象到对方的当前位置。并且如果我们利用这点来通过SDP传输会话的描述,对方的用户代理立刻可以得到这个会话的参数。如果我们用这个像传输会话描述(SESSIONDESCRIPTIONSD)一样呼叫方的照片,一个”呼叫ID”服务很容易就建立了。这个简单的例子说明了,SIP作为一个基础,可以在其上提供很多不同的服务。
SIP并不提供会议控制服务(比如议席控制或者投票系统),并且并没有建议会议应该则那样管理。可以通过在SIP上建立其他的会议控制协议来发起一个会议。由于SIP可以管理参与会议的各方的会话,所以会议可以跨异构的网络,SIP并不能,也不打算提供任何形式的网络资源预留管理。
安全对于提供的服务来说特别重要。要达到理想的安全程度,SIP提供了一套安全服务,包括防止拒绝服务,认证服务(用户到用户,代理到用户),完整性保证,加密和隐私服务。
SIP可以基于IPV4也可以基于IPV6
- 2. SIP协议常用术语说明
Address-of-Record:Address-of-Record(AOR)是一个SIP或SIPS URI,它指向带位置服务的一个域,位置服务可以将一个URI与另一个URI(可能找到用户的URI)映射。典型的,通过注册来填写位置服务。通常认为AOR是用户的“公开地址”。
Back-to-Back User Agent:背对背用户代理(B2BUA)是一个逻辑实体,它接收请求,并作为用户代理服务器(UAS)处理该请求。为确定如何应答一个请求,它作为用户代理客户端(UAC)并生成请求。与代理服务器不同,B2BUA保持对话状态,并参与其建立的对话中发送的所有请求。由于B2BUA是UAC和UAS间的连接,所以不需要明确定义其行为。
Call:呼叫是一个非正式术语,它是指对等实体间的通信,通常是建立多媒体对话。
Call Leg:对话[31]的另一个名称,本规范中没有使用。
Call Stateful:如果一个代理从初始INVITE到终止BYE请求都保留了对话的状态,那么该代理有呼叫状态。有呼叫状态代理通常是有状态事务。但反之则不一定成立。
Client:代理是发送SIP请求和接收SIP响应的任何网络元素。客户端可以与也可以不与用户直接交互。用户代理客户端和代理都是客户端。
Conference:包含多个参与者的多媒体会话。
Core:核心指明特殊SIP实体类型特有的功能,即有状态或无状态代理、用户代理和注册员特有的。所有核心,除无状态代理的核心外,都是事务用户。
Dialog:对话是两个UA间持续一段时间的对等SIP关系。对话由SIP消息建立,如INVITE请求的2xx响应。用呼叫标识符、本地标签和远程标签标识对话。对话即RFC2543中的呼叫腿。
Downstream:在一个事务中转发消息的方向,它是指请求从用户代理客户端流向用户代理服务器的方向。
Final Response:终止SIP事务的响应,与不终止SIP事务的临时响应相反,所有2xx、3xx、4xx、5xx和6xx响应都是最终响应。
Header:头是SIP消息的一个组件,它传递消息的信息。构造头字段作为头字段序列。
Header Field:头字段是SIP消息头的一个组件。一个头字段可以是一个或多个头字段行。头字段行由一个头字段名和零或多个头字段值组成。给定的头字段行中的多个头字段值用逗号隔开。某些头字段只能有一个头字段值,因此,它们通常只有一个头字段行。
Header Field Value:头字段值是一个值,头字段由零或多个都字段值组成。
Home Domain:此域为SIP用户提供服务。典型的,它通常是注册的记录地址中URI中出现的域。
Informational Response:与临时响应相同。
Initiator, Calling Party, Caller:用INVITE请求发起会话(和对话)的一方。从发送建立对话的初始INVITE请求到该对话终止,呼叫者保持此角色。
Invitation:一个INVITE请求。
Invitee, Invited User, Called Party, Callee:接收用于建立新会话的INVITE请求的一方。从接收用于建立对话的初始INVITE请求到该对话终止,被呼叫者保持此角色。
Location Service:SIP重定向或代理服务器使用位置服务,以获得被呼叫者可能位置的信息。它包含记录地址与零或多个联系地址的绑定列表。有多种创建和删除这种绑定的方法。本规范定义了更新这种绑定的REGISTER方法。
Loop:一个请求到达代理,然后被转发,最后又返回到同一个代理。当请求第二次到达代理时,Request-URI与第一次相同,其它影响代理操作的头字段也不变,因此代理能对该请求作出与第一次相同的处理决定。循环的请求是错误的,由协议描述检测和处理循环请求的程序。
Loose Routing:如果代理遵循本规范定义的处理路由头字段的程序,那么我们就称其为松散路由选择。该程序将请求的目的地址(在Request-URI中)与请求在路由中要访问的代理(在路由头字段中)相分离。符合此机制的代理即是松散路由器。
Message:协议中SIP元素间发送的数据。SIP消息是请求或响应。
Method:方法是请求调用服务器上的基本功能。方法在请求消息中。方法的实例有INVITE和BYE等。
Outbound Proxy:从客户端接收请求的代理,尽管它可能不是Request-URI解析的服务器。一般手动为UA配置一个出站代理,或通过自动配置协议获知一个代理。
Parallel Search:在并行搜索中,代理在接收到入站请求后,向可能的用户位置发布几个请求。在顺序搜索中,发送一个请求后,代理等待最终响应,而不是立即发送下一个请求;并行搜索在发送请求时,不等待先前请求的结果。
Provisional Response:服务器使用该响应指示进行的响应,该响应不终止SIP事务。1xx响应是临时响应,其它响应都认为是最终响应。
Proxy, Proxy Server:一个媒介(中间)实体,它既作为服务器,也作为客户端(代替其它客户端发送请求)。代理服务器主要执行路由选择,即其职责是确保请求发送到一个离目标用户“更近”的实体。代理也可用于执行策略(例如,确定一个用户可以发起呼叫)。代理负责解释,如果需要的话,在转发请求前重写请求消息的特定部分。
Recursion:当客户端在响应的Contact头字段中产生一个或多个URI的请求时,客户端递归一个3xx响应。
Redirect Server:重定向服务器是用户代理服务器,它为接收到的请求生成3xx响应,告诉客户端联系一个可能的URI集。
Registrar:注册员是服务器,它接收REGISTER请求,并把从请求中接收的信息放入其所在的域的位置服务中。
Regular Transaction:正常事务是方法非INVITE、ACK和CANCEL的事务。
Request:为调用一个特殊的操作。客户端向服务器发送的SIP消息。
Response:为指明客户端向服务器发送的请求的状态,服务器向客户端发送的SIP消息。
Ringback:回铃是呼叫者的应用产生的信令音调(signalingtone),表明被呼叫者正在响铃。
RouteSet:路由集是按顺序的SIP或SIPSURI的集合,它是发送特殊请求时必须遍历的代理的列表。路由集是可通过头如Record-Route识别的,或是可配置的。
Server:服务器是一个网络元素,它接收请求,并为这些请求发送响应。服务器的实例有代理、用户代理服务器、重定向服务器和注册员。
Sequential Search:在顺序搜索中,代理服务器按顺序尝试每个联系地址,在前一个产生最终响应后再开始尝试下一个。通常用2xx或6xx最终响应终止一个顺序搜索。
Session:在SDP规范中这样描述:“多媒体会话是多媒体发送者、接收者以及发送者和接收者间数据流的集合。多媒体会议是多媒体会话的实例。”(RFC2327[1])(定义的SDP会话包含一个或多个RTP会话。)正如所定义的,在同一会话中,被呼叫者可以被不同的呼叫邀请多次。如果使用SDP,会话由源字段中的SDP用户名、会话ID、网络类型、地址类型和地址元素的串接定义。
SIPTransaction:SIP事务在客户端和服务器之间发生,包含从客户端向服务器端发送的第一个请求到服务器端向客户端发送的最后一个响应(非1xx)间的所有消息。如果请求是INVITE而最后的响应不是2xx,那么事务也包含响应的ACK。INVITE请求的2xx响应的ACK是一个独立的事务。
Spiral:螺旋是发送到代理,继续转发,再次回到该代理的SIP请求,但是这次将做出不同于原始请求的处理决定,通常表示请求的Request-URI与先前的不同。螺旋不是一个错误情况,与循环不同。典型的原因是呼叫转发。一个用户呼叫joe@example.com.com,代理将其转发到Joe的PC,PC反之将其转发给bob@example.com。该请求再次转发到.com代理。但是,这不是循环。因为此时该请求的目标用户不同于上次,它被称为螺旋,这是一种有效的情况。
Stateful Proxy:本规范定义的在处理请求过程中,维持客户端和服务器事务状态机的逻辑实体,也称为事务有状态代理。有状态代理行为的详细定义见第16节。(事务)有状态代理不同于呼叫有状态代理。
Stateless Proxy:本规范定义的在处理请求过程中,不维持客户端和服务器事务状态机的逻辑实体。无状态代理转发它从下游接收到的每个请求和从上游接收的每个响应。
Strict Routing:如果代理遵循RFC2543以及本版本的先前工作中的路由处理规则,则称代理为严格路由。使用严格路由规则时,当出现Route头字段时,代理会损坏Request-URI的内容。本规范没有使用严格路由行为,而是使用松散路由行为。实现严格路由的代理称为严格路由器。
Target Refresh Request:将在对话中发送的目标更新请求定义为请求,它能修改对话的远程目标。
Transaction User(TU):传输层上的协议处理层。事务用户包括UAC核心、UAS核心和代理核心。
Upstream:事务中消息的转发方向,它是指响应从用户代理服务器流向用户代理客户端的方向。
URL-encoded:根据RFC2396编码的字符串,见第2.4[5]节。
User Agent Client(UAC):用户代理客户端是创建新请求,然后使用客户端事务状态机发送请求的逻辑实体。UAC角色仅存在事务的持续时间内。换言之,如果软件发起一个请求,它仅在该事务的持续时间内是一个UAC。如果随后它接收到一个请求,在处理此事务时,它被假想成一个用户代理服务器角色。
UAC Core:事务和传输层上的UAC的处理功能集。
User Agent Server(UAS):用户代理服务器是一个逻辑实体,它产生SIP请求的响应。响应接收、拒绝和重定向请求。该角色仅存在于事务期间。换言之,如果软件对请求作出响应,那么它在事务期间就是UAS。如果随后它产生一个请求,那么在处理事务的期间我们就把它假想成用户代理客户端。
UASCore:事务和传输层上UAS的处理功能集。
User Agent(UA):既能作为用户代理客户端又能作为用户代理服务器的逻辑实体。角色UAC和UAS,以及代理和重定向服务器都是定义在事务的基础上的。例如,当用户发起一个呼叫发送初始INVITE请求时,它作为UAC;当它从被呼叫者接收BYE请求时,作为UAS。类似的,同一软件能作为一个请求的代理服务器和下一个请求的重定向服务器。以上定义的代理、位置和注册服务器都是逻辑实体。实现时可能将它们结合到一个应用中。
- 3. SIP协议结构
SIP是一个分层协议,这就意味着其行为用相对独立的处理阶段集来描述,每个阶段间松耦合。为便于表述,协议的行为用层来描述,允许对功能的描述跨越元素。但是它没有规定实现。当我们说一个元素“包含”一层,我们的意思是说元素遵从该层定义的规则。并非协议指定的每个元素都包含每一层。而且,SIP所指的元素都是逻辑元素,而非物理元素。物理实现可作为不同逻辑元素,甚至是基于事务(transaction-by-transaction)的。
SIP的最低层是语法和编码。其编码指定使用巴科斯范式(BNF)
第二层是传输层。它定义在网络上客户端如何发送请求和接收响应,服务器如何接收请求和发送响应。所有SIP元素都包含传输层。
第三层是事务层。事务是SIP的基础组件。事务是客户端事务(使用传输层)向服务器事务发送的请求,以及服务器事务向客户端发回的该请求的响应。事务层处理应用层转播、响应与请求的匹配以及应用层超时。用户代理客户端(UAC)完成任何任务都使用一系列事务。用户代理包含一个事务层,如有状态代理。无状态代理不包含事务层。事务层有一个客户端组件(称为客户端事务)和服务器组件(称为服务器事务),它们都用有限状态机表示,用来处理特殊请求。
事务层之上的层称为事务用户(TU)。每个SIP实体,除无状态代理外,都是事务用户。当事务用户想发送请求时,它就创建一个客户端事务实例,并将请求与目的IP 地址、端口一起发送。创建客户端事务的TU 也可以取消事务。客户端取消事务的时候,就要求服务器停止进一步处理,并恢复到初始化事务前的状态,然后返回该事务的一个错误响应。可通过CANCEL请求完成取消事务,CANCEL请求包含自己的事务,同时也提及需要取消的事务。
SIP元素即用户代理客户端和服务器、无状态和有状态代理、注册服务器,包含区分这些元素的核心(Core)。除无状态代理外,核心是事务用户。UAC 和UAS核心的行为依赖于方法,所有方法有一些通用规则(见第8 章)。对UAC 而言,规则支配请求的结构;对UAS而言,规则管理请求的处理和响应的生成。由于注册在SIP中扮演很重要的角色,处理REGISTER 的UAS有一个特殊的名称“注册员”。第10 章描述了REGISTER 方法的UAC、UAS核心行为。
其它的请求都在对话中发送。对话是在两个用户代理间持续一定时间的对等SIP关系。对话促成两个代理间消息顺序和请求的正确路由。INVITE方法是本规范中定义的用于建立对话的唯一方法。当UAC 在对话的连接中发送一个请求时,它遵通用UAC规则以及对话中请求规则。
SIP中最重要的方法是INVITE方法,它用于在参与者间建立会话。会话是参与者和参与者间通信的媒体流的集合。
- 4. SIP消息结构
SIP消息是从客户端到服务器的请求,或从服务器到客户端的响应。
尽管语法在字符集和语法细节上不同,请求和响应都使用基本的RFC 2822格式。(SIP允许头字段不是有效的RFC 2822头字段。)两种类型的消息都由一个起始行、一个或多个头字段、一个标识头字段结束的空行和一个可选消息体组成。
generic-message=start-line
*message-header
CRLF
[message-body]
start-line=Request-Line/Status-Line
起始行、每个消息头行和空行必须以回车换行序列(CRLF)终止。注意:即使没有消息体,也必须有空行。
除了上面所述的字符集不同外,SIP消息和头字段语法大部分与HTTP/1.1相同。
4.1. 请求
SIP请求的起始行有Request-Line,作为与其它消息的区分。Request-Line包含一个方法名、一个Request-URI和由空格(SP)字符分开的协议版本。
Request-Line以CRLF结束。除在终止行CRLF序列中外,其它的地方都不允许CR或LF。在元素中不允许任意数量的空格(LWS)。
Request-Line=Method SP Request-URI SP SIP-Version CRLF
Method:用于注册Contact信息的REGISTER;用于建立会话的INVITE、ACK和CANCEL;用于终止会话的BYE和用于查询服务器能力的OPTIONS。用于即时消息的MESSAGE;SIP扩展在RFC中可能有附加方法。
Request-URI:Request-URI是SIP或SIPS URI,描述见第17.1节,或是普通URI(RFC 2396)。它指明请求的目的用户或服务。Request-URI不能包含未保留空间(unescaped spaces)或控制字符,也不能包围在“<>”中。
SIP元素可能支持“SIP”和“SIPS”以外的Request-URI模式,如RFC 2806中的“tel”URI模式。SIP元素可使用任何机制将非SIPURI转换成SIPURI、SIPS URI或其它模式。
SIP-Version:请求和响应消息都包含所使用的SIP版本号,遵循[H3.1](用SIP替换HTTP、SIP/2.0替换HTTP/1.1)中关于版本次序、规范要求和版本号更新的描述。为遵从本规范,发送SIP消息的应用必须包含SIP-Version“SIP/2.0”。SIP-Version字符串区分大小写,实现时必须发送大写形式的字符串。
与HTTP/1.1不同,SIP将版本号视为字符串。而实际上,这是没有区别的。
例如:
4.2. 应答
SIP响应在起始行中有一个Status-Line,作为与请求的区分。Status-Line依次由协议版本号、数字Status-Code和以及相关的文本分析(textual phrase)组成,它们之间用字符SP隔开。
除在最后的CRLF序列中,其他地方不允许有CR或LF。
Status-Line=SIP-Version SP Status-Code SP Reason-Phrase CRLF
Status-Code是一个三位整数的结果代码,它指明尝试理解和满足请求的结果。Reason-Phrase是Status-Code的简短的文本描述。Status-Code用于自动控制,Reason-Phrase便于人理解。客户端不需要检查或显示Reason-Phrase。
建议这些原因分析的一些特定用语,实现可以先用其它的文本,例如,在请求的Accept-Language头字段中指明的语言。
Status-Code的第一个数字定义响应类型。后面的两位数字没有类别的意义。因此,状态代码在100到199之间的响应成为“1xx”响应,状态代码在200到299之间的响应成为“2xx”响应,依此类推。SIP/2.0中第一个数字有六种值:
1xx:informational-已经收到请求、继续处理请求。
2xx:success-已经成功收到,理解和介绍行动。
3xx:Redirection-为完成呼叫请求,还须采取进一步地动作。
4xx:ClientError-请求有语法错误或服务器不能执行请求。
5xx:ServerError-服务器出错,不能执行合法请求。
6xx:GLOBALFAILURE-任何服务器都不能执行请求。
例如:
4.3. 头字段
在语法和语义方面,SIP头字段和HTTP头字段很相似。特别的,SIP头字段遵循HTTP/1.1中消息头语法的定义,以及扩展多行头字段的规则。但是,后者在HTTP中有隐含的空白和折叠(white space and folding)。本规范遵从RFC 2234,使用明确的空白和折叠作为语法的完整部分。
HTTP/1.1中指出其值用逗号分隔的、具有相同字段名的多个头字段能组合成一个头字段。这同样适用于SIP,但对不同的语法有特殊的规则。特别的,SIP头的语法格式如下:
header=header-name HCOLON header-value *(COMMA header-value)
它允许将名称相同的头字段组合成用逗号分隔的一列。除非头字段值为“*”,Contact头字段允许用逗号分隔。
4.3.1. 头字段格式
头字段遵循RFC 2822的第2.2节中的通用头格式。每个头字段依次由:字段名、冒号“:”和字段值组成。
field-name:field-value
消息头的正式语法允许冒号的两边有任意多个空格;但是,实现时应避免字段名和冒号间的空白,在冒号和字段值间使用一个空格(SP)。
Subject: lunch
Subject : lunch
Subject :lunch
Subject: lunch
因此,以上格式都是有效格式,且意义相同,但最后一个是首选格式。
头字段可扩展为多行,方法是在每个附加行前添加一个空格(SP)或横向制表(HT)。一行的结束以及下一行开始的空格都看作一个空格(SP)字符。因此,以下两种方式意义相同:
Subject: I know you’re there,pick up the phone and talk to me!
Subject: I know you’re there,
pick up the phone
and talk to me!
不同字段名的头字段的相对顺序并不重要。但推荐需要代理处理的头字段(例如,Via、Route、Record-Route、Proxy-Require、Max-Forwards和Proxy-Authorization)放在消息的头部,以便于快速解析。相同字段名的头字段行的相对顺序非常重要。带相同字段名的多个头字段行仅出现在这样的消息中:头字段的整个字段值的定义是用冒号分隔的一列。我们可以将多个头字段行组合成一个“field-name:fieldvalue”对,而不改变消息的语义,方法是依次将每个字段值添加到第一个后,它们之间用逗号分隔。WWW-Authenticate、Authorization、Proxy-Authenticate和Proxy-Authorization不符合本规则,字段名为上述项的多个头字段行可能在一个消息中同时出现,因此不能将它们组合成一个头字段行。
实现应能以每行一值或逗号分隔值的格式处理相同名称的多个头字段行。
以下几组头字段行是合法的并且意义相同:
Route: <sip:alice@atlanta.com>
Subject: Lunch
Route: <sip:bob@biloxi.com>
Route: <sip:carol@chicago.com>
Route: <sip:alice@atlanta.com>, <sip:bob@biloxi.com>
Route: <sip:carol@chicago.com>
Subject: Lunch
Subject: Lunch
Route: <sip:alice@atlanta.com>, <sip:bob@biloxi.com>, <sip:carol@chicago.com>
以下几组是合法的,但意义不同:
Route: <sip:alice@atlanta.com>
Route: <sip:bob@biloxi.com>
Route: <sip:carol@chicago.com>
Route: <sip:bob@biloxi.com>
Route:<sip:alice@atlanta.com>
Route: <sip:carol@chicago.com>
Route: <sip:alice@atlanta.com>, <sip:carol@chicago.com>, <sip:bob@biloxi.com>
头字段值的格式按头名定义。它通常是一个TEXT-UTF8八位位组序列,或一个空格、标记、分隔符和引用串的组合。许多现用的头字段遵循这样的通用格式:字段值,随后是分号分隔的参数名、参数值对:
field-name: field-value*(;parameter-name=parameter-value)
尽管在头字段值后可附加任意多个参数对,但任何给定的参数名不能出现多次。比较头字段时,字段名是不区分大小写的。除非在特定头字段的定义中有说明外,字段值、参数名和参数值都是不分大小写的。标记通常不区分大小写。除非特殊说明,引用串值要区分大小写。例如:
Contact:<sip:alice@atlanta.com>;expires=3600
与CONTACT: <sip:alice@atlanta.com>;ExPiReS=3600意义相同;
且Content-Disposition: session;handling=optional与
content-disposition: Session;HANDLING=OPTIONAL意义相同。
以下两个头字段意义不同:
Warning: 370 dev null "Choose a bigger pipe"
Warning:370 dev null "CHOOSE A BIGGER PIPE"
4.4. 消息体
4.4.1. 消息体类型
消息体的Internet媒体类型由Content-Type头字段指定。如果对体进行了任何编码,如压缩,那么必须在Content-Encoding头字段中指出。否则,必须省略Content-Encoding。如果可行,消息体的字符集作为Content-Type头字段值的一部分。
可以在消息体中使用RFC2046 [10]中定义的“多方”MIME 类型。如果远程实现请求,通过不包含多方的Accept头字段请求非多方消息体,那么发送包含多方消息体的请求的实现,必须发送一个会话描述,作为非多方消息体。
SIP消息可能包含二进制体或体部分。如果发送者没有明确指定字符集参数,定义“文本”类型的媒体子类型有默认的字符集值“UTF-8”。
4.4.2. 消息体长度
在Content-Length 头字段中指定以字节计算的体长度。第20.14 节详细描述了头字段的必须内容。
HTTP/1.1 的“分块”传输编码不能用于SIP。(注意,为以分块序列传输消息,“分块”编码修改消息体,每一个都有大小指示器)
- 5. 通用用户代理行为
用户代理表示终端系统。它包括用户代理客户端 (UAC)和用户代理服务器(UAS),UAC生成请求,UAS响应请求。外部因素(用户点击按钮或PSTN线信号)促使UAC生成请求,并处理响应。UAS接收请求,并且根据用户输入、外部因素、程序执行结果或者其它机制,生成响应。
当UAC发送请求时,请求通过代理服务器,可以将这些请求转发给UAS。当UAS生成响应,响应将转发给UAC。
UAC和UAS过程取决于两个因素。第一个是,请求或响应是在对话的内部还是外部;第二个是,请求的方法。
在本章,我们讨论了处理对话外的请求时,UAC和UAS行为独立于方法的规则。这当然包括自己建立对话的请求。
5.1. UAC行为
介绍对话外的UAC 行为。
5.1.1. 生成请求
UAC 制定的有效SIP请求,至少必须包括以下头字段:To、From、Cseq、Call-ID、Max-Forwards 和Via。在所有的SIP请求中,这些头字段都是必需的。这六个头字段是SIP消息基本的构件块,它们共同提供大部分关键性消息路由服务,包括消息的寻址、响应的路由、限制消息的传播、消息的排序和事务的唯一标识符。UAC 制定的有效SIP请求除了包含这些头字段外,还有必需的请求行。这个请求行包含了方法、Request-URI和SIP版本。
Request-URI消息初始Request-URI应该设置成To字段的URI值。但应注意,REGISTER方法例外。保密性原因或者便于将这些字段设置成相同的值(特别是在传输过程中,原始UA期望改变Request-URI),可能不符合需要。
在一些特殊的情况中,预有的路由集合的存在可能影响消息的Request-URI。预有的路由集合可能是用来识别一系列服务器的URI有序集合,UAC将对话外的出站请求发送到其中一个服务器上。通常,预有的路由集合由用户或者服务提供商在UA上手动配置,或者通过一些其它的非SIP机制配置。当提供商希望用带外代理配置UA时,推荐通过为UA提供一个带外代理预有的带单一URI的路由集合来配置UA。
To To头字段首先指明了想要的请求的“逻辑”接收者或者用户的记录地址或者作为请求目标的资源。这不一定是请求的最终接收者。To字段可能包含SIP或者SIPS URI,在适当的时候,它也可以使用其它URI模式(例如,tel URL (RFC 2806 [8]))。所有SIP执行必须支持SIPS URI模式。任何支持TLS的执行必须支持SIPS URI模式。
UAC可以知道怎样以多种方法为特定的请求填充To头字段。通常,用户建议To头字段通过用户界面填充——可能是手动输入URI或者从地址本中选择。通常,用户不必键入完整的URI,而是键入数字或字符串(如,“bob”)。这由UA来选择怎样解释此输入。使用字符串来形成用户SIPS URI的一部分,意味着UA希望此名字可以在SIPS URI注册的右边(RHS)进行域名解析(如,sip:bob@example.com)。使用字符串来形成用户SIPS URI的一部分,意味着UA 希望可以安全地通信,同时,此名字可以在[@]的右边进行域名解析。右边通常是请求者的归属域,这考虑了处理出站请求的归属域。这种像“快速拨号”的特征很有用,它需要归属域的用户部分的解释。
当UA 不希望指明应该解释用户输入的电话号码的域时,可以使用tel URL。相反,UA希望指明请求通过的每个域。例如,在飞机场的用户可能已经登机,通过飞机场的带外代理发送请求。如果键入“411”(美国本地目录帮助的电话号码),这需要通过飞机场——而不是用户归属域的带外代理解释并处理它。在这种情况中,tel:411 将是正确的选择。
对话外的请求不能包含To 标签,请求中的To 字段标签表示对等对话。因为没有建立对话,所以不存在标签。
From From头字段表示请求发起者的逻辑身份,有可能是用户的记录地址。和To字段一样,它包含了URI和显示名称,显示名称是可选的。SIP元素用它来确定应用于请求的处理规则(如,自动呼叫拒绝)。同样地,因为没有逻辑名字,不包含IP地址或者UA运行主机的正式域名的From URI很重要。
From头字段考虑了显示名称。如果客户端的身份是隐藏时,UAC应该使用显示语法正确的名字“匿名的(anonymous)”,而不是无意义的URI(像sip:thisis@anonymous.invalid)。
通常,特殊UA产生的、在请求中填充到From头字段的值是用户或者用户本地域的管理员预先提供的。如果多个用户使用特殊UA,那么,它具有可交换的描述,包括对应于描述用户身份的URI。为了确定它们是From 头字段所声称的人,请求的接收者可以认证请求的发起者。
From 字段必须包含UAC选择的新标签参数。
Call-ID Call-ID头字段作为集合一系列消息的唯一标识符。在对话中,每个UA 发送的所有请求和响应中,Call-ID 必须是一样的。UA 的每个注册中,它应该是一样的。
在UAC 创建的对话外的新请求中,如果不是特定方法行为覆盖的,UAC选择的Call-ID头字段必须是在时间和空间上全球唯一的标识符。所有的SIPUA必须有一种方法来保证其他UA 不会产生它们产生的Call-ID头字段。注意,当在特定的失效响应后,重发请求以修正请求时(如,认证挑战),重的请求将不作为新的请求,因此不需要新的Call-ID 头字段;
推荐在生成Call-ID 时,使用密码学上的随机标识符(RFC 1750 [11])。执行时可以使用这种格式“localid@host”。Call-ID 是区分大小写的,并且逐字节比较的。
使用密码学上的随机标识符提供了会话截获保护,并减少了Call-ID 冲突的可能性。
对于选择请求的Call-ID 头字段的值,不需要规章界面或用户界面。
CSeq CSeq头字段是用作识别和指示事务的。它由序列号和方法组成。此方法必须和请求相匹配。对于对话外的非REGISTER 请求,此序列号是任意的。此序列号的值必须是值小于2~31 的32 位的无符号整数。只要遵循上述原则,客户端就可以随意地使用一种机制来选择CSeq 头字段值。
Max-Forwards Max-Forwards头字段是用作限制请求传输到其目的地跳跃的点数。它是一个整数,在每个跳跃点上减一。如果请求在到达其目的地之前,Max- Forwards 值到0,将返回483(太多跳跃点)错误响应,拒绝其请求。
UAC必须在每个请求中插入Max-Forwards头字段,并赋初始值为70。此数字足够长,可保证在没有环路时,请求不会在SIP网络中丢失;当存在环路时,此数字不会消耗代理太多的资源。要谨慎地使用更小的值,只有在UA知道网络拓扑时,才可以使用更小的值。
Via Via头字段表示事务中使用的传输,并标识了响应发送的位置。仅在选择了要到达的下一个跳跃点后(这可能包括[4]中过程的使用),才在传输中加上Via 头字段的值。
当UAC创建请求时,它必须在请求中插入Via。在头字段中的协议名和协议版本必须分别是SIP和2.0。Via头字段值必须包含分支参数(branch parameter)。此参数用来识别请求创建的事务。此参数同时用于客户端和服务器。
对于UA发送的所有请求,其分支参数的值必须在时间和空间上是唯一的。此规则的异常是CANCEL和非2xx 响应的ACK。CANCEL请求有与它取消的请求相同的分支值。非2xx 响应的ACK也有与它所应答的INVITE响应相同的分支ID。
分支ID 参数唯一的特性,有助于其作为事务ID使用,和本规范一致的元素插入的分支ID 必须是以“z9hG4bk”字符开头。这七个字符用作magic cookie(7 认为是足以确保旧RFC 2543 执行不会选择这个值),以便于接收请求的服务器可以确定以本规范描述的格式(即全球唯一)构造分支ID。在本规范内,分支标签的精确格式是执行定义的(implementation-defined)。
当请求是通过传输层处理的,那么将发送Via头的maddr、ttl 和sent-by组件。
Contact Contact头字段提供了SIP或者SIPS URI,可用于为随后请求联系UA的具体实例。必须正确地表示Contact头字段,并包含任何请求中的SIP或者SIPS URI,这将导致建立对话。Contact的范围是全局的。即Contact头字段值包括UA要接收请求的URI,即使是在任何对话外的随后请求中使用,此URI都必须是有效的。
如果Request-URI或top Route头字段值包含SIPS URI,Contact头字段也必须包含SIPS URI。
Supported and Require 如果UAC支持SIP扩展,服务器可将此扩展用于响应,UAC应该在请求中引用Supported头字段,
列出的可选标签必须引用标准协议栈RFC中定义的扩展。这就防止了服务器为了接收
服务而坚持客户端执行非标准的、供应商定义的特性。因为,供应商也经常参考实验和情报的RFC 中定义的扩展,定义自己的扩展,所以,它们显然不能用在请求中的Supported头字段。
如果UAC坚持要UAS理解,UAC为处理请求而用于请求的扩展,它必须在请求中插入Require头字段,列出这些扩展的可选标签。如果UAC希望将扩展用于请求中,并坚持它所要经历的任何代理都可以理解这些扩展,它必须在请求中插入Proxy-Require头字段,列出这些扩展的可选标签。
和Supported 头字段一起,Require 和Proxy-Require 中头字段可选的标签必须仅仅引用标准协议栈RFC中定义的扩展。
Additional Message Components 在创建了新请求,并合理地构造上面所介绍的头字段后,可以添加任何其他的可选头字段,作为具体方法的头字段。
SIP请求可能包含MIME编码的消息体。不管请求包含的消息体是什么类型,必须阐明特定的头字段来说明主题内容的特征。
5.1.2. 发送请求
请求的目的地是可计算的。除非有本地的策略指明,目的地必须通过[4]中介绍的DNS过程才可以确定。如果路由集合中的第一个元素指示了严格的路由器,那么必须将DNS过程用于请求的Request-URI。否则,必须将DNS过程用于请求中的第一个Route头字段(如果存在),如果没有Route 头字段,将DNS过程用于请求的Request-URI。这些过程产生有序的地址集合、端口和传输。如果Request-URI指定SIP源,那么与过程[4]使用哪个URI无关;如果输入的URI是SIPS URI,那么UAC必须遵循过程[4]。
本地策略可以指定可选的目的地。如果Request-URI包含SIPS URI,那么,任何可选的目的地必须和TLS 联系。除此以外,如果请求中没有包含Route头字段,那么,就不约束可选的目的地。这提供了简单的可选机制——为预有的路由集合指定带外代理。然而,不推荐配置带外代理,反而应该使用单一URI 的预有的路由集合。如果请求包括路由头字段,那么请求应该发送到来自其最上面值的位置;但是也可以遵循本文档指定的路由和Request-URI策略发送至任何UA信任的服务器(与RFC 2543中相反)。特别的是,带外代理配置的UAC 应该试图发送请求给第一个Route头字段值指明的位置,而不是采用策略发送所有消息给带外代理。
这确保了没有添加Record-Route头字段值的带外代理将不参与随后请求的路径。它也允许不能解析第一个Route URI的终端,将此任务委派给带外代理。
对于有状态元素,UAC应该遵循[4]中定义的过程——尝试每个地址,直到联系到服务器。每次尝试构成新的事务,因此,每次用新的分支参数携带不同的最上面的Via头字段值。此外,在Via 头字段的传输值设置成每个目标服务器确定的传输。
5.1.3. 处理应答
响应最初是在传输层处理,然后传输到处理层。处理层完成其处理,然后将响应传输到TU。TU的大多数响应处理是指定方法的。但是,还有一些通用的行为与方法无关。
Transaction Layer Errors 在很多情况中,处理层返回的响应不是SIP消息,而是处理层错误。当从处理层接收到超时错误时,必须视为接收到了408(请求超时)状态代码。如果传输层报告了重大的传输错误(通常,由于UDP 的重大ICMP错误或者TCP连接错误),这种情况必须视为503(服务不可用)状态代码。
Unrecognized Responses UAC必须将其不识别的最终响应视为对等于x00类的响应代码,并且,UAC必须能够处理所有的x00响应代码。
例如,UAC接收到了不识别的响应代码431,它可以安全地设想此请求有问题,并将响应处理为接收到了400(错误请求)响应代码。UAC必须将不同于100 的、不识别的临时响应视为183(会话进行)。UAC 必须能够处理100和183 响应。
Vias 如果在响应中存在不止一个Via头字段,那么UAC应该丢弃这些信息。在请求发起者之前的其他Via 头字段值的状态,暗示消息是错误指向的或者是不可靠的。
Processing 3xx Responses 在接收到重定向响应后(如,301响应状态代码),客户端应该使用Contact头字段的URI来说明一个或者多个基于重定向请求的新请求。客户端从正确包含URI和原始请求Request-URI的初始目标集合开始。如果客户端希望阐明此请求的基于3xx类响应的新请求,它将此URI放在目标集合中。服从本规范的约束,客户端可以选择将哪个Contact URI放在目标集合中。随着代理递归,处理3xx 类响应的客户端不能够再在目标集合中添加任何给定的URI。如果原始请求有Request-URI中的SIPS URI,那么客户端可以选择换成非SIPS URI,但是应该通知用户不安全URI的重定向。
任何新请求都可能接收到包含其自己原始URI的3xx响应作为联系。两个位置可以互相配置成重定向。将任何给定的URI 仅在目标集合放置一次可以防止无穷的重定向环路。
随着目标集合的增长,客户端可以以任何顺序生成URI 新请求。通用的机制是按照Contact头字段值的‘q’参数值排序。URI请求可以是连续的,也可以是并行的。其中一种方法是,连续地处理递减的q 值排序,并行地处理URI 的每个q值。另一种方法是,仅仅连续地处理递减的q值排序,任意选择相等q值之间的联系。
如果联系列表中的地址造成错误,与在下一章所定义的一样,元素移到列表中的下一地址,直到列表用完为止。如果列表已经用完,那么此请求错误。
错误应该通过错误响应代码(比399大的代码)来检测;如果是网络错误,那么客户端事务将向事务用户报告传输层错误。注意,有些响应代码说明了重试的请求,重试的请求不应该认为是错误。
当接收到具体联系地址失败时,客户端应该尝试下一个连接地址。这包括创建新的客户端事务来传递新的请求。
为了在3xx 响应中创建基于联系地址的请求,UAC必须将目标集合中的整个URI复制到Request-URI,除了method-param和header URI参数。它使用头参数来创建新请求的头字段值。
注意,在一些实例中,在联系地址中通信的头字段可以改为添加到原始重定向请求的现有请求的头字段中。作为通用规则,如果头字段接受了逗号分隔的列标值,那么新的头字段可以添加到原始重定向请求的现有值中。如果头字段不能接受多个值,那么,可以用联系地址中通信的头字段值重写原始重定向请求。例如,如果与下列值一起返回联系地址:
sip:user@host?Subject=foo&Call-Info=<http://www.foo.com>
那么,将重写原始重定向请求的Subject 头字段,HTTP URI 不过是附加在现有的Call-Info 头字段值后面。
推荐UAC重用原始重定向请求中相同的To、From和Call-ID,但是UAC可以选择更新新请求的Call-ID 头字段值。
最后,一旦构造了新请求,那么,使用新的客户端事务来发送新请求,因此,必须在最上面Via字段中有新的分支ID。
无论从其他哪个方面来看,在接收重定向响应基础上发送的请求应该重用原始请求的头字段和消息体。
在一些实例中,与接收到的状态代码和逾时间隔的状态有关,可以在UAC中临时或者永久地缓存Contact头字段值。
Processing 4xx Responses 具体的4xx响应代码与方法无关,需要具体的UA处理。
如果接收到401(未经许可)或407(需要代理认证)响应,那么UAC应该遵循认证过程,使用凭证来重试请求。
如果接收到413(请求实体太大)响应,请求包含的消息体比UAS愿意接受的消息体长,如果可能的话,UAC应该忽略消息体或者使用较短的消息体重试请求。
如果接收到415(不支持的媒体类型)响应,UAS不支持请求中包含的媒体类型。UAC应该重试请求,此时仅使用响应中Accept头字段列出的类型,响应中Accept-Encoding头字段列出的编码和响应中Accept-Language头字段列出的语言。
如果接收到 416(不支持的URI 模式)响应,服务器不支持Request-URI使用的URI模式。客户端应该重试请求,此时使用SIPS URI。
如果接收到420(错误的扩展)响应,此请求包含Require 或者Proxy- Require头字段,列出了代理或者UAS不支持的可选标签特征。UAC应该重试请求,此时,忽略响应中扩展列出的Unsupported头字段。
在上面所有的情况中,通过适当的修改创建新请求,重试请求。新请求组成新事务,应该有与原来请求相同的Call-ID、To 和From 值,但是Cseq应该包含比原来高的新序列号。
其它的4xx响应,包括仍然在定义的,重试可以与也可以不与方法和使用案例有关。
5.2. UAS行为
当UAS处理对话外的请求时,与方法无关,要遵循一套处理规则。
注意,请求处理是基本的。如果接受请求,所有与其相关的状态改变必须执行。如果拒绝请求,所有的状态改变都不能执行。
5.2.1. 方法检查
一旦请求通过了认证(或者跳过认证),UAS必须检查请求的方法。如果UAS识别但是不支持请求的方法,它必须生成405(方法不允许)响应。UAS必须为405(方法不允许)响应添加Allow头字段。Allow头字段必须列出UAS生成消息支持的方法集合。
如果方法是服务器支持的一种,那么,将继续处理。
5.2.2. 包头检查
如果UAS不能理解请求中的头字段(即是没有在本规范或者其它支持的扩展中定义此头字段),那么服务器必须忽略此头字段,并继续处理消息。UAS应该忽略任何在处理请求中非必需的、非格式化的头字段。
To and Request-URI To头字段标识了原始请求中From字段中标识的用户。由于呼叫转移或者其它代理操作,原始接收者可以是也可以不是UAS处理此请求。当To头字段不是UAS身份时,UAS可以应用任何策略来确定是否接受请求。然而,即使在To头字段中有它们不识别的URI模式(如,tel:URI),或者To头字段没有指明UAS已知的或者当前用户,推荐UAS接受请求。另一方面,如果UAS决定拒绝此请求,它应该生成响应403(Forbidden)状态代码,并将其传送给服务器事务发送。
然而,Request-URI识别处理请求的UAS。如果Request-URI使用了UAS不支持的模式,它应该拒绝此请求,返回416(不支持URI 模式)响应。如果Request-URI不识别UAS将要接受请求的地址,它应该拒绝此请求,返回404(没找到)响应。典型地,使用REGISTER方法来为具体的联系地址绑定其记录地址的UA,将查找Request-URI与联系地址相等的请求。接收Request-URI的其它潜在资源包括,UA发送的、建立或者更新对话的请求和响应的Contact 头字段。
Merged Requests 如果请求在To头字段中没有标签,那么UAS核心必须检查正在进行事务的请求。如果From标签、Call-ID和Cseq和事务精确匹配,那么UAS核心应该生成482(发现环路)响应,并将其发送到服务器事务。
由于分发,相同的请求从不同的路径多次发送到UAS。UAS处理第一个接收到的这样的请求,对于第一个以外的请求,生成482(发现环路)响应。
Require 假定UAS认为Require是处理请求的要素,那么,如果存在Require的话,它将检查Require 头字段。
UAC使用Require头字段告诉UAS——为了正确地处理请求,UAC希望UAS支持的SIP扩展。如果UAS不理解Require 头字段中列出的可选标签,它必须返回状态代码420(错误的扩展)。UAS必须添加Unsupported头字段,并列出请求的Require 头字段中它所不理解选项。
注意,在SIPCANCEL中不能使用Require和Proxy-Require,或者发送ACK请求非2xx响应。如果在请求中出现了这些头字段,必须忽略它们。
ACK 请求2xx 响应必须仅包含在初始请求中出现的Require和Proxy-Require值。
以下是Require 的实例:
UAC -> AS: INVITEsip:watson@bell-telephone.com SIP/2.0
Require: 100rel
UAS-> UAC: SIP/2.0 420 Bad Extension
Unsupported: 100rel
当两边都理解了所有选项时,此行为确保了无延时地进行客户端-服务器交互;如果不理解选项,将减慢速度(如上例)。对于完好匹配的客户端-服务器对,节省了协商机制经常需要的来回路程,交互处理很快。
此外,当服务器不理解客户端请求的特征时,它可以模糊地移动。有些特征,如呼叫处理字段,仅是终端系统感兴趣的。
5.2.3. 内容处理
假定UAS理解客户端需要的任何扩展,UAS检查了消息体和描述它的头字段。如果不理解某一消息体的类型(Content-Type指明的)、语言(Content-Language指明的)或者编码(Content-Encoding指明的),并且此消息体部分不是可选(Content-Disposition字段指明的),那么UAS必须拒绝此请求,返回415(不支持的媒体类型)响应。如果请求中包含了UAS不支持的消息体类型,响应必须包括Accept头字段,列出它所理解的所有消息体。如果请求中包含了UAS不理解的编码,响应必须包括Accept-Encoding头字段,列出UAS所理解的编码。如果请求中包含了UAS不理解的语言,响应必须包括Accept-Language头字段,列出UAS所理解的语言。除了这些检查外,消息体处理还与方法及类型有关。
5.2.4. 应用扩展
除非是请求的Supported头字段中指明了支持的扩展,不允许UAS生成响应时应用扩展。如果不支持想要的扩展,那么服务器应该依靠基准SIP和客户端支持的扩展。在少数情况中,没有扩展,服务器不处理请求,服务器可能发送421(必需扩展)响应。此响应表示,没有支持指定的扩展,不能生成响应。此必需的扩展必须包含在响应的Require头字段中。不推荐使用此行为,因为它将破坏互操作性。
适用于非421响应的任何扩展必须在响应的Require头字段中列出来。当然,服务器不能应用响应的Require头字段没有列出来的扩展。结果,响应的Require头字段仅包含标准协议栈RFC中定义的可选标签。
5.2.5. 处理请求
假定通过了前面子章节的所有检查,UAS处理就成为面向具体方法的。后面章节分别介绍了REGISTER、OPTIONS 、INVITE、BYE、MESSAGE等请求处理。
5.2.6. 生成响应
当UAS希望为请求构造响应时,那么它遵循后面章节介绍的通用过程。在本节没有详细介绍的,正在讨论的响应代码的其他行为,也是必需的。
一旦与创建响应相关的所有过程完成,UAS就将这些响应发送给它所接收请求的服务器事务。
Sending a Provisional Response 生成响应大的、非面向具体方法的原则是,UAS不应该发布非INVITE请求的临时响应;而是,应该尽可能地生成非INVITE请求的最终响应。
当生成100(Trying)响应时,请求中的Timestamp头字段必须复制到100(Trying)响应中。如果在生成响应时有延时,那么UAS应该将延时加到响应的Timestamp 值中。此值必须包含以秒计算的、发送响应和接收请求的时间差。
Headers and Tags 响应的From 字段必须和请求的From 头字段相同,响应的Call-ID字段必须和请求的Call-ID 头字段相同,响应的CSeq 字段必须和请求的CSeq 头字段相同,
响应的Via字段必须和请求的Via头字段相同,并且必须保持相同的顺序。如果请求中包含请求的To标签,那么,响应中的To头字段必须和请求中的相同。然而,如果请求的To字段没有包含标签,那么,响应中的To头字段的URI必须和请求中的To头字段的URI 相同,此外,UAS必须在响应的To头字段中添加标签(除了100Trying)响应外,其中可能有标签)。这用于识别正在响应的UAS,可能会生成对话ID的组件。相同的标签必须用于该请求的所有响应,包括最终和临时的(也包括100(Trying))响应。
5.2.7. 无状态UAC行为
无状态UAS是不能保持事务状态UAS。它正常地回复请求,但是丢弃响应发送后的状态——通常UAS会保留其状态的。如果无状态UAS接收转发的请求,它将重新生成响应,并重新发送响应,就像它第一次收到请求一样。如果请求是一样的,除非请求处理的方法总是导致相同的结果,否则UAS不能是无状态的。例如,此规则输出无状态注册服务器。无状态代理不使用处理层,它们直接从传输层接收到请求,并直接将响应发送给传输层。
无状态UAS角色主要是处理发布挑战响应的不需认证的请求。如果对不需认证的请求进行有状态的处理,那么,恶意的不需认证的请求可以创建大量的事务状态,可能会减慢或者挂掉UAS的呼叫处理,实际上,将造成拒绝服务。
无状态UAS的大多数重要的行为如下:
² 无状态UAS不能够发送临时(1xx)响应。
² 无状态UAS不能够转发响应。
² 无状态UAS必须忽略ACK请求。
² 无状态UAS必须忽略CANCEL请求。
² 必须以无状态方式生成To 头字段——为相同请求一贯地生成相同标签的方式。
在其它各个方面,无状态UAS和有状态UAS是一样的。对于每个新请求,UAS可以以有状态或无状态的方式来操作
- 6. 注册(REGISTER)
SIP提供了发现机制。如果用户要发起和另一个用户的会话,SIP必须发现可到达目的用户的当前主机。发现处理经常是SIP网络元素完成,比如代理服务器和重定向服务器——它们负责接收请求,决定要发送请求的用户位置,然后将它发送到相应的位置。为了完成这些,SIP网络元素查询了抽象服务——定位服务,这提供了特定域的地址绑定。这些地址绑定将输入的SIP和SIPS URI(如,sip:bob@biloxi.com)映射到想要的用户“更近”的一个或多个URI(如,sip:bob@engineering.biloxi.com)。最后,代理将查询定位服务,将接收到的URI映射到想要的接收者常驻的用户代理。
注册创建了特定域中定位服务的绑定,它将记录地址 URI和一个或者多个联系地址相关联。因此,当域中的代理接收Request-URI和记录地址匹配的请求时,代理将请求转发给记录地址已注册的地址。通常,当请求记录地址路由到域中时,在域定位服务注册记录地址才有意义。在大多数情况中,这意味着注册的域需要与记录地址的URI域匹配。
建立定位服务内容有很多种方法。其中之一是管理。在上面的实例中,通过访问公司数据库可以知道Bob是工程部门的一员。但是,SIP为UA提供了一种机制明确地创建绑定。这种机制就是注册。
注册必须发送REGISTER请求给特定类型的UAS——即是注册服务器。注册服务器作为域中定位服务的前端,发送和写基于REGISTER内容的映射。随后主要是负责此域路由请求的代理服务器查询此定位服务。
SIP不强制执行定位服务的具体机制。唯一的要求是域中的注册服务器必须能够在定位服务中读和写数据,域中的代理和重定向服务必须能够读取相同的数据。在同一域内,注册服务器与特定的SIP代理服务器可以在同一接点。
6.1. 构造REGISTER请求
REGISTER请求添加、删除和查询绑定。REGISTER请求可以在记录地址和一个或多个联系地址之间添加新绑定。合适地通过认证的第三方可以完成代表特定记录地址的注册。客户端也可以删除以前的绑定或者查询绑定,确定记录地址绑定当前所在的位置。
除非另有说明,REGISTER请求的构造和客户端发送REGISTER请求的行为与第5.1节中介绍的通用UAC行为是一样。
REGISTER请求不建立对话。UAC可以在REGISTER请求中包括基于第5.1节介绍的预有的路由集合的Route头字段。在REGISTER请求和响应中的Record-Route头字段没有意义,如果存在,必须忽略。特别的,UAC不能根据REGISTER请求的任何响应中存在和缺少的Record-Route头字段创建新的路由集合。
下列头字段,除了Contact外,必须包含在REGISTER请求中。当然,也可以包括Contact头字段。
Request-URI: Request-URI指定了注册服务器指明的定位服务域。( 如sip:chicago.com)。不能出现SIPS URI的userinfo和@组件。
To: To头字段包括记录地址,可以创建、查询和修改其注册。To头字段和Request-URI字段主要的不同是,前者包含用户名。此记录地址必须是SIP或者SIPS URI。
From: From头字段包含负责注册的人的记录地址。除非是第三方注册,此值和To头字段的值是一样的。
Call-ID: UAC所有的注册应该使用与发送到注册服务器的注册相同的Call-ID头字段值。
如果相同的客户端使用不同的Call-ID值,那么注册服务器不能检测延时的REGISTER请求是否没有排序到达。
CSeq:CSeq值保证REGISTER请求适当的排序。对于每个使用相同的Call-ID的REGISTER请求,UA必须逐一增加Cseq值。
Contact: REGISTER请求可能包括有一个或多个地址绑定值的Contact头字段。直到它们接收到来自注册服务器的前一请求的最终响应,或之前的REGISTER请求超时,UA才能发送新的注册(即是包含与转发相对的新Contact头字段值)。
在REGISTER请求中,下面的Contact头参数有特定的意义。
action:RFC 2543 不赞成action 参数。UAC 不应该使用action 参数。
expires:expires参数表示了UA绑定的有效时间。此参数值是表示秒的数字。如果不提供此参数,那么将使用expires头字段的值代替。不规范的值应该视为等于3600。
6.1.1. 添加绑定
发送给注册服务器的REGISTER请求包括SIP请求应该转发给记录地址的地址。记录地址包括在REGISTER请求的To头字段中。
请求的Contact头字段值主要是由SIP和SIPS URI组成的,它确定具体的SIP终端(如,“sip:carol@cube2214a.chicago.com”),同时,也可以使用其它的URI模式。例如,SIPUA可以选择注册电话号码(使用tel URL,RFC 2806)和email 地址(使用mail to URL,RFC 2368 )作为的记录地址的联系人。
例如, Carol,使用记录地址“ sip:carol@chicago.com ”在SIP注册服务器的chicago.com 域注册。chicago.com 域的代理服务器将使用Carol 的注册,将Carol 的记录地址请求路由到其SIP终端。
一旦客户端在注册服务器上建立了绑定,如果需要,它可以发送包含新绑定和修订现有绑定的随后注册。REGISTER 请求的2xx 响应,将在Contact 头字段中包含完整的、在此注册服务器上注册记录地址的绑定列表。
如果REGISTER请求To头字段的记录地址是SIPS URI,那么,请求的Contact头字段值也应该是SIPS URI。当以其它方式来保证联系地址 表示的资源的安全时,客户端仅注册SIP记录地址下的非SIPS URI。这可适用于调用不是SIP协议的URI和不是TLS协议保证其安全性的SIP设备。
注册不需要更新绑定。典型的是,UA仅更新其自己的联系地址。Setting the Expiration Interval of Contact Addresses 当客户端发送REGISTER请求时,它可以建议逾时间隔的状态,表示客户端注册有效的时间。(注册服务器基于其本地策略选择实际的时间间隔。)
有两种方法可用于客户端建议绑定的逾时间隔:通过Expires 头字段或者expiresContact 头参数。当在单个REGISTER 请求中给出多个绑定时,后者允许在预先绑定的基础上建议逾时间隔,但是前者建议的逾时间隔适用于不包含expires 参数的所有Contact 头字段值。
如果在REGISTER中,表示建议到期时间的两种机制都不存在,那么,客户端表示,它要服务进行选择。
Preferences among Contact Addresses 如果REGISTER 请求发送多个Contact,那么,注册UA要将所有的Contact头字段值的URI和To字段的记录地址相关联。此列表可以使用Contact头字段的‘q’参数排出优先级。Q参数表示具体Contact头字段值与记录地址其它绑定的相关优先级。
6.1.2. 删除绑定
注册是软状态,除非是更新才到期,但是也可以明确地删除。客户端可以影响注册服务器选择的逾时间隔。通过在REGISTER 请求的联系地址指定“0”逾时间隔,UA请求立即删除绑定。UA应该支持这种机制,以便于可以在逾时间隔到期之前删除绑定。
REGISTER-specific Contact头字段值“*”用于所有的注册,但是,如果Expires头字段不用值“0”表示,不能使用“*”。
使用“*”Contact 头字段值允许注册——在不知道精确值时,删除与记录地址相关的所有绑定。
6.1.3. 提取绑定
不管请求是否包含有Contact 头字段,REGISTER 请求成功的响应包含全部的现有绑定。如果REGISTER 请求中没有Contact 头字段,那么绑定列表左边不改变。
6.1.4. 更新绑定
每个UA负责更新它之前建立的绑定。UA不应该更新其它UA建立的绑定。注册服务器的200(OK)响应包含一系列的Contact头,列举了所有的当前绑定。UA 比较每个联系地址,检查它是否创建了联系地址。如果是,根据expires参数——如果没有,就根据Expires 字段值更新逾时间隔。UA随后在逾时间隔结束之前,为其每个绑定发布REGISTER请求。它也可以在REGISTER请求中,组合几个更新。
在单个引导周期,UA应该为所有的注册使用相同的Call-ID。除非有重定向,注册更新应该发送到与原始注册相同的网络地址。
6.1.5. 设置内部时钟
如果REGISTER 请求的响应包含有Data 头字段,那么客户端可以使用此头字段获取当前的时间,设置内部时钟。
6.1.6. 发现注册服务器
UA可以使用三种方法来确定发送注册的地址:通过配置、使用记录地址和多播。可以用注册服务器地址配置UA,这种方法超出了本规范的范围。如果没有可配置的注册服务器地址,那么,UA应该使用通用的SIP服务器定位机制,将主机部分的记录地址作为请求的Request-URI和地址。例如,UA为用户“sip:carol@chicago.com”将REGISTER请求寻址到“sip:chicago.com”。
最后,UA可以配置成多播。多播注册都编址为已知的“所有SIP服务器”多播地址“sip.mcast.net”(224.0.1.75 for IPv4)。现在没有分配已知的Ipv6的多播地址;当需要时,将单独说明这种分配。SIPUA可以侦听此地址,并使用它来知道其它本地用户的当前位置;但是,它们并不响应请求。
在有些情况中,多播注册可能不适用,例如,如果多播事务共享相同的本地网络。
6.1.7. 发送请求
一旦构造了REGISTER方法,并确定了消息的目的地,那么,UAS遵循第5.1.2节介绍的过程,将REGISTER请求发送给处理层。如果因为REGISTER没有响应,处理层返回超时错误,UAS不应该立即向相同的注册服务器再尝试注册。
立即再尝试有可能也超时。为造成超时的条件等待合理的时间间隔,可以减少不必要的网络负载。没有强制具体的时间间隔。
6.1.8. 错误响应
如果UA 接收到423(Interval Too Brief)响应,在使得REGISTER 请求中的所有联系地址逾时间隔等于或大于423(Interval Too Brief)响应Min-Expires 头字段的逾时间隔后,它可以再注册。
6.2. 处理REGISTER请求
注册服务器是UAS,在其管理域内,它响应REGISTER请求,并保留可以访问代理服务器和重定向服务器的一系列绑定。注册服务器遵循第5.2节处理响应,但是它只接受REGISTER请求。注册服务器不能生成6xx响应。
在适当的时候,注册服务器可以重定向REGISTER请求。通用的用法是,注册服务器侦听多播接口,用302(暂时清除)响应将多播REGISTER请求重定向到其自己单播接口。
如果Record-Route包含在REGISTER请求中,那么,注册服务器必须忽略Record-Route头字段。注册服务器不能在REGISTER 请求的响应中包含Record-Route 头字段。
注册服务器可能接收到穿越代理的请求,它认为REGISTER是未知的请求,并添加Record-Route头字段值。
注册服务器必须知道(例如,通过配置)它所保留绑定的域。注册服务器必须按照其接收的顺序处理REGISTER请求。必须能够基本处理REGISTER 请求,意味着具体的REGISTER请求可以完全处理,也可以一点都不处理。必须独立于其它注册和绑定改变,处理每个REGISTER 请求。
当接收到REGISTER请求,注册服务器遵循以下步骤:
1. 注册服务器检查Request-URI,确定它是否可以访问Request-URI指定域的绑定。如果不能,并且,如果服务器也当作代理服务器,那么,服务器应该遵循第14章介绍的代理消息的通用行为,将请求转发给寻址域。
2. 为了保证注册服务器支持任何必要的扩展,注册服务器必须像第5.2.2节介绍的UAS一样处理Require 头字段。
3. 注册服务器应该认证UAC。注册服务器的行为绝对不会不考虑SIP通用认证框架。如果认证机制不可用,那么注册服务器可以将From地址作为请求发起者声明的身份。
4. 注册服务器应该确定,认证用户是否有权修改记录地址的注册。例如,注册服务器可能查询授权数据库——它映射了用户名和用户有权修改的一系列记录地址。如果认证用户无权修改绑定,注册服务器必须返回403(Forbidden),并跳过剩下的步骤。
在支持第三方注册的架构中,实体可以负责更新多个记录地址相关的注册。
5. 注册服务器从请求的To头字段取出记录地址。如果记录地址不可用于Request-URI域,那么注册服务器发送404(Not Found)响应,并跳过剩下的步骤。URI必须转换成规范的格式。为了实现这一点,必须删除所有的URI 参数(包括user-param),同时将所有的转义字符串转换成保留格式。此结果用作一系列绑定的索引。
6. 注册服务器检查请求是否包含Contact头字段。如果没有,跳到最后一步。如果有Contact头字段,注册服务器检查,Contact头字段是否包含了特殊值“*”和Expires字段。
如果请求有其他的Contact头或者非零的到期时间,那么,请求是无效的,服务器返回400(Bad Request)响应,并跳过剩下的步骤。如果没有,注册服务器检查,Call-ID是否和每个绑定的存储值一致。如果没有,它必须删除这些绑定。如果一致,仅在请求的Cseq高于绑定的存储值时,删除绑定。否则,必须放弃更新,同时请求失败。
7. 注册服务器现在依次处理Contact头字段的每个联系地址。对于每个地址,它按照下面的方法确定逾时间隔:
² 如果字段值有expires参数,此值必须当作请求的到期时间。
² 如果没有这样的参数,但是请求有Expires头字段,此值必须当作请求的到期时间。
² 如果都没有,本地配置的默认值必须当作请求的到期时间。
注册服务器可以选择小于请求的逾时间隔的到期时间。当且仅当请求的到期时间大于零,并且小于一个小时,同时小于注册服务器配置的最小值,注册服务器可以拒绝注册,并返回423(Interval Too Brief)响应。此响应必须包含Min-Expires 头字段——说明注册服务器想要的最小逾时间隔。然后,它跳过剩下的步骤。
在限制需要保持的状态和减少可能的注册停滞的同时,允许注册服务器设置注册时间间隔,防止过于频繁的注册更新。注册的逾时间隔频繁地用于创建服务上。其中一个实例是,follow-me服务,在这里,终端用户可能仅在一个很短的周期内可用。因此,注册服务器应该接受简短注册,如果时间间隔很短以至于更新会降低注册服务器的性能,那么,应该拒绝该请求。
对于每个地址,注册服务器随后使用URI 比较规则搜索当前的绑定列表。如果绑定不存在,将暂时添加它。如果绑定存在,注册服务器检查Call-ID 值。如果现有绑定的Call-ID值与请求中的Call-ID 值不同,如果逾时间隔为零或者有其它更新,必须删除绑定。如果它们是相同的,那么注册服务器比较CSeq 值。如果此值高于现有绑定的值,它必须更新或者删除绑定。否则,必须放弃更新,同时请求失败。
此算法确保了忽略相同UA 无序的请求。
每个绑定记录记录了请求的Call-ID 和Cseq 值。
当且仅当,绑定更新和添加成功,必须提交绑定更新(即是使得代理和重定向服务器可见)。如果其中之一失败了(例如,因为后台数据库提交失败),那么,请求必须失败,返回500(Server Internal Error)响应,同时必须删除所有尝试的绑定更新。
8. 注册服务器返回200(OK)响应。响应必须包含列出了所有当前绑定的Contact 头字段值。每个Contact 值必须对注册服务器选择的、说明其逾时间隔的“expires”参数起作用。此响应应该包含Date 头字段。
- 7. 查询(OPTIONS)
SIP方法OPTIONS允许UA 查询其它UA 和代理服务器的能力。这就允许客户端不必“Ringing”另一方,发现关于支持的方法、内容类型、扩展和编码等等的信息。例如,在客户端将Require 头字段插入到INVITE 列出的它所不确定目的UAS 支持的选项时,客户端可以使用OPTIONS查询目的UAS,检查Supported头字段是否返回此选项。所有的UA必须支持OPTIONS 方法。
Request-URI确定OPTIONS请求的目标,它可以识别其它的UA和SIP服务器。如果OPTIONS寻址到代理服务器,那么Request-URI设置为没有用户部分,和REGISTER请求的Request-URI 设置一样。
换言之,服务器接收到Max-Forwards头字段为0的OPTIONS请求,可能不管Request-URI而直接响应请求。
此行为和HTTP/1.1相同。通过发送一系列有递增Max-Forwards值的OPTIONS请求,此行为可以用作“路由跟踪”功能,检查单个跳跃点服务器的能力。
作为通用UA的行为,如果OPTIONS没有响应,处理层可以返回超时错误。这可以说明目标不可到达,因此是无效的。
OPTIONS请求可以作为建立对话的一部分发送,查询在对话中以后可能使用的对等物的能力。
7.1. 构造OPTIONS请求
使用第5.1.1节讨论的SIP请求的标准规则来构造OPTIONS请求。
Contact头字段可能出现在OPTIONS中。
应该包括Accept头字段,说明在响应中UAC想要接收到的消息体的类型。这主要用来设置一种用于描述UA媒体能力的格式,如SDP(application/sdp)。
OPTIONS请求的响应假定为是在原始请求Request-URI的范围内的。然而,仅当OPTIONS作为建立对话的一部分发送时,它才能保证生成OPTIONS响应的服务器可以接收到将来的请求。
以下是OPTIONS请求的实例:
OPTIONS sip:carol@chicago.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKhjhs8ass877
Max-Forwards: 70
To: <sip:carol@chicago.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 63104 OPTIONS
Contact: <sip:alice@pc33.atlanta.com>
Accept: application/sdp
Content-Length: 0
7.2. 处理OPTIONS请求
使用第5.2.6 节讨论的SIP响应的标准规则来构造OPTIONS响应。选择的响应代码必须和INVITE请求已经选择的一样。即使,如果准备接受呼叫,返回200 (OK);如果UAS 忙,返回486(这儿正忙)等等。这允许OPTIONS请求用作确定UAS 的基本状态,这可以是UAS是否接受INVITE 请求的指示。
在对话中接收到OPTIONS请求生成200 (OK)响应,和对话外构造的一样,对对话没有任何影响。
因为代理处理OPTIONS和INVITE请求的不同,OPTIONS的使用有局限性。分发的INVITE可能返回多个200(OK)响应,而分发的OPTIONS可能只返回一个200(OK)响应,因为代理使用非INVITE 处理机制处理的。
如果代理服务器生成OPTIONS响应,那么,代理返回200(OK),列出服务器的能力。此响应不包括消息体。
Allow、Accept、Accept-Encoding、Accept-Language和Supported头字段应该出现在OPTIONS请求的200 (OK)响应中。如果代理产生此响应,因为代理不知道方法,Allow是模糊的,所以代理应该忽略Allow头字段。Contact头字段可能出现在200 (OK)响应中,并和3xx 响应有相同的语义。即是它们可能列出到达用户的一系列可选名字和方法。Warning头字段也可能出现。
消息体可能发送,其类型由OPTIONS请求的Accept头字段确定(如果Accept头字段不存在,默认值为application/sdp)。如果此类型包括可以描述媒体能力的类型,那么,UAS应该为此目的在响应中包括消息体。在[12]中介绍了在application/sdp情况下,构造此消息体的详情。
以下是UAS创建的OPTIONS响应实例(和第11.1 节的请求相对应):
SIP/2.0 200 OK
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKhjhs8ass877 ;received=192.0.2.4
To: <sip:carol@chicago.com>;tag=93810874
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 63104 OPTIONS
Contact: <sip:carol@chicago.com>
Contact: <mailto:carol@chicago.com>
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE
Accept: application/sdp
Accept-Encoding: gzip
Accept-Language: en
Supported: foo
Content-Type: application/sdp
Content-Length: 274
(SDP not shown)
- 8. 对话
一个UA的核心概念就是对话。对话是表现为两个用户代理(UA)之间的持续一段时间的点对点的SIP关系。对话(Dialog)使得用户代理之间的消息顺序传递和两个用户代理之间的请求正确路由更加容易。对话(Dialog)可以认为是对SIP消息解释的上下文关系。第8节讲述了方法无关的UA处理和响应对话(Dialog)外的请求。本节将讨论如何通过请求和应答来创建一个对话(Dialog),并且在对话(Dialog)中如何发起和响应后续的请求。
一个对话在参与对话的UA中都有一个dialog ID作为标记,这个ID由Call-ID,和一个本地tag和远程tag组成。各个UA的dialog ID在对话中是不一样的。特别是,在一边UA的本地tag,在另外一方就是远程tag。这些tag都是互相不透明的,并且使得整个dialog ID是唯一的。
dialog ID同样是和所有的To头域中包含了tag参数的请求及应答相关。
填写一个消息中的dialog ID的规则依赖于SIP元素是UAC还是UAS。对于UAC来说,dialog ID中的Call-ID的值会填写到消息中的Call-ID域中,远程tag放在消息中的To的tag参数中,本地tag放在From的tag参数中。(这些规则对请求和应答都适用)。对于UAS来说,dialog ID的Call-ID值放在消息的Call-ID头域中,远程tag放在From头域的tag中,本地tag放在To头域的tag参数中。
一个对话包含一些特定的状态用于以后的对话中的消息传送。这个状态由dialog ID,本地序列号(用来排序UA到对方的请求的序列),远程序列号(用来排序请求从远端到本UA),本地URI,远端URI,remote target,一个布尔类型的标记”secure”,路由集合(一组有序的URI)组成。
路由集合是由发送请求到对方需要途径的一组服务器列表组成。一个对话可以处于”early”状态,这是由于当这个对话收到了临时应答而创建,并且当收到了2xx终结应答的时候转换到”confirmed”状态。对于其他应答,或者没有应答,”early”对话将会终结。
8.1. 创建一个对话
对话是由对一组特定请求的没有失败的应答来创建的。在本规范中,只有包含To tag的2xx和101-199应答,并且请求是INVITE的,会建立一个对话。当收到一个非终结应答的时候,对话会建立成”early”状态,并且成为early dailog。创建对话的时候可以使用Extension来定义扩展。13节描述了INVITE请求的更多细节。在这里,我们描述与方法无关的对创建对话状态的处理。
UA必须按照下边描述的方法对dialog ID进行赋值。
8.1.1. UAS行为
当UAS响应一个请求给出一个应答,并且这个应答会建立一个对话的时候(比如对INVITE的2xx应答),UAS必须拷贝所有的请求中的Record-Route头域到应答中去(包括URI,URI参数,和其他任何Record-Route头域的参数,无论UAS是不是认识的参数都需要原样拷贝),并且必须维持这些参数的顺序。UAS必须增加一个Contact头域给应答。这个Contact头域包含一个UAS在后续对话请求中接收请求的地址(这个包含了给INVITE请求的2xx应答的ACK请求处理的地址)。通常情况下,UAS会用IP地址或者FQDN形式来发布自己的这个Contact地址。这个在Contact头域中的URI必须是一个SIP或者SIPS URI。如果创建对话的请求在Request-URI中包含的是SIPS URI,或者在Record-Route头域的最上的一个值是SIPS URI,或者如果请求中没有Record-Route头域但是请求中的Contact头域是SIPS URI,那么给出的应答中的Contact头域必须是一个SIPS URI。 这个URI应该是全局有效的(就是说,这个URI可以用于对话外的消息)。同样的,在请求INVITE中的Contact头域的URI也不应当仅限于这个对话中使用。因此它可以用于对话外的消息中。
UAS接着创建这个对话的状态。对话状态必须维持直到对话结束。
如果请求是通过TLS过来的,并且Request-URI包含一个SIPS URI,”secure”标志将被赋值成为TRUE。
路由集合必须设置成为请求中的Record-Route的URI列表,保留所有的URI参数和顺序。如果请求中没有Record-Route头域,那么路由集合必须设置成为空。这个路由集合,即便是空的,为了以后的对话中的请求,也要覆盖任何预先存在(pre-existing)的路由集合。remote taget必须设置成为请求的Contact头域中的URI。
远程序列号必须设置成为请求中的Cseq头域的序列号。本地序列号必须设置成为空。dialog ID中的呼叫标志应该设置成为请求的Call-ID头域的值。dialog ID的本地tag必须设置成为对请求的应答包中的To头域的tag,并且dialog ID的远程tag必须设置成为请求中的From 头域中的tag。UAS必须能够处理接收到的请求中的From头域没有tag标志,在这种情况下,这个tag就是空值。这是为了兼容RFC2543协议,它并没有定义From tag。
远程URI(remote URI)必须设置成为From头域中的URI,并且本地URI必须设置成为TO头域中的URI。
8.1.2. UAC行为
当一个UAC发出一个请求,这个请求能够建立一个对话(比如这个请求是INVITE),它必须在Contact头域中提供一个基于全局的SIP或者SIPS URI(例如,可以在对话外使用的SIP URI)。如果请求包含一个Request-URI或者最上的Route头域是SIPS URI,Contact头域也必须包含的是SIPS URI。
当一个UAC接收到应答,并且这个应答建立对话的时候,它也同样构造这个对话的状态。这个状态必须维持到对话的结束。
如果这个请求是基于TLS发送的,并且Request-URI包含一个SIPS URI,那么”secure”标志被设置成为TRUE。
路由集合必须设置成为应答中的Record-Route头域的URI列表,保留所有的URI参数和顺序。如果在应答中没有Record-Route头域,那么这个路由集合必须设置成为空集合。这个路由集合即便是空的,为了以后的对话中的请求,也要覆盖任何预先存在(pre-existing)的路由集合。remote taget必须设置成为应答中的Contact头域的URI。
本地序列号必须设置成为请求中的Cseq头域的序列号。远程序列号必须设置成为空(他会由远端的UA在对话中发送请求而建立)。dialog ID中的呼叫标志必须设置成为请求的Call-ID头域的值。dialog ID的本地tag必须设置成为请求中的From头域的tag,dialog ID的远程tag必须设置成为应答中的To头域的tag。UAC必须能够处理接收到的应答的To头域中没有tag的情况,在这个情况下,tag值取值成为空。这是为了能够向下兼容RFC2543,它没有规定To的tag。
remote URI必须设置成为To头域的URI,local URI必须设置成为From头域的URI。
8.2. 对话中的请求
当两个UA之间的对话建立以后,他们都可以在对话中初始化一个新的事务(transaction)。如果UA发送请求,将遵循UAC的事务规则。UA接收请求将遵循UAS的规则。在建立对话的事务过程中,UA扮演的角色可能是不一样的。
在对话中的请求可以包含Record-Route和Contact头域。不过,虽然他们会修改remote target的URI,但是这些请求也不会导致对话的路由集被改变。明确说,如果请求不是刷新target的请求,那么这个请求不会更改对话的remote target URI,如果请求是刷新target的请求,那么这个请求才会更改对话的remote target URI。对于用INVITE建立的对话来说,唯一的能够刷新target的请求就是re-INVITE(见14节说明)。可能会有其他扩展定义通过其他方法来刷新target的请求。
注意ACK不是一个刷新target的请求。
刷新target请求只会更改对话的remote target URI,并且更改由Record-Route指定的路由集合。如果更新路由集合会带来严重的和RFC2543向后兼容问题。
8.2.1. UAC行为
8.2.1.1. 产生请求
在对话中的请求是通过用许多对话的状态部分来构造的。在TO头域中的URI部分必须设置成为对话状态中的remote URI。To头域的tag参数必须设置成为dialog ID中的remote tag部分。请求的From URI必须设置成为对话状态中的local URI。From头域的tag参数必须设置成为dialog ID的local tag部分。如果remote或者local tag是空值,那么tag参数必须分别从From或者To头域中去除。
在请求序列中的原始请求的To和From头域的URI的使用方法是为了向下兼容RFC2543协议的,在RFC2543协议中,使用URI作为对话的标志。在这个规范中,只有tags用于区分对话。有可能在本协议的后续版本中,在对话中的请求必须强制反应原始请求的To和From头域的URI将会去除。
请求的Call-ID必须设置成为对话的Call-ID。在对话中的请求必须严格遵循单个递增的Cseq序列号(每次增加1)(当然要除了ACK和CANCEL,这两个请求中的Cseq必须和原始的请求或者确认请求一样)。因此,如果本地序列号(local sequence number)不为空,那么本地序列号码必须依次增加1,并且这个数值要存放到Cseq头域中。如果本地序列号码是空的,那么在8.1.1.5节约定的初始值必须填写进去。在Cseq头域中的method字段必须和请求的方法(method)一致。
通过使用32位的长整数,使得即使每秒种产生1笔请求,也会要136年才会用完这个整数出现重复。这个序列号的初始值的选取是为了让对话中后续的请求序列号不会重复。非0的初始值可以考虑采用时间来作为初始的序列号。一个客户端可以用31位有符号整数或者32位无符号整数来存放时间作为初始化的序列号。
UAC使用remote target和路由集合来构造请求中的Request-URI和Route头域。如果路由集合是空的,那么UAC必须把remote target URI放到Request-URI中,并且UAC不能添加Route头域到请求中。
如果路由集合不为空,并且路由集合的第一个URI包含lr参数(见19.1.1),那么UAC必须填写remote target URI到Request-URI,并且必须包含Route头域,这个Route头域按照顺序填写路由集合和路由集合的参数。
如果路由集合不为空,并且路由集合的第一个URI没有包含lr参数,那么UAC必须把第一个URI放在Request-URI中,并且拆去所有不被Request-URI允许的参数。UAC必须增加一个Route头域顺序包含所有剩下的路由集合元素,及其参数。UAC接着必须把remote target URI放在Route头域的最后一项。
例如,如果remote targe是: sip:user@remoteua 并且路由集合包括:
<sip:proxy1>,<sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>
那么请求应该有下列的Request-URI和Route头域
METHOD sip:proxy1
Route: <sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>,<sip:user@remoteua>
如果路由集合的第一个URI不包含lr参数,那么对应的说明proxy并不能支持本文档所约定的路由机制,而是支持RFC2543文档所约定的路由机制,那么在发送信息的时候需要通过替换Request-URI为接收到的第一个Route头域的值。将Request-URI的值放在Route头域的目的是为了保护Request-URI,使得它经过严格路由的时候不丢失(当请求遇到一个松散路由的时候会返回到Request-URI中?????。)
在对话内的任何一个刷新target的请求中,都应当包含一个Contact头域,并且这个URI除非有必要,否则都应当是和对话内上次请求的URI值一样。如果”secure”标志设置成为TRUE,那么URI也应当是SIPS URI。
如果在12.2.2节讨论的那样,在刷新target请求中的Contact头域会更新remote target URI。这个允许UA提供一个新的联系地址(Contact address),表明它在对话中改变了自己的地址。不过,如果请求不是刷新target的请求,那么不会影响对话中的remote target URI。请求中的剩下的部分请按照8.1.1节描述的填写。一旦请求被创建了,请求将按照对话外请求发送标准步骤(8.1.2节)来解析服务器的地址并且发送请求。
8.1.2节中的步骤一般把请求发送到Route头域的最上一个地址,或者如果没有Route头域,那么就发送到Request-URI地址。由于受到特定的限制,这些步骤也允许把请求发送到另外一个地址(比如在route set中没有的缺省的外发proxy)
8.2.1.2. 处理应答
UAC将会从transaction层收到请求的应答。如果客户端的事务层返回一个超时,这会等同于一个408(请求超时)的应答。UAC处理3xx应答的时候,在这个应答是在对话内的请求的应答的处理方法和在对话外的处理方法是一样的。这个方法在8.1.3.4节中描述。需要注意的是,虽然UAC会尝试新的地址(处理3xx应答的时候),但是它依旧使用对话内的路由集合来构造请求的Route头域。
当UAC收到一个刷新target请求的2xx应答的时候,如果对话的remote target URI存在,那么它必须用这个应答的Contact头域的值来替换对话的remote target URI。
如果对话那的请求的应答是481应答(呼叫/事务不存在Call/Transaction Does Not Exits)或者一个408(请求超时),那么UAC应当终止这个对话。并且UAC应当在请求完全没有应答的时候(客户端transaciton将会通知TU这个超时)客户端transaction终止这个对话。
对于INVITE初始化的对话,终止对话需要发送一个BYE。
8.2.2. UAS行为
在对话中发送的请求,就像其他请求一样,是原子请求。如果UAS收到某个请求,所有的相关状态要么一起改变,要么就一起不变。在某些请求中,请求会影响好几个状态(比如INVITE请求)。
UAS从transaction层收到请求。如果请求的To头域有tag字段,UAS的处理核心需要校验对话的ID,拿请求中的tag和现存的对话相比较。如果匹配成功,那么就是一个在对话中的请求。在这种情况下,UAS首先使用8.2节中的对话外请求处理的步骤。如果请求To头域包括了一个tag字段,但是对话的ID并不匹配现存的对话,UAS可能是因为崩溃而重新启动,或者收到了一个另外(可能是错误的)UAS(UAS可以构造To的tags,这样UAS在灾备恢复下,可以把这个tag看成它自己的)。还有一种简单的可能是请求发送错误了。在这个基础上,UAS可以选择接受或者拒绝请求。在允许的情况下,尽量处理这些请求会提供灾难恢复的机制。UAS如果希望支持这样的特性就必须遵循一些原则,比如用始终使用单调递增的Cseq序列号,甚至是在重新启动之后也这样,在重启动后重建路由集合,处理越界的RTP时间戳和序列号等等。
如果UAS由于不希望重构对话而拒绝这个请求,它必须应答对方一个481(呼叫/事务不存在。Call / Transaction不存在)应答。
对于在对话中接收到的,那些不会用任何形式更改对话状态的请求,比如OPTIONS请求,他们等同于在对话外的处理请求。
如果远端的序列号(remote sequence number)是空的,它必须设置成为请求中的Cseq头域的序列号(sequence number)。如果remote sequence number不是空的,但是请求中的sequence number小于这个remote sequence number,请求就是非顺序的,并且必须通过应答500(服务器内部错误)打回去。如果remote sequence number不是空的,并且请求中的序列号大于这个remote sequence number,请求就是按照顺序的。这个请求中的Cseq的序列号可以比remote sequence number大不止1。在这种情况下,并非是错误的,并且UAS应当准备接收和处理比上次处理的请求Cseq值大于1 的请求。UAS必须设置remote sequence number成为请求中的Cseq头域中的序列号。
如果一个proxy废弃掉一个UAC产生的请求,并且UAC重新递交这个请求的时候。这个请求是会具有一个全新的Cseq序列号。UAS是不会收到第一个请求的,这样,Cseq序列号就会出现间隔,这样的间隔并非是一种错误的情况。
当UAS接收到一个target刷新请求的时候,如果请求中存在Contact头域,它必须用Contact头域中的URI来替换对话的remote target URI。
8.3. 终止对话
在建立对话中的终结对话,跟请求方法无关,如果对话外的请求产生了一个非2xx终结应答,任何前边请求创建的”早期对话”(early dialogs)将会终止。在已经建立的对话中,终结对话就是请求方法相关的。在这个定义中,BYE方法将会终结一个对话。15节有细致的讨论。
- 9. 初始化一个会话(INVITE)
当UAC希望初始化一个会话(比如,audio,video或者游戏),它首先构造一个INVITE请求。这个INVITE请求一个服务器来建立一个会话。这个请求可能会由proxy层层转发,最后到达一个或者多个可能能够处理这个邀请的UAS。这些UAS需要看看是否用户接收这个邀请。然后UAS可以接收这个请求(也就是会话建立了),通过发送2xx应答。如果邀请被拒绝,根据拒绝的原因,3xx,4xx,5xx或者6xx应答将会发送。在发送终结应答之前,UAS可以发送一些临时应答(1xx)应答给UAC,以便UAC能够掌握建立会话的进度。
当收到了一个或者多个临时应答,UAC可能收到一个或者多个2xx应答或者一个非2xx终结应答。由于在INVITE终结应答之前,可能有不少时间,在INVITE事务的可靠性机制和其他的请求不同(比如OPTIONS)。当UAC收到了终结应答,UAC需要给每一个INVITE的终结应答,发送一个ACK请求。发送ACK请求的步骤依赖于应答的类别。对于在300到699的终结应答,ACK是在transaction层处理的,并且遵循一系列规则(17节)。对于2xx应答,ACK是由UAC处理核心产生的。
INVITE的一个2xx应答会建立一个会话,同时也建立了一个基于发送INVITE请求的UA和产生2xx应答的UA之间的对话。因此,当从多个远程UA收到了多个2xx应答(可能由于INVITE的分支),每一个2xx建立一个不同的对话(dialog)。所有这些对话都是同一个呼叫的组成部分。
本节介绍了INVITE请求建立会话的详细过程。支持INVITE的UA也一定同时支持ACK,CANCEL和BYE。
9.1. UAC处理
9.1.1. 创建一个初始化的INVITE
由于初始化的INVITE请求是一个对话外的请求,它遵循8.1.1节的步骤创建。除此之外还有专门针对INVITE的附加处理步骤。
在INVITE中应当包括一个Allow头域(20.5节)。它用来标志在这个INVITE建立的这个对话(dialog)中什么样的方法可以接受。比如,一个UA可以在对话中接收和处理INFO请求[34],那么在INVITE请求的Allow头域中应当列出这个INFO方法。在INVITE请求中,Supported头域应当包含。这个头域包含了所有这个UAC支持的扩展部分。
在INVITE中可以包含一个Accept头域(20.1节)。这个标志了UA在后续建立的对话中,能兼容的接收和发送的Content-Type。Accept头域支持不同会话描述格式(session descrioption format)的时候特别有用。
UAC可以通过包含一个Expire头域(20.19节)来限制邀请的有效期限。如果Expire头域的时间到了还没有接收到INVITE的终结应答,UAC处理核心应当像9节描述的那样产生一个对INVITE请求的CANCEL请求,
UAC还可以根据需要增加Subject(20.36节),Organization(20.25节)和User-Agent(20.41节)头域。这些头域都包含了INVITE的相关资料。UAC可以给INVITE增加一个消息体。8.1.1.10节讲述了如何构造Content-Type头域来描述消息体。
对于消息体,有一些特别的规定――他们是基于某种磋商机制的,他们对应的Content-Disposition 是”session”(会话的)。SIP使用一个请求/应答模型,UA发出一个会话描述,称作是请求,里边包含了会话的描述。这个请求标志了特定的联系内涵(比如audio,vidio,game),这些内涵的参数(比如解码器等等),并且从应答方接收媒体信息的地址。对方UA会回应另外一个会话的描述,称之为应答,标志了能接受的联系内涵,这些内涵的参数。这个请求/应答的交换实在对话的上下文进行中的,所以如果一个SIP INVITE请求导致了多个对话,每一个对话都包含自己独立的请求/应答的交换。请求/应答模型定义了对于请求和应答的限制。(比如在上一个请求尚未处理完成情况下不能发起下一个请求)。这也导致了请求/应答在SIP消息中出现的位置限制。在这个规范中,请求和应答只能出现在INVITE、ACK请求和其应答中。请求和应答的使用中更进一步被限制。在初始化一个INVITE事务中,规则如下:
o 初始化请求必须在INVITE中,如果不在INVITE请求中,就必须在UAS回送给UAC的第一个非失败的可靠消息中。在这个规范中,这个应答就是2xx应答。
o 如果初始的请求是一个INVITE,那么应答必须是由UAS发送回给对应发出INVITE请求的UAC的可靠的非失败的消息。在本规范中,只有2xx应答对应这个INVITE请求。同样相同的应答可能在之前发送的零食应答中存在。UAC必须把它接收到的第一个会话描述当作是应答,并且必须忽略任何在初始INVITE请求中后续的会话描述应答描述。
o 如果初始请求是在第一个可靠的非失败的UAS回送给UAC的消息中,那么应答必须在这个消息的确认消息中(在本规范中,就是给2xx应答的ACK确认消息)
o 在发送或者接收到第一个请求的应答之后,UAC可以同样依据这样的问答方法产生后续的请求。但是只能在收到每一个请求的应答之后才能发起下一个请求。不能在上一个请求尚未收到应答的时候发起下一个请求。
o 当UAS发送或者接收到初始化的请求的时候,禁止在它给初始的INVITE请求的应答中产生后续的请求(协商会话描述请求)。这就意味着基于本规范的UAS在完成初始化的事务之前,不会产生任何会话描述请求。
具体来说,根据本规范,上边的规则分别定义了两种UA之间交换信息的方法。请求实在INVITE中,应答是在2xx(可能在1xx中也存在,具备相同的值)中,或者请求在2xx中,应答在ACK中。(这个意思是说,两个UA之间建立连接的时候,首先需要协商一下两个UA能够支持的消息体正文,那么这个协商关系也是通过问答形式的,也就是通过请求/应答的,这个媒体磋商的请求既可以在UAC发起的INVITE请求中,也可以在UAS回应的2xx应答中。同样的,媒体磋商的应答既可以在UAS的2xx应答或者1xx应答中,也可以在ACK确认请求中)。所有的支持INVITE请求的UA都必须支持两种交换方式。会话描述协议(session description protocol sdp)(RFC 2327[1])在所有的UA中都必须得到支持,并且它的用法和请求/应答的构造必须遵循[13]中定义的步骤。
在上边讲述的请求/应答模型中,只能适用于在包头域Content-Disposition中的值是”session”的包体情况。因此,有可能会INVITE和ACK请求中都包含一个包体信息(比如,INVITE包含一个相片(Content-Disposition:render)并且ACK包含一个会话描述(Content-Disposition:session))。
如果Content-Disposition头域不存在,Content-Type 是application/sdp的包体实现就等同于Content-Disposition”session”,其他Content-Type的情况就是实现”render”。
当INVITE请求创建以后,UAC遵循对话外请求发送的步骤进行发送(8节)。这也就是创建一个客户事务并且由这个客户事务发送请求并且处理应答。
9.1.2. 处理INVITE应答
当INVITE请求被传送给INVITE的客户事务层进行处理,UAS等待INVITE的应答。如果INVITE客户事务层返回一个超时而不是收到一个应答,那么这个TU就应当像收到一个408(请求超时)应答(8.1.3节)那样进行处理。
9.1.2.1. 1xx应答
有可能在收到一个或者多个终结应答之前,UAC会收到0个或者1个或者多个临时应答。INVITE的临时应答会建立”early dialogs”(早期对话)。如果一个临时应答在To头域中有一个tag子顿,并且应答的dialog ID并不是已经存在的对话的ID,那么就应当遵循12.1.2节定义的步骤创建一个对话(早期对话)。
early dialog只会在下边这个情况中需要:如果一个UAC需要在完成初始的INVITE事务之前,给对方发送一个对话内的请求的时候,就需要early dialog。在临时应答中的头域可以在当对话是early state的时候都有效(也就是说,比如一个临时应答的Allow 头域包含的方法,在对话状态是early state的时候都是有效的。[由于Allow是允许的方法集合,所以,当对话状态是早期对话的时候,这个Allow的集合是不会改变的,但是当创建正式的dialog之后,Allow的集合可能会改变哦]。)
9.1.2.2. 3xx应答
一个3xx应答可能包含一个或者多个Contact头域值,这个头域值提供了被叫方可能存在的地点。UAC可以根据3xx应答的状态码(21.3节)来决定是否尝试这些新的地址。
9.1.2.3. 4xx,5xx,6xx应答
在INVITE请求中,可能会收到单个非2xx终结应答。4xx,5xx,6xx应答如果包含了Contact头域,那么这个头域值指示了错误的详细信息的解释地点。后续的终结应答(只有可能在发生错误的情况下),必须被忽略掉。
所有的早期对话都会由于接收到非2xx终结应答而结束。
一旦接收到了非2xx终结应答,UAC处理核心就认为INVITE事务结束了。INVITE客户事务处理生成对这个应答的ACK(参见17节)。
9.1.2.4. 2xx 应答
单个INVITE请求可能会导致多个2xx应答返回给UAC,这是因为proxy可以分支。每一个应答都是由To中的tag参数来进行区分的,并且每一个应答都代表了一个独立的对话,具备单独的对话ID。
如果在2xx应答中的对话ID和一个现存的对话匹配,那么这个对话必须切换到”confirmed”状态,并且对话的路由集合必须基于2xx的应答进行重新计算(参见12.2.1.2)。如果不匹配,那么必须创建一个新的对话,这个对话具备”confirmed”状态,参见12.1.2的步骤进行创建。
注意在对话状态中,只有路由集合不需要重新计算。其他部分比如对话内的最大的序列号(远程的和本地的)等都不需要重新计算。路由集合只是由于需要向后兼容而需要重新计算。RFC 2543并没有要求在1xx应答中反射Record-Route头域回来,只在2xx请求中要求了。我们不能更新对话状态的全部部分,因为在早期对话(early dialog)中可能会存在对话中的请求,比如更改序列号等等。UAC核心必须为每一个2xx应答,产生一个ACK请求。除了在Cseq和身份认证相关的头域之外,ACK请求的头域的创建和在对话中的请求创建的方法一样(12节)。Cseq头域的序列号部分必须和需要确认的INVITE请求一样,但是Cseq的方法部分必须是ACK。ACK必须包含和INVITE请求相同的信任状。如果2xx包含一个媒体磋商请求(基于上述的规则),ACK必须在包体中包含一个媒体磋商应答。如果2xx应答的媒体磋商请求不能被接收,UAC核心必须产生一个有合法的应答ACK,并且立刻发送一个BYE请求。
当ACK创建以后,[附件4]中规定的步骤用来检测对方地址,端口和transport。这个请求是直接交给通讯层进行通讯的,而不是交给一个客户事务层进行发送。这是由于UAC核心直接处理ACK的重发,而不是事务层进行重发的处理。每次收到一个重发的2xx终结应答的时候都必须发送一个ACK到通讯层。
UAC核心认为INVITE事务在接收到第一个2xx应答后的64×T1秒后完成。在这个时间点后,所有没有转换成为建立连接状态的早期对话都会被终止。一旦UAC确认INVITE事务完成了,那么缺省认为不会收到新的2xx应答了。如果,在相应了对INVITE请求的全部应答之后,UAC并不希望创建这个对话,那么UAC必须通过15节描述的那样发送BYE请求来结束对话。
9.2. UAS处理
9.2.1. 处理INVITE
UAS核心从事务层收到INVITE请求。首先根据8.2节定义的步骤进行处理请求,8.2节中定义的是跟对话内外无关的请求的处理。如果处理顺利完成(没有产生应答),UAS核心根据如下步骤进行额外处理:
1、 如果INVITE请求包含一个Expires头域,UAS核心就设置一个时钟计数=这个头域值。如果时钟到了,这个邀请就过期了。如果在UAS尚未产生终结应答的时候就超时了,那么487(请求终止)应答应当产生给UAC。
2、 如果请求是一个对话中的请求,12.2.2节定义的方法无关的处理步骤将首先进行处理。这个处理可能会影响到会话;14节讲述了细节。
3、 如果请求的To头域包含了一个tag,但是对话的ID与现存的任何一个对话都不匹配,那么UAS可能是由于崩溃而重新启动的,或者是由于接收到了本应当发送给另外一个UAS的请求(或者就简单是由于请求填写错误)。12.2.2节提供了这种情况的处理指引。从这开始的处理将假定这个INVITE是在对话外的,并且INVITE请求的目的是建立一个新的会话。INVITE请求可能包含一个会话描述,在这种情况下是希望和UAS进行会话媒体的磋商。即使INVITE请求是对话外发出的,这个INVITE参与的用户也有可能正是那个会话中的参与方。这个是由于在多方会议中,某个正在会议中的用户,被其他参与方邀请参加。如果需要鉴别这样的情况,UAS可以使用会话描述来检查是否重复邀请。比如,SDP包含了会话的ID和版本号。如果这个用户本身就是会话中的一方,并且session参数包含的会话描述没有改变,UAS可能就悄悄接受这个邀请(就是说,在不提示用户的情况下发送2xx应答)。
如果INVITE并没有包含某个会话描述(session
description),UAS就是被邀请创建一个会话,并且UAC已经希望UAS来提供这个会话offer。UAS必须在它的给UAC的第一个非失败的可靠消息中提供这个offer。在本规范中,给INVITE请求的2xx应答中就应当提供这个offer。
UAS可以提示进度,接受,转发,或者拒绝这个邀请。在这些情况下,它通过按照8.2.6节描述的步骤建立应答。
9.2.1.1. 提示进度
如果UAS不能马上接受或者拒绝邀请,那么它可以提示某种形式的进度给UAC(比如提示一个回铃声等等)。这是通过一个101到199的临时应答实现的。这些临时应答建立了早期对话(early
dialog)(通过8.2.6和12.1.1)。如果UAS愿意,UAS可以发送多个临时应答。每一个临时应答都必须包含相同的dialog ID。这些临时应答都并非可靠传送的。
如果UAS打算延长一点时间来响应这个INVITE请求,它需要请求一个”extension”来防止proxy来取消这个事务。proxy有权利来取消超过3分钟未完成的事务。要防止这个取消,UAS必须每分钟发送一个非100临时应答,防止由于1xx临时应答的非可靠传输导致的临时应答丢失。
如果呼叫出于等待状态(比如用户设置成为呼叫等待的)或者这个呼叫正在和PSTN电话系统进行通讯(PSTN系统允许呼叫没有应答),一个INVITE事务是可以被延长处理时间的。
9.2.1.2. INVITE请求转发
如果UAS决定转发这个呼叫,就需要发出3xx的应答。300(多重选择),301(永久转移),302(临时转移)应答中应当包含一个Contact头域,这个头域包含了一个或者多个表明需要重试的URI新地址。这个应答交给INVITE服务端事务层,由服务端事务层负责应答的重发。
9.2.1.3. INVITE请求的拒绝
拒绝INVITE请求的常见情景是被叫方不想或者不能在终端系统上接收这个呼叫。486(用户忙)应当在这样的情况下返回。如果UAS知道没有其他终端系统能够响应这个呼叫,就应当返回一个600(Busy Everywhere)。不过,通常情况下UAS是不太会知道这个情况的,并且这个应答也是罕见的。这些应答是交给INVITE服务端的事务层进行发送的,由这个事务层来保证应答的重发机制的。如果UAS拒绝的是INVITE请求包含的媒体磋商offer,UAS应当返回一个488(Not Acceptable
Here)应答。这个应答应当包含一个Warning头域来解释为何offer被拒绝。
9.2.1.4. 接受INVITE请求
UAS核心产生一个2xx应答。这个应答建立一个对话,然后遵循8.2.6节和12.1.1节的描述进行处理。
响应INVITE请求的2xx应答包含Allow头域和Supported头域,并且可能包含Accept头域。包含这些头域的目的是为了让UAC不需要再次请求就能够知道UAS的特性以及UAS的扩展支持。
如果INVITE请求包含了一个媒体磋商请求offer,并且UAS还没有发送应答,2xx应答中必须包含针对这个offer的应答。如果INVITE请求没有包含这个offer,而且UAS也尚未发出offer,2xx应答必须包含这个媒体磋商offer。
当应答构建好了以后,它会交给INVITE的服务端事务层进行发送。注意,INVITE的服务端事务将会由于收到这个终结应答并且交给通讯层进行发送而销毁。因此,有必要在没有收到ACK的时候,每隔一定的时间就直接交给通讯层进行发送。2xx交给通讯层进行发送的时间间隔是从T1秒开始,并且每次发送后就加倍,直到到达T2秒的时间间隔(T1和T2的时间间隔定义在17节)。当收到了针对这个应答的ACK请求之后,重发就终止了。这个是与使用什么通讯协议来发送这个应答是无关的。
由于2xx的重发是端到端的,并且在UAS和UAC之间存在采用UDP通讯的节点。所以要保证通过这些节点进行可靠的传送,就必须采用间隔时间重发的机制,哪怕UAS本身的通讯机制是可靠的。
如果服务端的对2xx应答的重发经过了64×T1秒还没有收到ACK请求,那么dialog就认为是confirmed,但是会话却应当终止。这个是用过15节描述的方法发送BYE请求来结束。
10.
更新已经存在的会话(UPDATE)
成功的INVITE请求同时建立两个用户代理之间的对话和呼叫-应答模式的会话。第8章说明了怎样使用目标更新请求修改现有的对话(如,修改对话的远程目标URI)。本章介绍了怎样修改当前会话。此修改包括,修改地址或端口、添加媒体流、删除媒体流等等。通过在建立会话的相同对话中发送新的INVITE来完成。在现有对话中发送INVITE是re-INVITE。
注意,单个的re-INVITE可以同时修改对话和会话的参数。
呼叫者和被呼叫者都可以修改现有的会话。
UA检测媒体错误的行为是本地策略的事情。但是,当网络拥塞时,为了避免大量的网络流量,不推荐自动生成re-INVITE和BYE。在任何请情况中,如果要自动发送这些消息,应该在随机的间隔后发送这些消息。
注意,上面的章节提及了自动生成re-INVITE和BYE。如果用户因为媒体错误而挂断,那么,UA
应该和平常一样发送BYE
请求。
10.1.
UAC行为
在INVITEs中用于会话描述的呼叫-应答模式也同样适用于re-INVITE。例如,想要添加媒体流的UAC,将创建包含此媒体流的新呼叫,并在INVITE请求中将其发送给其对等物。重点要指出的是,要发送会话的全部描述,而不仅仅是改变。这支持在不同元素中的无状态处理,也支持自动恢复能力。当然,UAC
可以发送无状态描述的re-INVITE,在这种情况中,re-INVITE的第一个可信非错误的响应将包含呼叫(在本规范中,即是2xx响应)。
如果会话描述格式有版本号,那么,呼叫应该指出已经改变了会话描述的版本。
按照第8 章介绍的,遵循与在现有对话内的规则请求相同的方法,设置re-INVITE
的To、From、Call-ID、Cseq和Request-URI。
因为,UAS一般不会警告用户接受re-INVITE,UAC可以选择不为re-INVITE添加Alert-Info头字段和Content-Disposition“Alert”消息体。
不像INVITE可以分发,re-INVITE不会分发,因此,它仅能产生单一的最终响应。re-INVITE
不会分发的原因是,Request-URI确定了目标是和它建立对话的UA,而不是确定用户的记录地址。
注意,当正在任一方向进行其它的INVITE事务时,UAC不能在对话内发起新的INVITE事务。
1.如果有正在进行的INVITE客户端事务,那么TU必须等到事务完成或结束状态,才可以发起新的INVITE。
2.如果有正在进行的INVITE服务器事务,那么TU必须等到事务完成或结束状态,才可以发起新的INVITE。
然而,当INVITE事务正在处理时,UA可以发起普通的事务。当普通的事务正在处理时,UA可以发起INVITE事务。
如果UA 接收到re-INVITE的非2xx最终响应,那么,将像没有发布re-INVITE一样,
不改变会话参数。注意,如果非200 最终响应是481(呼叫/事务不存在)或者408(请求超时),或者请求根本没有接收到re-INVITE
的响应(即是INVITE
客户端事务返回超时),那么,UA将结束对话。
如果UAC接收到re-INVITE的491响应,那么,它应该启动有T值的计时器,T选择如下:
² 如果对话ID有自己的Call-ID(指它生成的值),那么,T是以10ms为单位、在2.1到4
秒内随机选择的值。
² 如果对话ID没有自己的Call-ID,那么,T是以10ms
为单位、在0到2秒内随机选择的值。
当计时器溢出,如果它仍然要修改会话状态,那么,UAC
应该再次尝试re-INVITE。例如,如果呼叫已经用BYE
挂断,那么将不能出现re-INVITE。
发送re-INVITE和为re-INVITE的2xx响应生成ACK的规则和初始INVITE的规则相同。
10.2.
UAS行为
UAS在发送第一个INVITE的最终响应之前,接收到相同会话的、低Cseq序列号的第二个INVITE,UAS必须对第二个INVITE返回500响应(服务器内部错误),并且必须包括在0到10秒之间随机选择值的Retry-After
头字段。
UAS正在处理会话已经发送的INVITE时,接收到INVITE,它必须给接收到的INVITE返回491响应(请求待处理)。
如果UA接收到现有对话的re-INVITE,它必须检查会话描述的版本标识符,如果没有版本标识符,认为会话描述的内容已经改变。
如果会话描述已经改变,那么,可能在要求用户确认后,UAS
必须调整会话参数。
会话描述的版本可以用来调节新到达会议的能力——添加删除媒体、从单点到多播会议。
如果不能接受新会话描述,UAS可以通过对re-INVITE返回488响应(在此不能接受)拒绝它。此响应应该包含Warning
头字段。
如果UAS生成2xx响应,但不能接受ACK,那么,它应该生成BYE结束会话。
因为,UAC一般不将此信息返回给用户,UAS可以选择不为re-INVITE生成180(响铃)响应。因为此原因,在响应re-INVITE
时,UAS可以选择不使用Alert-Info头字段和Content-Disposition“Alert”消息体。
在2xx中提供呼叫(因为INVITE不包含呼叫)的UAS应该服从发送呼叫更新现有会话的约束,建立呼叫,就像UAS正在做出新的呼叫。特别的是,此方法应该包括UA愿意支持的媒体格式和媒体类型。UAS必须确保会话描述与UAS先前的会话描述在对等物需要支持的媒体格式、传输和其它参数等方面重叠。这就避免了对等物拒绝会话描述。如果UAC不能接受它,UAC应该生成带有有效会话描述的响应,然后发送BYE,结束会话。
11.
结束会话(BYE)
会话的状态和对话的状态是紧密相关的。当用INVITE发起会话时,不同UAS产生的1xx和2xx响应都创建对话,如果此响应完成呼叫/应答交换,那么,它也创建会话。结果,每个会话都与创建它的对话相关联。如果初始的INVITE生成非2xx最终响应,那么,它将结束通过此请求的响应创建的所有会话(如果有)和对话(如果有)。由于完成了事务,非2xx最终响应也防止了INVITE创建更深层次的会话作为INVITE的结果。BYE请求用来结束具体的会话和尚未建立的会话。在这种情况中,具体的会话和对话是一个UA与对话中另一个对等UA建立的。当在对话中接收到BYE,应该结束和此对话相关的所有会话。UA不能在对话外发送BYE。呼叫者UA可以在确认的和早期的对话中发送BYE;被呼叫者UA
可以在确认的对话中发送BYE,但是不能在早期的对话中发送BYE。然而,在被呼叫者UA接收到其2xx响应的ACK和服务器事务超时之前,它不能在确认的对话中发送BYE。如果在此对话相关的应用层状态中没有定义SIP扩展,那么,BYE也可以结束对话。
对话和会话中INVITE的非2xx最终响应的效果是使用CANCEL
attractive。CANCEL尝试将非2xx最终响应强制发送给INVITE(特别是,487)。因此,如果UAC希望完全放弃其呼叫尝试,它发送CANCEL。如果INVITE导致了INVITE的2xx最终响应,这意味着正在处理CANCEL时,UAS接受了此邀请。UAC可以继续2xx响应建立的会话,也可以使用BYE
结束会话。
在SIP中,没有很好的定义“挂断”的概念。挂断是很具体的细节,但是也是很通用的用户接口。一般,当用户挂断,它表示想要结束建立会话的尝试和结束已经创建的会话。对于呼叫者UA,如果初始的INVITE没有创建最终响应,那么挂断意味着CANCEL请求;对于在最终响应后所有确认的对话,挂断意味着BYE。对于被呼叫者UA,一般来说,挂断意味着BYE。
可能是,当用户拿起电话,生成2xx,在接收到ACK后,挂断可能会导致BYE。这并不是指,用户在接收到ACK前不能挂断电话,仅仅是指,为了正确的清除,用户电话的软件需要在一小段时间维持状态。如果特定的UI
允许用户在其应答之前拒绝呼叫,403(Forbidden)是一个很好的表示方式。按上面的规则,可以发送BYE。
Item |
Headerfield |
Sending |
Receiving |
||||
Ref. |
RFCstatus |
Profilestatus |
Ref. |
RFCstatus |
Profilestatus |
||
1 |
Accept |
[26]20.1 |
o |
o |
[26]20.1 |
m |
m |
1A |
Accept-Contact |
[56B]9.2 |
c18 |
c18 |
[56B]9.2 |
c22 |
c22 |
2 |
Accept-Encoding |
[26]20.2 |
o |
o |
[26]20.2 |
m |
m |
3 |
Accept-Language |
[26]20.3 |
o |
o |
[26]20.3 |
m |
m |
3A |
Allow |
[26]20.5 |
o |
o |
[26]20.5 |
m |
m |
4 |
Allow-Events |
[28]7.2.2 |
c1 |
c1 |
[28]7.2.2 |
c2 |
c2 |
5 |
Authorization |
[26]20.7 |
c3 |
c3 |
[26]20.7 |
c3 |
c3 |
6 |
Call-ID |
[26]20.8 |
m |
m |
[26]20.8 |
m |
m |
7 |
Content-Disposition |
[26]20.11 |
o |
o |
[26]20.11 |
m |
m |
8 |
Content-Encoding |
[26]20.12 |
o |
o |
[26]20.12 |
m |
m |
9 |
Content-Language |
[26]20.13 |
o |
o |
[26]20.13 |
m |
m |
10 |
Content-Length |
[26]20.14 |
m |
m |
[26]20.14 |
m |
m |
11 |
Content-Type |
[26]20.15 |
m |
m |
[26]20.15 |
m |
m |
12 |
CSeq |
[26]20.16 |
m |
m |
[26]20.16 |
m |
m |
13 |
Date |
[26]20.17 |
c4 |
c4 |
[26]20.17 |
m |
m |
14 |
From |
[26]20.20 |
m |
m |
[26]20.20 |
m |
m |
14A |
Geolocation |
[89]4.1 |
c23 |
c23 |
[89]4.1 |
c23 |
c23 |
14B |
Geolocation-Routing |
[89]4.2 |
c23 |
c23 |
[89]4.2 |
c23 |
c23 |
14C |
Max-Breadth |
[117]5.8 |
n/a |
c29 |
[117]5.8 |
c30 |
c30 |
15 |
Max-Forwards |
[26]20.22 |
m |
m |
[26]20.22 |
n/a |
c31 |
16 |
MIME-Version |
[26]20.24 |
o |
o |
[26]20.24 |
m |
m |
16A |
P-Access-Network-Info |
[52]4.4 |
c9 |
c10 |
[52]4.4 |
c9 |
c11 |
16B |
P-Asserted-Identity |
[34]9.1 |
n/a |
n/a |
[34]9.1 |
c6 |
c6 |
16C |
P-Charging-Function-Addresses |
[52]4.5 |
c13 |
c14 |
[52]4.5 |
c13 |
c14 |
16D |
P-Charging-Vector |
[52]4.6 |
c12 |
n/a |
[52]4.6 |
c12 |
n/a |
16E |
P-Debug-ID |
[140] |
o |
c27 |
[140] |
o |
c28 |
16F |
P-Preferred-Identity |
[34]9.2 |
c6 |
x |
[34]9.2 |
n/a |
n/a |
16G |
Privacy |
[33]4.2 |
c7 |
n/a |
[33]4.2 |
c7 |
c7 |
17 |
Proxy-Authorization |
[26]20.28 |
c5 |
c5 |
[26]20.28 |
n/a |
n/a |
18 |
Proxy-Require |
[26]20.29 |
o |
n/a |
[26]20.29 |
n/a |
n/a |
18A |
Reason |
[34A]2 |
c17 |
c21 |
[34A]2 |
c24 |
c24 |
19 |
Record-Route |
[26]20.30 |
n/a |
c31 |
[26]20.30 |
n/a |
c31 |
19A |
Referred-By |
[59]3 |
c19 |
c19 |
[59]3 |
c20 |
c20 |
19B |
Reject-Contact |
[56B]9.2 |
c18 |
c18 |
[56B]9.2 |
c22 |
c22 |
19C |
Request-Disposition |
[56B]9.1 |
c18 |
c18 |
[56B]9.1 |
c22 |
c22 |
20 |
Require |
[26]20.32 |
m |
m |
[26]20.32 |
m |
m |
20A |
Resource-Priority |
[116]3.1 |
c25 |
c25 |
[116]3.1 |
c25 |
c25 |
21 |
Route |
[26]20.34 |
m |
m |
[26]20.34 |
n/a |
c31 |
21A |
Security-Client |
[48]2.3.1 |
c15 |
c15 |
[48]2.3.1 |
n/a |
n/a |
21B |
Security-Verify |
[48]2.3.1 |
c16 |
c16 |
[48]2.3.1 |
n/a |
n/a |
21C |
Session-ID |
[162] |
o |
c32 |
[162] |
o |
c32 |
22 |
Supported |
[26]20.37 |
o |
o |
[26]20.37 |
m |
m |
23 |
Timestamp |
[26]20.38 |
c8 |
c8 |
[26]20.38 |
m |
m |
24 |
To |
[26]20.39 |
m |
m |
[26]20.39 |
m |
m |
25 |
User-Agent |
[26]20.41 |
o |
o |
[26]20.41 |
o |
o |
25A |
User-to-User |
[126]7 |
c26 |
c26 |
[126]7 |
c26 |
c26 |
26 |
Via |
[26]20.42 |
m |
m |
[20]20.42 |
m |
m |
c1: IFA.4/22THENoELSEn/a--actingasthenotifierofeventinformation. c2: IFA.4/23THENmELSEn/a--actingasthesubscribertoeventinformation. c3: IFA.4/7THENmELSEn/a--authenticationbetweenUAandUA. c4: IFA.4/11THENoELSEn/a--insertionofdateinrequestsandresponses. c5: IFA.4/8ATHENmELSEn/a--authenticationbetweenUAandproxy. c6: IFA.4/25THENoELSEn/a--privateextensionstotheSessionInitiationProtocol(SIP)forassertedidentitywithintrustednetworks. c7: IFA.4/26THENoELSEn/a--aprivacymechanismfortheSessionInitiationProtocol(SIP). c8: IFA.4/6THENoELSEn/a--timestampingofrequests. c9: IFA.4/34THENoELSEn/a--theP-Access-Network-Infoheaderextension. c10: IFA.4/34ANDA.3/1THENmELSEn/a--theP-Access-Network-InfoheaderextensionandUE. c11: IFA.4/34AND(A.3/7AORA.3/7DORA3A/84)THENmELSEn/a--theP-Access-Network-InfoheaderextensionandASactingasterminatingUA,ASactingasthird-partycallcontrollerorEATF. c12: IFA.4/36THENoELSEn/a--theP-Charging-Vectorheaderextension. c13: IFA.4/35THENoELSEn/a--theP-Charging-Function-Addressesheaderextension. c14: IFA.4/35THENmELSEn/a--theP-Charging-Function-Addressesheaderextension. c15: IFA.4/37ORA.4/37ATHENoELSEn/a--securitymechanismagreementforthesessioninitiationprotocolormediasecheaderfieldparameterformarkingsecuritymechanismsrelatedtomedia(note). c16: IFA.4/37ORA.4/37ATHENmELSEn/a--securitymechanismagreementforthesessioninitiationprotocolormediasecheaderfieldparameterformarkingsecuritymechanismsrelatedtomedia. c17: IFA.4/38THENoELSEn/a--theReasonheaderfieldforthesessioninitiationprotocol. c18: IFA.4/40THENoELSEn/a--callerpreferencesforthesessioninitiationprotocol. c19: IFA.4/43THENmELSEn/a--theSIPReferred-Bymechanism. c20: IFA.4/43THENoELSEn/a--theSIPReferred-Bymechanism. c21: IFA.3/2THENmELSEIFA.4/38THENoELSEn/a--P-CSCF,theReasonheaderfieldforthesessioninitiationprotocol. c22: IFA.4/40THENmELSEn/a--callerpreferencesforthesessioninitiationprotocol. c23: IFA.4/60THENmELSEn/a--SIPlocationconveyance. c24: IFA.4/38THENmELSEn/a--theReasonheaderfieldforthesessioninitiationprotocol. c25: IFA.4/70BTHENmELSEn/a--inclusionofCANCEL,BYE,REGISTERandPUBLISHincommunicationsresourcepriorityforthesessioninitiationprotocol. c26: IFA.4/76THENoELSEn/a--transportingusertouserinformationforcallcentersusingSIP. c27: IFA.4/80THENoELSEn/a--theP-Debug-IDheaderfieldforthesessioninitiationprotocol. c28: IFA.4/80THENmELSEn/a--theP-Debug-IDheaderfieldforthesessioninitiationprotocol. c29: IFA.4/71AND(A.3/9BORA.3/9C)THENmELSEIFA.3/1ANDNOTA.3C/1THENn/aELSEo--addressinganamplificationvulnerabilityinsessioninitiationprotocolforkingproxies,IBCF(IMS-ALG),IBCF(ScreeningofSIPsignalling),UE,UEperformingthefunctionsofanexternalattachednetwork. c30: IFA.4/71THENmELSEn/a--addressinganamplificationvulnerabilityinsessioninitiationprotocolforkingproxies. c31: IFA.3/1ANDNOTA.3C/1THENn/aELSEo--UE,UEperformingthefunctionsofanexternalattachednetwork. c32: IFA.4/91THENmELSEn/a--theSession-IDheader. |
|||||||
NOTE: Supportofthisheaderinthismethodisdependentonthesecuritymechanismandthesecurityarchitecturewhichisimplemented.Useofthisheaderinthismethodisnotappropriatetothesecuritymechanismdefinedby3GPPTS33.203[19]. |
11.1.
UAC行为
一旦构造了BYE,UAC核心创建新的非INVITE客户端事务,并将其传送给BYE请求。BYE请求一旦发送给客户端事务,UAC
必须认为会话结束了(并因此停止发送和侦听媒体)。如果BYE响应是481(呼叫/事务不存在)或者408(请求超时),或者请求根本没有接收到BYE的响应(即是INVITE客户端事务返回超时),那么,UAC必须认为会话和对话结束了。
11.2.
UAS行为
UAS首先遵循第5.2
节介绍的通用UAS处理过程来处理BYE请求。
UAS核心接收到BYE请求,检查它是否和现有对话匹配。如果BYE不能和现有对话匹配,那么,UAS核心必须生成481(呼叫/事务不存在)响应,并将其发送给服务器事务。
此规则意味着,将拒绝UAC 发送的无标签BYE。这是对RFC
2543的修改——它允许无标签的BYE。
UAS核心接收现有对话的BYE请求,并且发现和现存的对话匹配,必须遵循对话处理过程处理请求。一旦完成,UAS应该结束会话(并因此停止发送和侦听媒体)。它唯一不可以选择的情况是多播会话,在多播会话中,即使会话的其他参与者已经结束了其包含的会话,都有可能参与(participation)。不管它是否结束参与会话,UAS核心必须对BYE生成2xx
响应,并将其发送到服务器事务传输。
UAS必须响应任何此对话中接收的挂起的请求。推荐对于这些挂起的请求生成487(请求结束)响应。
12.
取消请求(CANCEL)
CANCEL请求,是用于取消客户端发送的前一请求。特别的是,它要求UAS
停止处理请求,并为请求生成错误响应。对占用服务器很长时间才能响应的请求来说,CANCEL请求是最有用的。因为这样,CANCEL请求在占用服务器很长时间响应的地方很有用。为此,对需要很长时间生成响应的INVITE请求,CANCEL是最好的。按照常例,接收到INVITE的CANCEL请求,但是没有发送最终响应的UAS,可能会“停止响铃”,然后给INVITE发送特定的错误代码(487)。
代理和用户代理客户端都可以构造CANCEL请求。
有状态代理对CANCEL做出响应,而不是简单地转发它从下游元素接收的响应。因此,因为每个有状态代理的跳跃点都响应它,CANCEL被称为“逐跳”请求。
12.1.
UAC行为
不应该发送CANCEL取消INVITE外的请求。
因为INVITE外的请求是立即响应的,为非INVITE发送CANCEL会创造竞态条件。
使用下列过程来构造CANCEL请求。CANCEL请求中的Request-URI、Call-ID、To、Cseq的数字部分和From
头字段,必须与要取消的请求中的一样,包括标签。客户端构造的CANCEL必须有单一的Via与要取消的请求中的最上面Via值匹配。这些头字段使用相同的值,允许CANCEL和它要取消的请求匹配。然而,此方法的CSeq头字段必须有CANCEL值。这允许它凭借自身的实力确定和处理为事务。
如果要取消的请求包括Route 头字段,那么CANCEL请求必须包括此Route
头字段值。
这是必需的以便于无状态代理可以正确地路由CANCEL请求。
CANCEL请求不能包含任何Require和Proxy-Require头字段。
一旦构造了CANCEL,客户端应该检查是否接收到要取消请求(这里指“原始请求”)的任何(临时或者永久)响应。
如果没有接收到临时响应,必须发送CANCEL请求,当然,客户端必须等待发送请求之前的临时响应。如果原始请求产生最终响应,不应该发送CANCEL,因为它是有效的无操作,而CANCEL对已经生成最终响应的请求没有影响。
当客户端决定发送CANCEL时,它为CANCEL创建客户端事务,并将其和CANCEL请求以及目的地址、端口和传输一起发送。CANCEL请求的目的地址、端口和传输必须和发送请求的一样。
在接收到之前请求的响应之前,如果允许发送CANCEL,那么,服务器将在原始请求之前接收到CANCEL。
注意,原始请求对应的事务和CANCEL事务是完全独立的。然而,UAC
取消请求不依靠接收原始请求的487(请求超时)响应,因为与RFC2543
兼容的UAS
不会生成这样的响应。如果在64*T1
秒内没有原始请求的最终响应,客户端应该考虑取消的原始事务,并且应该破坏客户端事务处理原始请求。
12.2.
UAS行为
CANCEL方法要求服务器边的TU取消挂起的事务。TU确定使用CANCEL请求取消的事务,然后假定请求方法是CANCLE和ACK之外的任何方法,并进行事务匹配过程。取消匹配事务。
服务器的CANCEL程序请求与服务器的类型有关。无状态代理将转发CANCEL,有状态代理可以响应CANCEL,并生成自己的CANCEL请求,同时,UAS将响应CANCEL。
UAS 首先根据第5.2节介绍的通用UAS
处理方法处理CANCEL请求。然而,因为CANCEL请求是逐跳的,并且不可以重新提交,为了在Authorization头字段得到正确的凭证,服务器不会向它们发出挑战。注意,CANCEL请求也不包含Require头字段。
如果UAS 没有找到与上面过程的CANCEL匹配的事务,那么它应该返回481(呼叫腿/事务不存在)响应。如果原始请求的事务仍然存在,接收到CANCEL请求的UAS行为与它是否已经发送了原始请求的最终响应有关。如果它对会话状态和原始请求生成的响应没有影响,那么CANCEL请求对原始请求的处理没有影响。如果UAS没有发布原始请求的最终响应,那么,其行为和原始请求的方法有关。如果原始请求是INVITE,UAS应该立即响应INVITE,返回487(请求结束)。CANCEL请求对本规范中定义的其它方法的事务处理没有影响。不管原始请求的方法是什么,只要CANCEL可以匹配现有的事务,那么,UAS回答CANCEL请求200(OK)响应。遵循第5.2.6
节介绍的过程构造此响应。注意,CANCEL响应的To标签和原始请求响应的To标签应该是一样的。CANCEL响应传送给服务器事务发送。
13.
即时消息(MESSAGE)
即时消息被定义为一组参与实体之间的实时消息交互,通常情况下,这些消息是文字信息,并且这些消息不需要存储。SIP即时消息通过扩展方法MESSAGE实现。
当用户想要发送即时消息时,该用户通过MESSAGE方法发起一个SIP请求。MESSAGE请求的Request
–URI可以是SIP URI,也可以是其它类型的地址。
传递消息内容包含在MESSAGE请求的消息体中,该消息体可以是任何MIME类型,包括message/
CPIM 。但更建议message/cpim,因为已有的IM系统标准是message/cpim格式
MESSAGE消息被送达之前,可能通经过一系列SIP
proxy,每个SIP
proxy需要重写请求URI以提供消息传递的路由信息。
像SIP其它请求一样,收到MESSAGE应回临时应答和最终应答。200-OK只说明消息成功到达了目的地,并不代码用户已经阅读。
MESSAGE请求并不创建对话。
13.1.
UAC行为
MESSAGE body必须支持plain/text格式。可以选择支持message/cpim格式。由于不创建一个dialog,所以MESSAGE不应该包含contact头域。MESSAGE可以在in dialog里发送,此时代表这个消息和某个dialog有关联关系(即发消息的URI为SIP URI)。
最终应答的含义,
200-OK:消息已成功送达目的地,但不保证对方用户已阅;
202-Accept:消息已成功发送到某网关,但不保证网关一定能把该消息送达目的地。
外发proxy有可能把MESSAGE分叉,可以加Expire头域,Date头域表明消息的生存时间。
13.2.
使用IM URI
Request URI,From头域,To头域可以填SIP URI,实在不行也可以填IM URI,此时会有proxy解析成SIP URI。
和路由相关的头域中的URI必须是SIP URI。
13.3.
UAS的操作
和SIP相关的操作参见rfc3261。
200-OK:UAS收到MESSAGE,应立即回200-OK,但是是否把消息的内容显示给用户与本地策略有关 (是不是可以用来制作黑名单功能?) 。200-OK不能带body,,也不能携带Contact头域。
202-Accepted:如果自己不是MESSAGE的最终用户,就回202-Accepted。意味着该MESSAGE会被尽力转发,但不保证一定能到达目的地。
4xx, 5xx :4xx, 5xx表示消息未被成功发送。
6xx :6xx表示消息虽被成功发送,但已被拒收。
支持MESSAGE就必须支持text/plain格式的MIME type。也可以选择支持message/cpim格式。
如果消息携带有Expire头域,就处理超时,否则没有超时的概念。
如果UAS收到消息时该消息已经超时,可以选择处理该消息这和本地策略有关。例如可以选择丢弃,也可以正常显示给用户(标明超时),或采取其它策略。
如果消息不能被正确中继,如何处理该消息也与本地策略有关。
13.4.
拥塞控制
MESSAGE用信令携带媒体,所以流量会很大,需要特殊考虑。 如果可能,MESSAGE最好使用有拥塞控制的传输层协议,如TCP,SCTP。
消息本身的大小不一不要超过1300个字节,除非你知道确切的PMTU大小。
对于不采用Dialog方式的消息,发往同一个目的URI的MESSAGE,如果上一个transaction还没有结束,就不允许发送下一个MESSAGE;而且如果不是路由设置每一跳在传输中采用拥塞控制,用Dialog传送的MESSAGE也禁止这么做。
有人曾建议为了减少拥塞,MESSAGE不必回临时应答。实际上没有必要规定这个。因为很多代理服务器根本就不关心是否是MESSAGE方法,他们只管转发。
14. Proxy行为
SIP代理是将SIP请求路由到用户代理服务器和将SIP响应路由到用户代理客户端的元素。到达UAS
的请求可以穿透其间的多个代理。每个代理都可以作出路由决定,并在将请求转发到下一个元素之前对其修改。响应将通过代理路由,该代理与逆序请求穿透的代理相同。代理作为SIP元素的逻辑角色。当请求到达时,该元素在作为代理之前先确定是否需要响应其上的请求。例如,在充当代理之前,该请求可能难以理解或者该元素需要客户端的凭证。该元素可能用适当的错误代码作出响应。当直接响应请求时,该元素则扮演UAS
的角色,并且其行为必须如第5.2
节所述。
对于每个新的请求,代理可以以有状态或者无状态模式操作。当采用无状态模式时,代理仅充当一种简单转发元素。它转发每一个请求下行到一个由请求所决定的目的地,该目的地是基于请求由目标和路由决定确定的。它简单地转发它接收的每个上流响应。一旦请求被转发,无状态代理将丢弃该请求相关的所有状态信息。
有状态代理将保留每个入站请求的信息(特别是事务状态),并且它所发送的任何请求都作为入站请求处理结果。有状态代理使用入站请求信息来影响与该请求相关的下一步消息处理。有状态代理可以选择“分发”请求,并将它路由到多个目的地。转发给多个位置的请求都必须有状态处理。
在某些情况中,代理可能在未成为有状态事务时就使用有状态传输协议(如TCP)转发请求。例如,代理可以将请求从一个TCP
连接转发到另一个无状态事务,只要它在消息里放有足够多的信息以将该响应向下转发给该请求所到达的相同连接上。在不同传输类型之间所转发的请求必须有状态的转发事务,这里代理的TU
必须主动地确保在每一个传输上都是可靠传输。
在请求处理期间,只要有状态代理不作阻止其回到初始无状态的事情(例如,分发或生成一个100
响应),它就可以转换为无状态操作模式。当完成这样的转换时,丢弃了所有状态。代理不应发起CANCEL
请求。
无论请求是有状态还是无状态,相关的许多处理都是相同的。
14.1.
有状态代理
当处于有状态模式时,代理则完全作为一个SIP事务处理引擎。在此按照服务器和客户端事务模拟其行为。有状态代理通过高级代理处理组件(作为代理核心)将一个服务器事务与多个客户端事务联系在一起。入站请求由服务器事务来处理。服务器事务处理后的请求传送到代理核心。代理核心确定将该请求路由到何处,并选择一个或者多个下一跳位置。每个下一跳位置的出站请求都由其自身相关的客户端事务来处理。代理核心收集来自客户端事务的响应,然后使用客户端事务将响应发送到服务器事务。
有状态代理为每个所接收的新请求创建新的服务器事务。随后按第14章介绍的服务器事务,处理请求的转发。在发送及时的临时请求给第5.2.6节介绍的服务器事务(如,100(Trying))时,代理核心必须作为UAS。因此,有状态代理不应该对非INVITE请求生成100(Trying)响应。
这是代理行为的模型,而不是软件。可以*地采用任何方法来复制该模型定义的外部行为。
对于所有新的请求,包括未知方法、作为代理的元素,请求必须:
1. 确认请求
2. 预处理路由信息
3. 确定请求目标
4. 向每个目标转发请求
5. 处理所有响应
14.2.
请求确认
在元素代理请求之前,它必须确认消息的有效性。有效消息必须经过以下检查:
1. 合理语法
2. URI 模式
3. Max-Forwards
4. (可选)循环检测
5. Proxy-Require
6. Proxy-Authorization
如果其中任何检查失败,该元素必须作为用户代理服务器(参见第5.2节),然后响应错误代码。
注意代理不需要检测已合并的请求,它不应将已合并的请求作为错误的条件。接收请求的端点将解析合并,如第5.2.2节所述。
1、合理的语法检查
请求必须足够规格化,以便于与服务器事务一起处理。在剩余的这些请求确认步骤中或者请求转发章节所涉及的组件必须是规格化组件。对于其它组件,则对规格化的要求不是很严格,当转发消息时,可以忽略此组件或者该组件保持不变。举个例子来说,由于Date头字段的格式不好,元素将不能接受请求。同样的,代理不应该在转发请求之前将格式不好的Date头字段删除。
本协议可用来扩展。新扩展可以随时定义一些新的方法和头字段。这样元素不能因为请求中包含它所不知道的方法和头字段而拒绝代理请求。
2、URI
模式检查
如果Request-URI的URI模式不为代理所理解,代理应拒绝该请求,并返回416(不支持URI
模式)响应。
3、Max-Forwards
检查
Max-Forwards头字段用于限制SIP请求可以穿透的元素数。
如果请求不包含Max-Forwards头字段,Max-Forwards检查通过。
如果该请求包含Max-Forwards头字段,该字段是比零大的字段值,则Max-Forwards检查也通过。
如果该请求包含Max-Forwards头字段,该字段值为零,元素不需转发请求。如果该请求用于OPTION,该元素可以作为最终接受者,并作出响应。此外,元素必须返回483(太多跳跃点)响应代码。
4、可选循环检测检查
在转发请求前,元素可以对转发循环作检查。如果请求包含了Via头字段,所发送的值等于代理预先放置在请求中的值,该请求先由该元素转发。请求通过该元素循环或者合理的螺旋。为了确定了请求是否循环,该元素可以完成分支参数计算,并将它与Via
头字段中所接收的参数进行比较。如果参数匹配,请求循环。如果它们不相同,则请求正螺旋,处理继续。如果探测出发生循环,则该元素返回482(循环探测)响应。
5、Proxy-Require检查
对协议作进一步扩展,则可以引入代理特殊处理的功能。端点在请求中加入使用这些特性的Proxy-Require(代理需求)头字段,告知代理除非理解该特性,否则不处理该请求。如果该请求包含Proxy-Require头字段,它带有一个或者多个该元素所不理解的可选标签,元素必须返回420(错误扩展)响应。该响应必须包括Unsupported头字段,该字段列出了该元素不理解的可选标签。
6、Proxy-Authorization如果代理要求在转发请求之前进行身份认证,那么必须进行请求检查。
14.3.
路由信息处理
代理必须检查请求的Request-URI。如果请求的Request-URI包含了该代理预先放置在Record-Route
头字段值,代理必须使用Route
头字段中的最后一个值代替请求中Request-URI
的值,并且将该值从Route
头字段中去掉。一旦代理接收到已修改的请求,它将继续处理。
这种情况只有在元素向作为严格路由器的代理(可以是一个端点)发送请求时发生。对所接收的请求重写必须具有这些元素的向后兼容性。它也允许遵循本规范的元素,通过严格路由代理保留Request-URI。
本规范不强制代理保持状态,以便于它检查先前放置在Record-Route头字段中的URI。反过来说,代理只需要在这些URI中放置足够多的信息,它就可以在随后出现时可以识别其值。
如果Request-URI包含maddr参数,那么代理必须检查所配置代理负责的值是否在地址集或者域中。如果Request-URI包含带值的maddr参数,该值由代理负责,并且使用Request-URI中所指示(明确指示或者默认)的端口和传输来接收请求,那么,代理必须去除maddr参数和任何非默认端口或者传输参数,并且继续处理,就像这些值从未在请求中出现一样。
如果所接收请求带有与代理匹配的maddr,但是该maddr参数与URI中所指示的端口或者传输不同。这样就需要使用所指示的端口和传输将请求转发给代理。
如果Route 头字段中的第一个值指明这个代理,该代理必须将此值从请求中去掉。
14.4.
确定请求目标
接下来,代理计算出请求的目标。目标集合可以由请求的内容预先确定,也可以从抽象定位服务中获得。集合中的每个目标都作为URI。
如果请求的Request-URI包含maddr参数,Request-URI必须作为唯一的目标URI放在目标集合中,并且,代理必须继续请求转发。
如果Request-URI的域表示此域不是该元素负责的域,那么,Request-URI
必须作为唯
一的目标URI 放在目标集合中,同时,此元素必须继续请求转发的任务(第16.6
节)。
在很多情况中,代理可能接收到其不负责的域的请求。防火墙代理处理出站呼叫(HTTP
代理处理出站请求的方式)是可能发生此情况的一个实例。
如果请求的目标集合没有如上所述预先确定,那么,它意味着元素是对Request-URI的域负责任的,同时,元素可以使用它想要的任何机制,确定发送请求的地址。这些机制可以作为访问抽象定位服务的模型。这可能由以下几个部分组成——获取SIP注册服务器创建的定位服务信息、读取数据库、查询状态服务器、使用其他协议和在Request-URI中简单地执行算法替代。当访问注册服务器构造的定位服务时,在将其用作索引之前,首先要将Request-URI规范化。这些机制的输出用作构造目标集合。
如果Request-URI没有为代理提供足够的信息来确定目标集合,那么,它应该返回485(模糊)响应。此响应应该包含Contact头字段——包含已作尝试的新地址的URI。如,对sip:John.Smith@company.com的INVITE可能有些含糊不清,其定位服务列出了多个John
Smiths。
在请求中或关于请求或元素当前环境的任何信息都可以用作构造目标集合。例如,依赖于头字段和消息体的内容和状态、请求间隔的延迟、请求到达的接口、之前请求的错误、甚至是元素的当前利用率,可以构造不同的目标集合。
当通过这些服务对潜在的目标定义时,将其URI添加到目标集合中。目标仅能在目标集合中放置一次。如果在目标集合中已经有目标URI(以URI
类型相同的定义为基础),那么,不能再添加目标URI。
如果原始请求的Request-URI没有指出代理负责的资源,那么,代理不能在目标集合中添加其它的目标。
只有代理对此URI负责时,代理才可以在转发期间修改Request-URI的URI。如果代理不对此URI负责,那么,它将不能使用下面介绍的3xx和416响应。
如果原始请求的Request-URI指出了代理负责的资源,那么,在开始请求转发后,代理可以在目标集合中添加目标。它可以使用在处理过程中获取的任何信息来确定新目标。例如,代理可以选择将重定向响应(3xx)中获取的联系信息合并到目标集合中。如果在建立目标集合的同时,代理使用一个动态信息资源(如,它查询SIP
注册服务器),那么,在处理请求的过程中,它应对资源监控。一旦新的地址有效,应该将其添加到目标集合中。如上所述,任何给定的URI不能多次添加到目标集合中。
仅允许URI 在目标集合中添加一次,可以减少不必要的网络流量,从重定向请求中获取的合并联系信息的情况,防止了无穷的网络递归。
例如,当目标URI和入站的请求URI相同时,通常的定位服务是“无操作”。为了进一步处理,请求发送到指定的下一跳代理。在请求转发期间,以SIP和SIPS
URI表示的下一跳识别符作为最上面的Route头字段值插入到请求中。
如果Request-URI指出此代理上的资源不存在,那么,代理必须返回404(没找到)响应。
如果在应用以上所有之后,目标集合仍然为空,代理必须返回480(暂时不可用)错误响应。
14.5.
转发请求
一旦目标集非空时,代理便开始转发请求。有状态代理可以以任何顺序处理这个目标集合。它可以串行处理多个目标,即允许客户端事务在下一个事务开始前完成。它也可以采用每个目标并行处理的方式启动客户端事务。它还可以将目标集合任意分组,对这些组串行处理,或者对每个组中的目标并行处理。
常用的排序机制是使用目标的q值参数,这些参数可从Contact头字段中获取。目标的处理顺序是从最高q值到最低q值。具有相同q值的目标可并行处理。
当接收响应时,有状态代理必须具有维护对象集合的机制,然后将对每个转发请求的响应与原始请求相关联。对于该模型而言,这种机制就是由代理层在转发第一个请求之前创建的“响应上下文”。
对于每个目标,代理都遵循以下步骤发送请求:
1、
复制所接收的请求
2、
更新Request-URI
3、
更新Max-Forwards头字段
4、
添加Record-Route头字段值(可选的)
5、
添加其它头字段(可选)
6、
后处理路由信息
7、
确定下一跳的地址、端口和传输协议
8、
添加一个Via头字段值
9、
如果需要,添加一个Content-Length头字段
10、
转发新请求
11、
设置计时器C
每个步骤详述如下:
1、
复制请求
代理首先将接收到的请求复制。这个副本最初必须包含所接收到的请求的所有头字段。在一下描述中没有明确说明的字段不可以去掉。副本应与所接收的请求保持相同的头字段顺序。代理不能用通用字段名对字段值重新排序。代理也不能添加、修改或删除消息体。
在实际执行中有时并不需要完成复制请求;但是主要前提条件是每个下一跳的处理都以相同的请求开始。
2、
Request-URI
必须用目标的URI代替位于副本起始行的Request-URI。如果这个目标的URI含有Request-URI中不允许的任何参数,那么必须删除它们。
这就是代理角色的本质。代理会通过这种机制将请求路由到目的地。
在某些情况下,接收到的Request-URI 未经修改便放到了目标集合中。对于该目标而言,无需执行以上替换操作。
3、
Max-Forwards
如果副本包含Max-Forwards头字段,那么代理必须将其值减1。
如果副本不含Max-Forwards头字段,那么代理必须添加一个Max-Forwards字段值,其值为70。
现有的某些UA 在请求中没有提供Max-Forwards
字段。
4、
Record-Route
如果这个代理希望保留在该请求所创建的对话中后续请求的路径上(假定这个请求会创建一个对话),那么即使已经存在Route头字段,它也必须在副本中任何已有的Record-Route头字段值之前插入一个Record-Route头字段值。
建立对话的请求可以包含一个预先加载的Route头字段。
如果这个请求已经是对话的一部分,那么,如果代理希望保留在对话中后续请求的路径上,它就应当插入一个Record-Route头字段值。在通常端点操作的情况下,这些Record-Route
头字段值不会对端点使用的路由集合产生任何影响。
如果代理选择不向已经是对话的一部分的请求中添加Record-Route头字段值,那么代理将会保留在该路径上。但是当失败端点重新构建对话时,将会从这个路径上去掉这个代理。
代理会向任何请求添加Record-Route头字段值。如果请求不启动对话,那么端点将忽略这个值。欲了解端点如何使用Record-Route头字段值构建Route头字段的详情。
请求路径中的每个代理都会单独选择是否添加Record-Route头字段值——请求中Record-Route头字段的状态并不迫使该代理添加一个值。
放入Record-Route 头字段值中的URI
必须是SIP或SIPS
URI。这个URI必须包含一个lr参数。这个URI
可以与请求转发到的每个目的地不同。除非代理知道(如在私有网络中)下一个将在后续请求路径中的下游元素支持此传输协议,URI不应包含传输参数。
这个代理提供的URI将被其它元素用来作路由决策。一般说来,这个代理无法知道其它元素的性能,因此它必须将其本身限制为SIP执行的强制元素:SIP
URI和TCP/UDP传输。
当对放置在Record-Route头字段的URI应用服务器定位的第四步时,这个URI必须对插入此URI的元素解析,这样后续请求就可以访问相同的SIP元素。如果Request-URI
包含SIPS
URI,或顶端Route头字段值(在步骤6
的后处理后)包含SIPS
URI,那么放置在Record-Route头字段的URI必须是SIPS
URI。此外,如果没有在TLS之上接收到这个请求,代理就必须插入一个Record-Route头字段。与之类似的情况:代理在TLS
上接收请求,但是生成的请求没有在Request-URI或顶端Route头字段值中(在步骤6
的后处理后)的SIPS
URI,那么代理必须插入一个非SIPS
URI的Record-Route头字段。
在整个对话期间,位于安全边界的代理必须保留此边界。
当放置在Record-Route头字段中的URI以响应的形式回传时,如果它需要重写,那么这个URI必须与众不同,以便在此时可以定位。(这个请求可通过此代理螺旋,导致了添加多个Record-Route头字段值)。
代理可在Record-Route头字段值中包含参数。这些参数值将在对该请求的某些响应中得到回应,这些响应如:对INVITE的200(OK)响应。这样的参数对于在消息中而不是在代理中保持状态会很有用。
如果某代理需要存在于任何类型的对话(如一用户要跨越防火墙)路径中,那么它应当用一个它不理解具有对话语义的方法向每个请求添加一个Record-Route头字段值。
代理放置在Record-Route 头字段中的URI,仅仅在出现URI
的事务所创建的任何对话的生命周期内有效。例如,对话有状态代理可在对话终止后可以拒绝接受在Record-Route中有该值的后续请求。当然,非对话有状态协议没有对话何时终止的概念,但它们可以对值中信息编码,以将其与后续请求的对话标识符相比较,然后会拒绝与那些信息不匹配的请求。
端点不能在提供它的对话外使用从Record-Route头字段获得的URI。
代理需要观察对话中所有消息的某些服务可能需要Record-Routing。但是,Record-Routing会减慢处理过程,并降低可扩展性,使得代理只在特定服务需要的情况下进行Record-Route。
Record-Route过程旨在为启动对话的任何SIP
请求工作。INVITE
是本规范中唯一属于这类的请求,但是这个协议的扩展可以定义其它请求。
5、
添加其他头字段
在这一点上,代理可以向副本添加任何其它适当的头字段。
6、
对路由信息进行后处理
代理可以设置本地策略,要求请求在传送到目的地之前访问特定代理集合。代理必须确保所有这些代理都是松散路由。一般说来,如果这些代理位于同一管理域中,这点可能比较容易明白。该代理集合由URI集合(每个URI都包含lr参数)表示。该代理集合必须放入副本中的Route头字段中,并且位于任何现有的值之前,如果这些值存在的话。
如果代理可以设置本地策略,要求请求访问一个特定代理,那么将Route值放入Route头字段中的可选办法是跳过下面描述的第10步的转发步骤,而仅向那个特定代理的地址、端口以及传输协议确定的目的地发送请求。如果这个请求有Route头字段,那么除非知道下一跳代理是松散路由,否则不能使用这种可选方法。此外,虽然这个方法也可用,但对于操作的健壮性、灵活性、通用性和连贯性来说,Route插入机制是更可取的。而且,如果Request-URI包含SIPS
URI,那么必须使用TLS与该代理进行通信。
如果副本包含一个Route头字段,那么代理对其第一个值中URI检查。如果此URI不含有lr参数,这个代理必须对该副本做如下修改: