取消一个请求(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请求被创建了,客户端应当检查是否收到了这个CANCEL请求取消的原始请求的任何应答(临时的或者终结的应答)。如果没有任何临时应答收到,这个CANCEL请求一定不能发送,直到客户端等到了第一个临时应答。如果原始请求已经收到一个终结应答,这个CANCEL也不应当发送,因为CANCEL请求对已经产生了终结应答的请求没有任何作用
当客户端决定发送一个CANCEL,它会为这个CANCEL创建一个客户transaction,并且通过目的地址、端口、传输层来发送CANCEL请求。这个CANCEL中的目标地址、端口和传输层必须和原始请求一样。
服务端行为(Server Behavior)
UAS不能找到与CANCEL请求相匹配的事务,它应该给CANCEL一个481应答(调用的Leg/Transaction不存在 会话/事务不存在)。
如果已经发出了,不会对CANCEL请求对应的原始请求做任何处理,不会更改任何会话状态,不会对原始请求的应答做任何处理。如果UAS没有发出对原始请求的终结应答,它会依赖于CANCEL所取消的原始请求方法。如果原始请求方法是INVITE,UAS应当立刻响应INVITE一个487(请求终止)
查询能力
在客户端试图在INVITE请求头中增加一个请求字段选项的时候,它并不知道对方UAS能否支持这个选项,它就可以用OPTIONS来查询一下UAS,通过检查OPTIONS返回的Supported头域,就可以知道是否支持这个选项。所有的UA都必须支持OPTIONS方法。
给OPTIONS的应答的构造遵循标准的构造规则(8.2.6节描述)。应答码的选择必须和处理INVITE请求一样的应答码(就像处理INVITE请求一样的返回)。这就是说,200(OK)应当在UAS能够接收请求的时候返回,486(忙)应当在UAS如果忙的时候返回。这样OPTIONS可以用来检测UAS的基本状态,这就是说,我们可以用OPTIONS来知道UAS能否接收INVITE请求。
应答没有消息体
Allow,Accept,Accept-Encoding,Accept-Language,和Supported头域应当在200(OK)应答中出现。
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)
对话(Dialog)
一个对话在参与对话的UA中都有一个dialog ID作为标记,这个ID由Call-ID,和一个本地tag和远程tag组成。
路由集合是由发送请求到对方需要途径的一组服务器列表组成。一个对话可以处于”early”状态,这是由于当这个对话收到了临时应答而创建,并且当收到了2xx终结应答的时候转换到”confirmed”状态。对于其他应答,或者没有应答,”early”对话将会终结。
创建一个对话
UAS行为
当UAS响应一个请求给出一个应答,并且这个应答会建立一个对话的时候(比如对INVITE的2xx应答),UAS必须拷贝所有的请求中的Record-Route头域到应答中去(包括URI,URI参数,和其他任何Record-Route头域的参数,无论UAS是不是认识的参数都需要原样拷贝),并且必须维持这些参数的顺序。UAS必须增加一个Contact头域给应答。这个Contact头域包含一个UAS在后续对话请求中接收请求的地址(这个包含了给INVITE请求的2xx应答的ACK请求处理的地址)。
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。
对话中的请求
UAC行为
在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头域中去除。
请求的Call-ID必须设置成为对话的Call-ID。在对话中的请求必须严格遵循单个递增的Cseq序列号(每次增加1)(当然要除了ACK和CANCEL,这两个请求中的Cseq必须和原始的请求或者确认请求一样)
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>
创建一个初始化的INVITE
在INVITE中应当包括一个Allow头域(20.5节)。它用来标志在这个INVITE建立的这个对话(dialog)中什么样的方法可以接受。比如,一个UA可以在对话中接收和处理INFO请求[34],那么在INVITE