sip協議中文(2)

6、協議的定義
以下講述的名次對SIP有着額外的意義:
Address-of-Record: 記錄地址。一個address-of-record(AOR)是一個SIP或者SIPS URI它指向了一個具有定位服務的主機,這個主機可以把URI映射成爲用戶真正物理位置的URI。通常情況下,定位服務器是通過登記服務來建立的。一個AOR經常被認爲是一個用戶的”公共地址”
Back-to-Back UserAgent:背對背的用戶代理(B2BUA)是一個邏輯實體,它就像用戶代理服務器(UAS)一樣接收和處理請求。爲了決定該如何應答一個請求,B2BUA就像UAC一樣工作,並且發出請求。但是它不像代理服務器(proxy),它維持對話狀態,並且參與已經建立的對話中的每一個請求。由於它是直接的UAC和UAS的串連,所以,不需要對他有額外的定義。

Call:呼叫,一個呼叫是一個非正式的術語,它是指在端點之間一個一些通訊行爲,通常用於建立多媒體對話。
Call Leg: 對話的別名;在本規範中沒有使用。

Call Stateful: 如果一個代理服務器(proxy)保存一個對話的狀態(從最開始的INVITE到對話終結的BYE),那麼這個代理服務器就是請求有狀態的。一個請求有狀態(call stateful)的代理服務器也一定是事務有狀態的,但是事務有狀態的不一定是請求有狀態的。

Client:客戶端。一個客戶端是一個任意的網絡元素,它發出SIP請求和接收SIP應答。客戶端可能會也可能不會和人交互。用戶代理客戶端(UAC)和代理服務器都是客戶端。

Conference: 一個包含多個參與方的多媒體會話(見後)。

Core:核心。核心定義了SIP實體的特定類別。比如定義了一個有狀態和無狀態的代理服務器,一個用戶代理或者註冊服務器(registrar)。所有的核心,除了無狀態代理服務器,都是事務用戶。

Dialog:對話,一個對話是持續一段時間的兩個UA之間的端到端的SIP關係。一個對話由SIP消息建立,就像用2xx響應INVITE請求。我們用Call identifier,local tag(本地tag),remote tag(對方tag)來標誌一個對話,一個對話在RFC 2543中被正式叫做CALL LEG.

Downstream: 它是事務中的消息傳遞方向。它特指從UAC到UAS的請求流的方向,

Final Response:終結響應。一個響應終端SIP事務的應答,和事務中間的臨時響應相反。所有的2xx,3xx,4xx,5xx,6xx響應都是終結響應。

Header:頭。頭域是在SIP消息頭部用來描述這個SIP消息信息的部分。它由一堆頭域字段組成。

Header Field:頭域字段。頭域字段是在SIP消息頭域的字段。一個頭域字段可以由多個頭域字段行組成。一個頭域字段由一個頭域名和(零個或多個)頭域值組成。多個頭域值用’,’分割。某些頭域字段只能有單個值,比如結果域(result)就只能有一個值。

Header Field Value:頭域值。一個頭域值是一個單個的值,一個頭域字段可以有0個或者多個頭域值。

Home Domain:宿主機。一個提供SIP服務的主機。一般指的是在登記服務中指明的記錄地址中的URI的主機。

Informational Response:提示應答。和臨時應答一樣。
Initiator, Calling Party, Caller: 用INVITE初始一個會話(和對話)的那方。一個caller從發出INVITE請求建立對話開始,到對話終止都一直是這個角色。

Invitation: 一個INVITE請求。

Invitee,Invited User,Called Party, Callee:被叫方。收到INVITE請求並且建立會話的那方。一個被叫方從收到INVITE請求起,到終止INVITE建立的對話結束,都稱作被叫方。

Location Service: 定位服務。定位服務是用來給SIP轉發或者代理服務器確定被叫方可能的位置使用的。它包含一張綁定了address-of-record的表,被叫方可能有0到多個記錄。綁定的記錄可以通過多種渠道添加和刪除;本規範定義了REGISTER方法來更新綁定表。

Loop:環路。當請求抵達一個代理服務器,代理服務器轉發這個請求,當這個請求再次來到同一個代理服務器,就稱之爲環路。當第二次抵達的時候,Request-URI中包含了上次抵達的資料,並且由於並沒有什麼東西可以改變轉發的策略,這樣就導致這個請求還會再次被轉發回來。環路請求是錯誤的,所以,處理程序需要檢測和防止協議中出現的環路請求。

Loose Routing:丟失路由。代理服務器在下述情況下會丟失路由。
A proxy is said to be loose routing if it follows the procedures defined in this specification for processing of the Route header field. These procedures separate the destination of the request (present in the Request-URI) from  the set of proxies that need to be visited along the way (present in the Route header field). A proxy compliant to these mechanisms is also known as a loose router.

Message:消息。SIP元素之間傳送的協議數據就是消息。SIP消息既可以是請求也可以是應答。

Method:方法。方法是在服務器請求處理的主要功能。方法是請求消息自身攜帶的。典型的方法就是INVITE和BYE。

Outbound Proxy:對外代理服務器。一個代理服務器接收到客戶的請求,即使它不是由Request_URI所決定的服務器。通常一個UA會手工配置一個對外的代理服務器,或者可以通過一個自動配置的協議自動配置一個。

Parallel Search: 並行搜索。並行搜索情況下,代理服務器會向多個用戶可能存在的地方發起請求,並且等待應答。同串行搜索不同的地方是,並行搜索不會等待上一個請求應答回來之後再發起下一個搜索,而是一個接一個的發起搜索請求。

Provisional Response: 臨時應答。服務器用來標誌自己正在處理的應答,但是本應答並不結束一個SIP事務。1xx應答就是臨時的,其他應答標誌着事務的結束。

Proxy,Proxy Server:代理、代理服務器。一箇中間的實體。它本身即作爲客戶端也作爲服務端,爲其他客戶端提供請求的轉發服務。一個代理服務器首先提供的是路由服務,也就是說保證請求被髮到更加”靠近”目標用戶的地方。代理服務器對某些強制政策有用(比如,確認一個用戶是否允許建立一個呼叫等)。一個代理服務器翻譯,並且,如果有需要的話,再轉發前會重寫請求消息。

Recursion:迴路、遞歸。一個客戶端,在響應請求的時候產生新的到Contract包頭域的URI請求的時候,會在3xx響應中陷入遞歸。A client recurses on a 3xx response when it generates a new request to one or more of the URIs in the Contact header field in the response.

Redirect Server:重定向服務器。一個重定向服務器是一個產生3xx應答的UAS服務器,指示客戶端連接別的URI。

Registrar: 登記員。一個登記員(登記服務器)是一個接收REGISTER請求得服務器。他把請求得信息放到定位服務器中,這樣可以讓定位服務器很方便得查找位置信息。

Regular Transaction:常規事務。凡不包含INVITE,ACK,或者CANCEL方法得事務就是常規事務。

Request: 請求。 一個由客戶端發到服務端得SIP信息,用於執行特定得功能。

Response:應答。一個由服務端發到客戶端得SIP信息。用來標誌從客戶端發往服務端得請求處理得情況得。

Ringback: 回鈴音。回鈴音是一個信號音。是給呼叫方得一個信號表示被叫方正在振鈴(Ringing)。

Route Set: 路由集。路由集合是一個順序得SIP或者SIPS URI。這些URI描述了傳遞一個請求所必須經歷得代理列表。一個路由集可以是自適應得,因爲包頭中包含了Record-Route(記錄路由),也可以是依賴配置得到得。

Server:服務器。一個server是一個網絡元素接收請求並且處理請求並且發送迴應給請求方。典型得服務器就是代理服務器(proxies),用戶代理服務器(user agent servers),重定向服務器,登記服務器。

Sequential Search:順序查找。在順序查找中,代理服務器順序嘗試聯繫地址,在處理下一個之前必須等待上一個請求已經有一個結束應答。一個2xx或者6xx系列得最終應答總是結束一個順序查找。

Session:會話。根據SDP得描述:”一個多媒體會話是一個由多媒體發送方和接受方組成得集合,並且包括在發送方和接受方之間得數據流。一個多媒體會議是一個典型得多媒體會話。”(RFC 2327[1])(一個session在SDP訂一下可以是一個或者多個RTP sessino)。在定義中,一個被叫方可以被多次邀請,被不同得呼叫方邀請,到同一個會話。在SDP中,一個會話可以被SDP用戶名,session id,網絡類型,地址類型,地址元素得一個集合串所規定。

SIP 事務:一個SIP事務是在客戶端和服務端得事件,包括了從第一個由客戶端發送到服務端得請求,到最後一個(非1xx)服務端向客戶端發出得終結應答。如果請求是一個INVITE請求,並且終結應答是一個非2xx得應答,那麼事務還包括一個ACK給服務器做應答。給INVITE請求的2xx應答的ACK迴應,是一個獨立的事務。

Spiral:回溯。一個回溯是指一個SIP請求,路由給一個proxy,並且轉發,但是又被路由回這個proxy,但是不同於迴路(遞歸)的是,這次路由回來的請求包的包頭中,包含了不同於原請求的請求包部分,使得本次proxy決定的路由轉發與上次不同。通常,這是說,請求的Request-URI不同於上次的Request_URI。一個回溯不是一個錯誤,不同於迴路(環路loop)。通常導致這樣的現象是呼叫轉發(call forwarding)。一個用戶呼叫
[email protected]。example.com代理服務器轉發請求到Joe的PC,並且Joe的pc呼叫轉移到[email protected]。這個請求被轉發回example.com代理服務器。可是這個並不是一個環路(loop)。因爲請求的目的地址變成了另一個用戶,這就是回溯,是一個合法的情況。

Stateful Proxy:有狀態的代理服務器。在邏輯上,有狀態的代理服務器就是處理一個請求的過程中,維持的一個本規範所定義的客戶端和服務端的事務狀態機。也是一個事務又狀態代理服務器(transaction stateful proxy)。具體的stateful proxy在第16節定義。一個(事務)有狀態代理服務器和一個call stateful proxy不是一回事。

Stateless Proxy:無狀態的代理服務器。在邏輯上,無狀態代理服務器在處理請求中,並不維持客戶和服務端的事務狀態機。一個無狀態的代理服務器直接轉發每一個接收到的請求和每一個接收到的響應。

Strict Routing:嚴格路由。路由處理規則如果複覈RFC2543協議(and many prior work in progress versions of this RFC) 就是一個嚴格路由。在這個規則下,如果在包頭中包含Route域,那麼代理服務器就會刪除Request_URI域內容。本文檔並不要求一定要有嚴格路由,本文檔只要求鬆散路由就可以了。支持嚴格路由的代理服務器也叫嚴格路由器。

Target Refresh Request: 目標刷新請求。一個Target Refresh Request是一個在對話中發出的請求,用來更改對話目標的請求。

Transaction User(TU):事務用戶。在transaction 層之上的協議層。TU包括了UAC 核心,UAS core,和proxy core。


Upstream:上行流。一個在事務中的消息流向方向。它是指由用戶代理服務器(UAS)發出應答到用戶代理客戶端(UAC)的消息流向方向。

URL-encoded:一串根據RFC2396-2.4節編碼的字符。

User Agent Client(UAC):用戶代理客戶端。用戶代理客戶端是一個邏輯的概念,他創建一個新請求,並且用客戶事務狀態機發送這個請求。UAC角色只在事務中存在。換句話說,UAC就是一小段代碼初始化一個請求,並且在事務中遵循UAC的規則。如果它接下來收到一個請求,那麼在那個事務中,它就是作爲UAS來處理請求。

UAC Core:UAC核心。在transaction和transport層之上得UAC實現的功能集合。

User Agent Server(UAS): 用戶代理服務器.UAS是一個邏輯的實體,對SIP請求做響應的。應答接受、拒絕、或者轉發對應的請求。UAS角色在事務中存在。換句話說,是響應請求的一小段軟件,在事務中作爲UAS存在。如果他發出請求,那麼他就在事務中作爲UAC存在。

UAS Core:UAS核心。在transaction和transport層智商的UAS實現的功能集合。

User Agent(UA)。一個邏輯實體的概念,包含UAC和UAS。
UAC和UAS,就像代理服務器和轉發服務器,是在事務by事務的原理(串行事務處理)上定義的。例如,當發出一個初始化INVITE請求的時候,UA作爲UAC初始化一個呼叫動作,當從被叫方接收到一個BYE請求的時候,UA作爲UAS響應。類似的,同樣的代碼可以對一個請求做爲proxy服務器處理,對另一個請求作爲重定向服務器。

proxy,location,registrar服務器都是邏輯實體,在它們的實現中,可能是作爲單個應用實現的。

7、SIP消息:
SIP協議是一個基於文本的協議,使用UTF-8字符集(RFC2279[7])。
一個SIP消息既可以是一個從客戶端到服務器端的請求,也可以是一個從服務器端到客戶端的一個應答。
即使在字符集上和語法細節上有所不同,請求(7.1)還是應答(7.2)消息都基於RFC2822格式的。(SIP允許包頭域不是標準的RFC2822包頭域)。這兩種消息類型都由一個起始行,一個或者多個包頭域,一個可選的消息中文組成。

一般消息=                 起始行
*消息包頭
CRLF
[消息正文]
起始行=                        請求行/狀態行

起始行、每一個包頭行,空行、都必須由回車換行組成(CRLF)。即使消息中文沒有,也必須有一個空行跟隨。
除了在字符集上的區別以外,很多SIP的消息和包頭域的格式都同HTTP/1.1一樣。我們在這裏就不重複它的語法和語義了,我們用[HX.Y]來標誌HTTP/1.1規範(RFC2616[8])的X.Y節的描述。
SIP並非一個HTTP的超集或者擴展。
7.1 請求
SIP請求是根據起始行中的Request-Line來區分的。一個Request_line包含方法名字,Request-URI,用單個空格(SP)間隔開的協議版本。
Request-Line由CRLF結束。除了用作行結束標誌以外,不允許CR或者LF出現在其他地方。在其他域中,不允許出現線形的空白(liner whitespace LWS)

Request-Line        =        Method SP Request-URI SP SIP-VERSION CRLF
Method: 這個規範規定了6中方法:REGISTER用於登記聯繫信息,INVITE,ACK,CANCEL用於建立會話,BYE用於結束會話,OPTIONS用於查詢服務器負載。SIP擴展、標準RFC追加可能包含擴展的方法。
Request-URI: Request-URI是一個SIP或者SIPS URI,他們在19.1節由描述。也可以是一個通用的URI(RFC 2396[5])。它標誌了這個請求所用到的用戶或者服務的地址。Request-URI禁止包含空白字符或者控制字符,並且禁止用”<>”括上。
SIP 元素可以支持除了SIP或者SIPS之外所規定的Request-URIs。比如”tel” URI模式(RFC 2806[9])。SIP元素可以用他們自己的機制來轉換non-SIP URIs到SIP URI,SIPS URI或者其他什麼格式的URI。
SIP-Version:請求和應答消息都包含當前使用的SIP版本,這個遵循[H3.1](類似HTTP用SIP替代,用SIP/2.0替代HTTP/1.1)中關於版本的規定,版本依賴,升級版本號。一個應用,發出的SIP消息一定包含了SIP-Version “SIP/2.0”。這個SIP版本串是大小寫不銘感的,但是在實現中必須發送大寫。
不像HTTP/1.1,SIP把版本號當作一個文本串處理。在實現上,這個應該不會有區別。
7.2應答
SIP應答和SIP請求的區別在於在START-LINE中是否包含一個STATUS-LINE。一個status-line在由數字表達的status-code之前,是一個協議的版本串,每一個元素之間用一個單個SP(空格)分開。
除了最後用作結束標誌以外,CR/LF不允許出現在其他地方。
status-line        = SIP-VERSION SP STATUS-CODE SP Reasong-Phrase CRLF
Status-Code 是一個3位的數字result code,用來標誌處理請求的一個結果。Reason-Phrase是一個簡短的Status-Code的說明。Status-Code是爲了能自動處理使用的,但是Reason-Phrase是用來給用戶看得。一個客戶端並不要求一定要顯示或者解釋這個Reason-Phrase。本文檔建議輸出這個reason-phrase,實現中可以選擇其他文本,比如用請求包頭中指定的合適語言來顯示。
status-code的第一個數字表示了應答的類型。接下來兩個數字並不作分類使用。基於這個原因,任何status code在100到199的可以統稱位”1xx應答”,類似的,在200到299的可以統稱位”2xx應答”,依此類推。SIP/2.0允許6類應答:
1xx:臨時應答-請求已經接收,正在處理這個請求。
2xx:成功處理-請求已經成功接收,並且正確處理了這個請求。
3xx:重定向-還需要附加的操作才能完成這個請求,本請求轉發到其他的服務器上處理。
4xx:客戶端錯誤--請求包含錯誤的格式或者不能在這個服務器上完成。
5xx:服務器錯誤-服務器不能正確的處理這個顯然合法的請求。
6xx:全局錯誤-請求不能被任何服務器處理。
21節定義了詳細的代碼說明。
7.3 頭域
SIP頭域和HTTP頭域在語法和語義上都比較類似。特別的,SIP頭域遵循[H4.2]關於消息頭的語法的定義,並且遵循多行的擴展頭域的規則。(後者 is specified in HTTP with implicit whitespace and folding)。這個規範遵循RFC2234[10],並且把清晰的空白和封裝作爲內在的語法規則。
[H4.2]也定義了具有相同域名的多個頭域,他們的值是用逗號分開的列表,可以合併到一個頭域中。這個也適用於SIP,但是細節上略有不同,因爲語法不同。實際上,任何SIP的包頭語法都是基於如下範式的:
header = “ header-name” HCOLON header-value *(COMMA header-value)
這個允許合併在具有同一個域名的多個頭域,到一個用逗號分割的單個頭域中。Contact頭域除了當域值是”*”之外,都允許用逗號分割的列表。
7.3.1 頭域格式。


頭域遵循在RFC2822的2.2節定義的通用頭域格式。每一個頭域都由一個域名加上冒號(”:”)和域值組成。
                field-name:field-value
消息頭的正則語法在25節中有介紹介紹。
在消息頭中,允許在冒號的左右有任意個數的空白;但是,在實現中,建議避免域名和冒號中間有空格,並且建議在冒號和值之間使用單個空格(SP)。
                Subject:         lunch
                Subject    :     lunch
                Subject         :lunch
                Subject: lunch
所以,上面的都是合法的,也是相等的,但是最後一種是我們所推薦的。
頭域可以擴展成爲多行的,只要在每一個附加行前邊用至少一個SP或者水平TAB(HT)打頭就可以了。這種多行的頭域在行結尾並且在下一行之前的空白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等等)放在消息頭的最前邊,這樣可以提高處理的速度。相同域名的域之間的順序非常重要。只有當單個頭域的域值是可以用逗號分割的列表的時候,纔可以表達成爲同一個域名的多個頭域(這就是說,如果遵循7.3定義的語法)。也就是意味着必須可以將同一個域名的多個頭域在不改變消息語義的前提下,改換表達成爲一對”域名: 域值”;這個轉換是通過順序增加每一個域的域值,域值之間用逗號分割。這個規則有幾個例外,就是WWW-Authenticate,Authorization,Proxy-Authenticate,和Proxy-Authorization頭域。多個頭域行可以在消息頭中出現,但是由於他們的語法並不遵循7.3中定義的通用格式,所以,他們並不能合併成爲單個頭域行。
在實現上,必須能夠既能夠處理多個頭域行的情況,也必須能夠處理用逗號分割的合併的單個頭域行的情況。
下邊的幾組頭域是相等的:
        Route: <sip:[email protected]>
        Subject: Lunch
        Route: <sip:[email protected]>
        Route: <sip:[email protected]>
       
Route: <sip:[email protected]>, <sip:[email protected]>
        Route: <sip:[email protected]>
        Subject: Lunch

        Subject: Lunch
        Route: <sip:[email protected]>, <sip:[email protected]>
                        <sip:[email protected]>

下邊各組是合法的,但是並不相等。
Route: <sip:[email protected]>
Route: <sip:[email protected]>
Route: <sip:[email protected]>

Route: <sip:[email protected]>
Route: <sip:[email protected]>
Route: <sip:[email protected]>

Route: <sip:[email protected]>,<sip:[email protected]>,<sip:[email protected]>

每一個頭域值的格式是依賴於它的頭域名的。他可以是任意順序的TEXT-UTF8字符,也可以是一個空格,標記,分隔符,引號括起來的字串的組合。很多頭域都回附帶一個通用的域值格式。這個域值格式是由分號分開的參數名和參數值的組合:
        field-name: field-value *(;parameter-name=parameter-value)
雖然在域值裏邊可以有任意數量的parameter-name/parameter-value對,但是不能允許有相同的parameter-name存在(唯一性)。除了特別指出的頭域之外,頭域中的域名、域值、parameter name parameter-value都是大小寫不敏感的。標記詞始終是大小寫不銘感的。除非有特別的指定,引號串的字符串是大小寫敏感的。例如:
        Contact: <sip:[email protected]>;expires=3600

        CONTACT: <sip:[email protected]>; ExPiReS=3600
相同。
        Content-Disposition: session;handling=optional

        content-disposition: Session;HANDLING=OPTIONAL
相同。
下邊的兩個頭域不相同:
        Warning: 370 devnull “Choose a bigger pipe”
        Warning: 370 devnull “CHOOSE A BIGGER PIPE”

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 <sip:[email protected]>
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” <sips:[email protected]> ; tag=a48s
From: sip:+
[email protected];tag=887s
From: Anonymous <sip:[email protected]>;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:[email protected]>
From: Alice <sip:[email protected]>;tag=1928301774
Call-ID: a84b4c76e66710
Cseq : 63104 OPTIONS
Contact: <sip:[email protected]>
Accept: application/sdp
Content-Length: 0
11.2 處理OPTIONS請求
給OPTIONS的應答的構造遵循標準的構造規則(8.2.6節描述)。應答碼的選擇必須和處理INVITE請求一樣的應答碼(就像處理INVITE請求一樣的返回)。這就是說,200(OK)應當在UAS能夠接收請求的時候返回,486(忙)應當在UAS如果忙的時候返回。這樣OPTIONS可以用來檢測UAS的基本狀態,這就是說,我們可以用OPTIONS來知道UAS能否接收INVITE請求。
在一個對話中的OPTIONS請求會產生一個200(OK)的應答,這是和在對話外創建的並且對對話沒有任何影響的請求相同。
這個OPTIONS的使用有一定的限制,因爲對於proxy處理OPTIONS和INVITE請求的不同。一個分支的INVITE可以有多個200(OK)的應答返回,但是一個分支的OPTIONS只能有單個200(OK)應答返回。因爲這是由於proxy處理OPTIONS請求是當作非INVITE的處理。參見16.7節有詳細的說明。
如果OPTIONS請求的應答是由proxy服務器給出的,proxy返回一個200(OK)的應答,列出這個服務器的各種選項和能力。
應答沒有消息體
Allow,Accept,Accept-Encoding,Accept-Language,和Supported頭域應當在200(OK)應答中出現。如果這個是由proxy產生的應答,那麼Allow頭域應當忽略,因爲proxy是方法無關的(也就是說不知道該如何處理方法的)。Contact頭域可以在200(OK)的應答中出現,並且與3xx應答有相同的語義。這就是說,他們可以列出指向客戶的一串名字和方法的集合(用以轉發請求)。一個Warning頭域是可以存在的。消息體也可以存在,消息體的類型是由OPTIONS請求的Accept頭域指明的(application/sdp是缺省的,如果Accpet頭域不存在的話)。如果Accept頭域中包含了一個類型能描述媒體接收能力,UAS應當在應答中包含一個消息體用於這個用途。詳細的application/sdp包體說明在[13]中描述。
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:[email protected]>;tag=93810874
From: Alice <sip:[email protected]>;tag=1928301774
Call-ID: a84b4c76e66710
Cseq: 63104 OPTIONS
Contact: <sip:[email protected]>
Contact: mailto:[email protected]
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)
12 對話(Dialog)
一個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”對話將會終結。

12.1 創建一個對話
對話是由對一組特定請求的沒有失敗的應答來創建的。在本規範中,只有包含To tag的2xx和101-199應答,並且請求是INVITE的,會建立一個對話。當收到一個非終結應答的時候,對話會建立成”early”狀態,並且成爲early dailog。創建對話的時候可以使用Extension來定義擴展。13節描述了INVITE請求的更多細節。在這裏,我們描述與方法無關的對創建對話狀態的處理。
UA必須按照下邊描述的方法對dialog ID進行賦值。
12.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。
12.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。

12.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向後兼容問題。

12.2.1 UAC行爲
12.1.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)
12.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。
12.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。
12.3 終止對話
在建立對話中的終結對話,跟請求方法無關,如果對話外的請求產生了一個非2xx終結應答,任何前邊請求創建的”早期對話”(early dialogs)將會終止。在已經建立的對話中,終結對話就是請求方法相關的。在這個定義中,BYE方法將會終結一個對話。15節有細緻的討論。
13 初始化一個會話
13.1 概覽
當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。
13.2 UAC處理
13.2.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節)。這也就是創建一個客戶事務並且由這個客戶事務發送請求並且處理應答。
13.2.2 處理INVITE應答
當INVITE請求被傳送給INVITE的客戶事務層進行處理,UAS等待INVITE的應答。如果INVITE客戶事務層返回一個超時而不是收到一個應答,那麼這個TU就應當像收到一個408(請求超時)應答(8.1.3節)那樣進行處理。
13.2.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的集合可能會改變哦]。)
13.2.2.2 3xx應答
一個3xx應答可能包含一個或者多個Contact頭域值,這個頭域值提供了被叫方可能存在的地點。UAC可以根據3xx應答的狀態碼(21.3節)來決定是否嘗試這些新的地址。
13.2.2.3 4xx,5xx,6xx應答
在INVITE請求中,可能會收到單個非2xx終結應答。4xx,5xx,6xx應答如果包含了Contact頭域,那麼這個頭域值指示了錯誤的詳細信息的解釋地點。後續的終結應答(只有可能在發生錯誤的情況下),必須被忽略掉。
所有的早期對話都會由於接收到非2xx終結應答而結束。
一旦接收到了非2xx終結應答,UAC處理核心就認爲INVITE事務結束了。INVITE客戶事務處理生成對這個應答的ACK(參見17節)。
13.2.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請求來結束對話。
13.3 UAS處理
13.3.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節描述的步驟建立應答。
13.3.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事務是可以被延長處理時間的。
13.3.1.2 INVITE請求轉發
如果UAS決定轉發這個呼叫,就需要發出3xx的應答。300(多重選擇),301(永久轉移),302(臨時轉移)應答中應當包含一個Contact頭域,這個頭域包含了一個或者多個表明需要重試的URI新地址。這個應答交給INVITE服務端事務層,由服務端事務層負責應答的重發。
13.3.1.3 INVITE請求的拒絕
拒絕INVITE請求的常見情景是被叫方不想或者不能在終端系統上接收這個呼叫。486(用戶忙)應當在這樣的情況下返回。如果UAS知道沒有其他終端系統能夠響應這個呼叫,就應當返回一個600(Busy Everywhere)。不過,通常情況下UAS是不太會知道這個情況的,並且這個應答也是罕見的。這些應答是交給INVITE服務端的事務層進行發送的,由這個事務層來保證應答的重發機制的。如果UAS拒絕的是INVITE請求包含的媒體磋商offer,UAS應當返回一個488(Not Acceptable Here)應答。這個應答應當包含一個Warning頭域來解釋爲何offer被拒絕。
13.3.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請求來結束。
14 更改已經存在的會話
一個成功的INVITE請求(13節)既會創建一個基於兩個用戶之間的對話,也會基於請求/應答模式(offer-answer)創建一個會話。12節講述瞭如何通過target refresh 請求來修改一個現存的會話(比如,修改對話的remote target URI)。本節描述如何修改實際的會話(session)。
這個修改可以包括修改地址或者端口、增加媒體流、刪除媒體流等等。這是通過發起新的INVITE請求來完成的,並且這個新的INVITE請求是基於建立會話所相同的對話的。在一個現存對話中發出INVITE請求就是re-INVITE.
注意,單個的re-INVITE請求可以同時更改對話和會話的參數。
呼叫方或者被叫方都可以更改現存的會話。
在本協議中,UA檢測本地媒體有效性是基於自身的策略的。但是,我們並不建議自動產生re-INVITE或者BYE請求,因爲這樣可能會導致網絡上的阻塞。在任何情況下,如果某些消息將被自動發送,那麼他們應當等待一個隨機的時間間隔。
注意,上邊的這些描述是特指自動產生的BYE和re-INVITE。如果用戶由於媒體不兼容而掛機,UA應當正常發出BYE請求,而不視爲自動產生的BYE。
14.1 UAC行爲
與INVITE相同的會話描述磋商offer-answer模式(13.2.1節)在re-INVITE中也一樣採用。假設UAS希望增進一個媒體流,那麼UAC將會創建一個新的offer包含這個媒體流,並且發送INVITE請求給他的對方。特別需要注意的是,這個會話的全描述,而不是變化部分需要傳送。這個支持無狀態的會話處理,並且支持錯誤恢復機制。當然,UAC可以發送一個re-INVITE請求而不包含會話描述,在這樣的情況下,就是在這個re-INVITE的第一個可靠的非失敗的應答中將會包含這個會話描述offer(在這個規範中,就是2xx應答)。
如果會話描述格式具有版本號碼,那麼這個磋商的offer應當標誌這個變化了的媒體描述版本。
re-INVITE請求中的To,From,Call-ID,Cseq,Request-URI頭域應當和正常的在對話中的請求構造方法一樣(12節)。
在re-INVITE請求中,UAC可以選擇不增加一個Alert-Infor頭域或者具有Content-Disposition=”alert”的消息體。因爲UAS通常不會要求提示操作者來響應這個re-INVITE請求。
和INVITE不同的是,INVITE可以分支(分岔成爲多份INVITE),re-INVITE是不會分支的,所以,只會由一個單個的終結應答。re-INVITE不會分岔的原因是因爲Request-URI標誌的是建立對話的UA的目標地址,而不是用戶的address-of-record地址。
需要注意的是,在相同的對話中,UAC不能在上一個INVITE請求完成前(無論是那一方發起的INVITE)再次發起一個新的INVITE。
1、        如果有正在處理的INVITE客戶事務,TU必須等待這個事務終結或者完成,才能初始化一個新的INVITE。
2、        如果有正在處理的INVITE服務事務,TU必須等待這個事務確認或者終結,才能開始處理一個新的INVITE。
不過,UA可以在INVITE事務正在處理的同時,處理一個普通的事務。也可以在一個普通事務正在處理的同時來初始化一個INVITE事務。如果UA接收到一個針對re-INVITE的非2xx終結應答,則會話參數不能改變,應當就像沒有收到過這個re-INVITE請求一樣。注意,就像在12.2.1.2節一開始講的那樣,如果非2xx終結應答是一個481(Call/Transaction Does Not Exists),或者一個408(Request Timeout),或者完全沒有re-INVITE請求的應答(也就是說從INVITE客戶事務端返回一個超時),UAC會終止這個對話。
如果UAC收到一個re-INVITE的491應答,他應當啓動一個值爲T的時鐘,這個T的取值如下:
1、        如果UAC是這個dialog ID的Call-ID的擁有者。(也就是說是UAC產生的Call-ID),那麼T取值爲一個2.1到4秒的隨機數,單位是10毫秒。
2、        如果UAC並非是dialog ID的Call-ID的擁有者,T應當取值是0到2秒的隨機數,單位是10毫秒。
當這個時鐘到了,如果UAC還希望再次嘗試更改會話參數,UAC應當再次嘗試re-INVITE請求一次。這個意思是說,如果這個呼叫已經被BYE所掛掉了,那麼re-INVITE請求就沒有再發的必要。
發送re-INVITE請求的規則,以及針對re-INVITE請求產生的2xx應答而產生的ACK請求的發送規則,等同於初始的INVITE請求(13.2.1節)。
14.2 UAS行爲
13.3.1節描述了區分初始的INVITE請求和re-INVITE請求的方法,以及處理對現存的對話中處理re-INVITE請求的步驟。
UAS在發送第一個INVITE的終結應答之前,收到第二個INVITE請求,並且這個請求的Cseq序列號大於第一個INVITE請求,那麼就應當給第二個INVITE請求返回一個500(服務器內部錯誤)應答,並且必須包含一個Retry-After頭域,這個頭域中應當包含一個0-10秒的隨機數。
如果UAS正在處理一個INVITE請求的時候又收到了一個在同一個對話上的INVITE請求必須返回一個491(Request Pending)應答給接收到的INVITE。
如果UA接收到一個對現存的對話的re-INVITE請求,那麼就必須檢查有關會話描述(session description)的版本標誌(version identifiers),或者,如果沒有版本標誌,那麼就需要檢查會話描述的正文看看有沒有變化。如果會話描述改變了,UAS必須由此調整會話參數,在調整參數的時候可能會要求用戶確認。

會話描述的版本可以用來提供給會議的新近加入者,增加或者刪除媒體,或者由單點會議更改成爲多方會議。
如果新的會話描述是不能被UA接受的,UAS可以用返回一個488(Not Acceptable Here)來拒絕這個re-INVITE請求。這個響應應當包含一個Warning頭域(用來提供給請求方,提供這個拒絕的原因)。如果UAS產生一個2xx應答,但是沒有收到ACK,它應當產生一個BYE來結束這個對話。
UAS可以不產生180(Ringing)應答給re-INVITE,因爲UAC一般不展示這個信息給用戶。同樣的,UAS可以增加一個Alert-Info頭域或者一個由Content-Disposition“alert”的消息體來應答給re-INVITE來讓UAC展示這個信息。
如果UAS在2xx應答中提供了一個媒體磋商offer(因爲INVITE沒有包含一個offer),那麼它應當當作新呼叫一樣的構造這個offer,就像在[13]中SDP描述一樣,遵循發送更改現有的會話的offer的限制。特別需要注意的是,這個意味着它應當包含全部的UA支持的媒體類型和媒體格式。UAS必須確保會話描述在媒體格式,通訊方式,或者其他要求對方支持的參數上,新的會話描述和舊的會話描述一樣。這個可以避免對方拒絕這個會話描述。如果,UAC任然是不能接受這個會話描述,UAC應當產生一個它能夠接受的會話描述應答,並且發送一個BYE來結束這個會話。

15 結束一個會話
本節描述了結束由SIP建立的會話的步驟。會話的狀態和對話的狀態是密切相關的。當一個會話由INVITE建立的時候,每一個由不同UAS的1xx或者2xx的應答創建一個對話,並且當完成了會話描述的請求/應答(offer/answer)交互之後,它也就創建了一個會話。這就是說,每一個會話都和單個對話”相關”-會話是對話所創建的。如果初始化的INVITE產生了非2xx的終結應答,它也終結了由本次請求創建的任何會話(如果有的話),並且終結了所有的本次請求創建的對話(如果有的話)。由於事務完整性的保證,一個非2xx的終結應答同樣也防止了本次INVITE以後可能創建的會話。BYE請求用於終結指定的會話或者嘗試建立的會話。在這裏,特定的會話是一個和與之相對的對話的對方UA。當在對話中接收到了一個BYE,任何與該對話相關的會話都應當終止。UA禁止在對話外發送BYE請求。請求方UA可以在已經建立好的對話或者早期對話中發起BYE請求;被叫方只能在建立好的對話中發起BYE請求,不能在早期對話中發起BYE請求。
不過,在一個建立好的對話中,被叫方的UA不能在接收到對應2xx應答的ACK請求前發送BYE請求,或者不能在服務器事務超時前發送BYE請求。如果沒有SIP擴展定義了和這個對話相關的其他應用層狀態,這個BYE請求同樣結束了對話。

在對話和會話中,給INVITE的非2xx的終結應答,使得使用CANCEL比較有吸引力。CANCEL是嘗試強制給INVITE請求一個非2xx應答(比如,487應答)。因此,如果UAS希望放棄整個呼叫,它可以發送一個CANCEL。如果INVITE會有2xx終結應答,這個意味着UAS在CANCEL正在處理的時候,接收到一個邀請。UAC可以繼續用這個2xx應答建立會話,也可以用BYE終結這個會話。(這個意思是說,一般情況下,如果UAC希望cancel 這個INVITE請求,那麼就會發出CANCEL請求,如果接收到了非2xx的終結應答,就意味着CANCEL掉了,但是如果接收到的還是2xx應答,就說明沒有CANCEL掉,沒有CANCEL掉呢,就可以選擇繼續建立會話,或者說發送一個BYE來終結會話)

在SIP中,並沒有一個很好的”hangin up”(掛機中)定義。它屬於一個用戶界面的普通常見的細節。通常,當用戶掛機,它意味着結束建立會話的嘗試,並且終止所有已經建立的會話。對於呼叫方的UA來說,如果沒有收到初始INVITE請求的終結應答,這個可能是產生對初始INVITE請求的一個CANCEL請求,並且收到終結應答之後給每一個建立好的對話發出一個BYE。對於被叫方的UA,就是很普通的BYE;粗略來說,當用戶(因爲響應振鈴)摘機,就會產生一個2xx應答,於是掛機會在收到ACK請求之後發送一個BYE。這不是說在收到ACK之前用戶不能掛機,這只是表達在用戶的電話中的軟件,需要保持一小會兒狀態,來正確釋放狀態。如果某個UI(用戶界面)允許用戶在不摘機的情況下拒絕呼叫,可以用403(Forbidden)來作爲INVITE的應答,在這樣的情況下,BYE就不能發送。
15.1 使用BYE請求終止一個會話
15.1.1 UAC行爲
BYE請求就像其他在對話內的請求一樣的構造,參見12節的描述。
當BYE請求創建好了之後,UAC核心處理部分創建一個新的非-INVITE客戶端事務,並且用它來處理BYE請求。UAC必須認爲當BYE請求一發送到客戶端事務,會話就結束了(因此也就停止發送或者接收媒體流)。如果BYE請求的應答是481(Call/Transaction Does Not Exists)或者408(Request Timeout)或者BYE請求壓根沒有應答(就是說客戶端事務返回一個超時),UAC必須認爲會話和對話都已經結束。
15.1.2 UAS行爲
UAS首先按照通用的UAS接收到請求的處理步驟進行BYE請求的處理(8.2節)。UAS核心處理部分接收到BYE請求以後,首先檢查它是否和現存的對話匹配。如果BYE並不匹配現存的任何一個對話,那麼UAS應當產生一個481(Call / Transaction Does Not Exists )應答,並且傳送給服務器事務。
這個規則意味着如果UAC發送沒有帶tag標誌的BYE請求會被拒絕。這個是一個對RFC2543的改動,RFC2543允許BYE不帶tag標誌。
UAS核心處理部分接收到BYE請求,並且發現和現存的對話匹配,那麼它必須遵循12.2.2的步驟來處理請求。一旦處理完成,UAS應當終止這個會話(因此停止發送和接收媒體流)。唯一一個可以選擇不終止的情況是多方會話,在多方會話中參與者允許對話中的其他參與方終止他們自己的會話。不管是否終止會話中的參與方,UAS核心處理都必須給BYE產生2xx的應答,並且必須由服務器的通訊層進行傳輸。
UAS必須依舊響應在這個對話中接收到的未決的請求。我們建議用487(請求終止)來給這些未決的請求以應答。
16 proxy行爲
16.1 概述
SIP代理服務器是路由SIP請求到UAS的,並且路由SIP應答到UAC的。一個請求可能通過多個proxy到達UAS。每一個都會作出路由決定,在發送給下一個節點前對請求做一點修改。應答會通過和請求相同的proxy路徑,只是順序是逆序的。
proxy是一個SIP邏輯上的概念。當接收到一個請求,在做代理服務器之前,首先應該有一個部件來決定是否自身需要響應這個請求。例如,在作爲代理服務器處理請求之前,首先判定請求可能是非法的或者請求需要一個信任狀。這個元素可以使用任何合適的錯誤代碼來響應這個請求。當直接應答請求的時候,這個元素(proxy)將作爲UAS角色,並且必須遵循8.2節描述的UAS行爲規範。
proxy對於每一個新的請求來說,既可以作爲有狀態的也可以作爲無狀態的模式來處理。當作爲無狀態的處理模式的時候,proxy就是簡單的轉發。它轉發每一個請求下行到一個由請求所決定的目的地。並且簡單轉發從上行流取得的應答。一個無狀態的proxy在處理完一個消息之後就會丟棄這個消息的相關資料。有狀態的proxy會保留這些信息(尤其是事務信息),保留每一個接收的請求和每一個接收請求的應答的相關信息。它保留這些信息用於處理與這個請求相關的後續消息。一個有狀態的proxy可以選擇”分支”一個請求,路由它到多個地點。任何被路由到多個地點的請求都必須當作有狀態的處理。在某些情況下,proxy可以用有狀態的通訊協議(比如TCP)來轉發請求,而不用自身成爲事務有狀態的。例如,proxy可以簡單轉發請求從一個TCP連接到另外一個TCP連接,而不用自身作爲有狀態的,只要它放置足夠的信息在需要轉發的消息裏,使得能夠正確把應答發送到接收到對應請求的連接發送出去。如果在不同傳輸通訊協議之間進行請求的轉發,那麼就必須要求proxy的TU採用某種手段來保證可靠的從一個協議有狀態的轉到另一個協議。
有狀態的proxy可以在處理請求中的任何時候轉換成爲無狀態的,只要它不作任何可能導致不能無狀態的操作(比如分支,比如產生100應答)。當做這樣的轉換的時候,所有的狀態就只是簡單的廢棄掉。proxy不應當發起一個CANCEL請求。
在作爲無狀態的或者有狀態的時候,處理請求的步驟是一樣複雜的。接下來的步驟是從有狀態的proxy角度來些的。最後一節是講述無狀態proxy的區別。
16.2 有狀態的proxy
作爲有狀態的proxy,它必須是一個純粹的SIP事務處理引擎。它在這裏的定義遵循17節講述的服務端和客戶端的事務處理規定。有狀態的proxy有一個服務端事務,這個事務與一個或者多個客戶端事務相關,這些客戶端事務是由高層proxy處理元素產生的(圖3),這些高層proxy處理元素就是proxy處理核心。一個輸入的請求是通過一個服務端事務來處理的。請求由服務端事務交給proxy處理核心進行處理。proxy處理核心檢查請求應當路由到哪裏,選擇一個或者多個下一個節點。每一個發往下一個節點的外發請求都由客戶端事務進行處理。proxy處理核心從客戶端事務中得到請求的應答並且把他們的應答交給服務端事務進行發送。
有狀態的proxy爲每一個接收到的新的請求創建一個服務端事務。任何請求的重複都是由這個服務端事務來處理(參見17節)。proxy處理核心必須遵循UAS的模式,發送一個直接臨時應答(比如100 trying)到這個服務端事務上(8.2.6節)。因此,有狀態的proxy不應當給非INVITE請求產生100(trying)應答。
這是一個proxy的模型,並非軟件實現。在實現上可以擴展並且複用用這個模型定義。
對於所有的新請求來說,包括那些未知方法的請求,proxy處理請求必須:
1、        驗證請求(16.3)
2、        預處理路由信息(16.4)
3、        決定請求的目的(targets)(16.5)


+--------------------+
|                                                | +---+
|                                                 | | C |
|                                                | | T |
|                                                | +---+
+---+        |                         Proxy                | +---+ CT = Client Transaction
| S |         |         "Higher" Layer         | | C |
| T |         |                                                 | | T | ST = Server Transaction
+---+         |                                                 | +---+
|                                                 | +---+
|                                                 | | C |
|                                                 | | T |
|                                                 | +---+
+--------------------+
Figure 3: Stateful Proxy Model
4、        轉發請求到每一個目的地(16.6)
5、        處理所有的應答(16.7)

16.3 驗證請求
在proxy轉發請求之前,它必須檢查消息的合法性。一個合法的消息必須經過如下的檢查:
1、        合法的語法
2、        URI scheme
3、        最大轉發次數
4、        (可選)循環檢測(loop detection)
5、        proxy-require
6、        proxy-authorization
如果任何一步失敗了,proxy都必須作爲UAS(8.2)一樣,應答一個錯誤碼。
注意proxy並不要求檢查合併的請求,並且不能把合併的請求當作一個錯誤的情況。終端接收到合併的請求會根據8.2.2.2節講述的內容進行分解。
1、        合法的語法。
請求要能夠被服務端事務處理,那麼請求就必須是語法無誤的。請求中的任何與檢查相關的部分或者與請求轉發節相關的部分都必須語法嚴格無誤。在檢查中,其他部分的嚴格與否,在檢查中都被忽略,並且在轉發消息過程中保持不變。例如,proxy不會由於非法的Date頭域而拒絕請求。同樣的proxy不會在轉發請求的時候移去非法的Date頭域。這個是爲了設計成爲可擴展的。以後的擴展可能定義新的方法、新的頭域。proxy不能拒絕由於包含了不認識的方法或者不認識的頭域而拒絕轉發這個請求。
2、        URI scheme檢查
如果Request-URI包含一個proxy所不能理解的URI形式,那麼proxy應當通過返回一個416來拒絕這個請求(Unsupported URI scheme)。
3、        最大轉發次數檢查
最大轉發次數Max-Forwards頭域(20.22)是用來限制轉發的次數的。如果請求沒有包含Max-Forwards頭域,那麼這個檢查將被忽略。如果請求包含了一個Max-Forwards頭域,並且這個頭域大於0,那麼這個檢查就跳過。如果請求包含一個Max-Forwards頭域,並且這個頭域爲0,那麼這個proxy不能轉發這個請求。如果請求是OPTIONS請求,那麼proxy可以作爲最終響應者來響應這個請求,並且遵循11節講述的產生應答。否則proxy應當返回一個483(too many hops)應答。
4、        可選的Loop Detection檢查
proxy可以檢查看看轉發是否可能導致循環。如果請求包含一個Via頭域,並且這個頭域值,和proxy早先保留的請求的頭域值相同,那麼這個請求就是以前經過過本proxy。要麼這個請求就是循環處理了,要麼就是合法的循環處理。要檢測請求是否循環處理,proxy可以用branch參數,根據16.6節的第8步來計算,並且和接收到的Via頭域做比較。如果參數相等,那麼請求就循環了。如果不等,那麼請求就是合理的經過,並且繼續處理。如果檢測到了循環,那麼proxy應當返回一個482(LoopDetechted)應答。
5、        Proxy-Require檢查
本協議的以後的擴展可能會要求額外的proxy特性。所以終端會在請求中包含一個Proxy-Require頭域來表明會使用到那些特性,這樣proxy就可以根據Proxy-Require判定自己是否能夠支持這些特性。
如果請求包含一個Proxy-Require頭域(20.29)並且有一個或者多個本proxy不能理解的option-tags。那麼這個proxy必須返回一個420(Bad Extension)錯誤,並且這個錯誤應答必須包括一個Unsupported(20.40)頭域列明瞭那些option-tags這個proxy不能支持。
6、        Proxy-Authorization 檢查
如果proxy要求在轉發請求之前進行身份認證,那麼必須根據22.3節中描述的那樣進行請求的檢查。22.3節也定義了proxy應當怎樣處理檢查失敗的情況。
16.4 路由信息預處理
proxy必須檢查請求中的Request-URI部分。如果Request-URI包含了一個本proxy早先放在Record-Route頭域中的值(參見16.6,4),proxy必須用Route頭域中的最後一個值來替換Request-URI,並且從Route頭域中刪去這個值。proxy必須接着按照個修改後的請求進行處理。
這個只會在某元素髮送請求到proxy(proxy本身可能是一個終端),並且這個請求是基於嚴格路由的。在接收到請求後重寫這個字段是必須的,因爲要保持向後兼容性。同時也允許按照本規範實現的元素保護Request-URI通過嚴格路由的proxy(12.2.1.1節)。
這個要求並沒有強制proxy保留狀態來檢查其早先放在Record-Route頭域中的URI。作爲替換的方法,proxy只需要保留足夠的信息在那些URI裏邊,這樣,在以後出現的時候就能識別了。
如果Request-URI包含了maddr參數,proxy必須檢查這個參數來看看是否在proxy配置的可信任的地址列表或者可信任的區域列表中。如果Request-URI包含一個maddr參數,並且這個參數包含了一個proxy可以信任的地址,並且這個請求是通過Request-URI中(指明或者缺省)的端口和協議接收到的,proxy必須抽掉maddr和其他非缺省的端口和通訊參數,並且繼續處理。

proxy可能收到一個帶有匹配maddr的請求,但是不是在URI中指出的端口和transport接收到的。這個請求需要通過指明的port和transport轉發到對應的proxy。

如果Route頭域的第一個值就是這個proxy,那麼proxy必須從請求中把它移去。

16.5 確定請求的目的
接着,proxy計算請求的目的(或者多個目的地)。目的地集合可以由請求的內容決定或者由絕對位置服務提供。目的地集合中的每一個目的地都由URI來表達。

如果請求中的Request-URI包含了maddr參數,必須把Request-URI放在目標集合中,並且是作爲唯一一個目標URI,並且proxy必須按照16.6節中的約定進行處理。

如果Request-URI的區域並非本proxy負責的區域,那麼Request-URI必須放在目標集合中,並且作爲唯一一個目標URI,並且proxy必須按照16.6節中的約定進行處理。

有很多種情況都會導致proxy收到並非本proxy負責的區域的請求。一個防火牆proxy處理外發的請求(就像HTTPproxy處理外發的請求)就是一個典型的例子。

如果請求的目標集合沒有像上邊講述的這樣預先設定,那麼這就意味着proxy是負責Request-URI所指明的區域的,並且proxy可以用任何機制來決定往哪裏發送這個請求。這些機制都可以歸結成爲訪問一個絕對位置服務的形式。這個可能由從SIP註冊服務器創建的位置服務器獲得信息、讀取數據庫、查閱服務器、利用其他協議、或者就簡單的替代Request-URI來實現。當訪問由註冊服務器創建的位置服務的時候,在作爲索引查詢之前,Request-URI必須首先根據10.3節進行規範處理。這些機制的輸出結果將作爲目的地集合。
如果Request-URI沒有提供足夠的信息來讓proxy能夠產生目的地集合,它應當返回一個485(Ambiguous)應答。這個應答應當包含一個Contact頭域包含一些應當嘗試的新位置。比如,一個到sip:[email protected]的INVITE可能在某一個proxy是不明確的,因爲這個proxy有多個JohnSmith。21.4.23節有細節描述。

任何與這個請求有關的,或者與proxy當前環境有關的信息都可以用來構造目的地集合。例如,由於請求的內容不同或者包頭域的不同,可以有不同的目的地集合,又或者不同時間到達的請求也可以有不同的目的地集合,或者不同的時間間隔,上一次失敗的請求,甚至是當前proxy的利用率都可以導致目的地集合的不同。

通過這些機制,我們可以有一個可能的目的地列表,他們的URI被增加到目的地集合。每一個目的地只能在目的地列表中出現一次。如果目的URI已經在這個集合中存在了(基於URI類型的相等定義),那麼它不能再次增加。

如果原請求的Reuqest-URI指明的區域並非本proxy所負責的區域,那麼本proxy不能增加任何額外的目的地到目的地集合。

如果proxy負責Request-URI所指明的區域,那麼這個proxy只可以在轉發的時候改變請求的Request-URI。如果proxy並非負責這個URI,那麼它不會在3xx或者416應答的時候查生遞歸。

如果原始請求的Request-URI是屬於本proxy負責的區域的,那麼proxy可以在請求轉發的時候增加目的地。他可以在處理過程中,用任何可以獲得的信息來決定新的目的地。 例如,proxy可以選擇把一個轉發應答(3xx)所包含的聯繫地址合併到目的地集合中。如果proxy使用一個動態的信息源來構造目的地集合(例如訪問SIP的註冊服務器),它應當在處理請求的過程中監測這個信息源。當有新的目的地出現的時候,就應當加到這個目的地集合裏邊。就像上邊說得,每一個URI只能在集合中出現1次。

只能出現1次的原因是可以降低網絡衝突,在合併重定向請求的聯繫地址的情況下可以防止無限遞歸的出現。

舉例來說,一個簡單的位置服務是一個”no-op”(無操作的),返回的目的URI就是輸入的請求URI。請求將送到一個特定的下一個節點proxy。在16.6節/6小節定義請求轉發中,通過SIP或者SIPS URI表達的,下一個節點的身份, 將在Route的頭域最上一層插入。
如果Request-URI是這個proxy所負責的,但是在本proxy中找不到,那麼proxy必須返回404(Not Found)應答。

如果目的集合經過上邊的處理依舊是空的,那麼proxy必須返回一個錯誤應答,這個錯誤應答應當是408(暫時不可用)。

16.6 請求轉發
當目的地集合不是空的時候,proxy可以開始轉發這個請求。有狀態的proxy可以按照任意的順序處理這個目的地集合。它可以順序處理多個目的地,上一個完成前下一個不能開始。也可以採用並行的處理多個目的地。也可以通過分組的形式,每組之間是串行的,組內是並行的。

通常的處理順序機制是使用一個Contact頭域的qvalue參數來處理(20.10節)。目的地從最高的qvalue開始處理到最低的qvalue。相同qvalue的目的地可以並行處理。

有狀態的proxy必須包含針對目的地集合的一個接收到應答和轉發出去的原始請求進行匹配的機制。爲了完成這樣的目的,這個機制是一個由proxy層在轉發第一個請求前創建的”response context”(應答上下文)來保障的。

對於每一個目的地,proxy轉發請求都遵循下列步驟:
1、        拷貝一個接收到的請求
2、        更新Request-URI
3、        更新Max-Forwards頭域
4、        可選增加一個Record-Route頭域
5、        可選增加附加的頭域
6、        路由信息後處理
7、        決定下一個節點地址、端口、通訊協議。
8、        增加一個Via頭域值
9、        如果需要,增加一個Content-Length頭域
10、        轉發這個新的請求
11、        設置定時器C
下面詳細介紹每一步。

1、        拷貝請求
proxy首先把接收到的請求做一個拷貝。拷貝必須包含接收到的請求的全部頭域。在接下來的處理步驟中未提及的頭域不能刪除。拷貝應當保留接收到的請求的頭域的順序。proxy不能用合併的域名來進行域值的重新排序(參見7.3.1)。proxy不能增加、修改、刪除消息體。
實際上,在實現中並非只是做一個拷貝;首要的事情是爲每一個下一個節點準備一個相同的請求。

2、        Request-URI
在拷貝好的請求中的Request-URI必須用目的地的URI進行替換。如果這個目的URI包含任何在Request-URI中所不能允許的參數,那麼這些參數必須被刪去。
這個步驟是proxy的本質步驟。proxy通過這個機制來把請求轉發到目的地。在某些情況下,接收到的Request-URI會不作更改的添加到目的地集合中。對於這樣的目的地來說,上邊講的替換就等於是沒有任何操作。

3、        Max-Forwards
如果拷貝的頭域包含一個Max-Forwards,proxy必須把這個域值減一。
如果拷貝的頭域沒有包含一個Max-Forwards頭域,proxy必須自己增加一個頭域,缺省值是70。現在有一些UA不會在請求中填寫Max-Forwards頭域。

4、        Record-Route
(假設proxy接收到的這個請求會創建一個對話的情況下),如果希望保留這個請求創建的對話中,後續的請求依舊是要經過本proxy,那麼本proxy必須增加一個Record-Route頭域值在這個拷貝中,並且增加的這個頭域值應當是在其他現存的Record-Route頭域之前。通過請求建立的對話可以包含一個預置的Route頭域。
如果這個請求已經是一個對話的一部分,proxy如果希望以後這個對話的請求依舊經過本proxy,那麼proxy應當增加一個Record-Route頭域值。在12節描述的普通的終端操作中,這些Record-Route頭域值不會對終端使用的路由集合造成任何影響。

如果請求本身已經在對話中的話,如果proxy不增加一個Record-Route頭域在請求的包頭,後續的請求也會經過本proxy。但是,如果當終端中斷並且重新構造這個對話的時候,本proxy就會從對話所經過的節點中刪去。

一個proxy可以在任何請求中增加這個Record-Route頭域值。如果請求並沒有初始化一個對話,終端將會忽略這個頭域值。12節講述了終端如何使用Record-Route頭域來構造Route頭域的。

在請求路徑上的每一個proxy都是獨立的決定是否增加一個Record-Route頭域值的-在請求的Record-Route頭域上的值並不影響這個proxy決定增加還是不增加Record-Route頭域值。

在Record-Route頭域中防止的URI必須是SIP或者SIPS URI。這個URI必須包含一個lr參數(參見19.1.1)。這個URI可以和請求將被轉發的地方不同。這個URI不應當包含通訊參數,除非該proxy確認在後續請求將會經過的下行節點中,都支持這個通訊參數(比如本地網絡等等)。

本proxy提供的這個URI可能會讓其他元素(其他proxy)作出路由決定。本proxy,通常,並不知道其他節點的處理能力,所以,它必須嚴格律己,讓自己遵循規範的SIP實現:SIP URI和TCP或者UDP通訊協議。

在Record-Route中的URI必須指向插入它的元素(或者替代元素),這個意思是說通過附件[4]的服務器定位步驟可以順利找到這個元素,這樣後續的請求才能順利到達同一個SIP元素。如果Request-URI包含一個SIPS URI,或者Route頭域的最上的值(經過後續第6步的處理)包含一個SIPS URI,那麼插入Record-Route頭域的值必須是一個SIPS URI。而且,如果請求不是基於TLS接受的,那麼proxy必須增加一個Record-Route頭域。在相似的情況下,proxy如果從TLS上接收的請求,但是產生的是一個在Record-Route中或者Route頭域最上值中沒有SIPS URI的請求(在第6步後處理之後),必須在Record-Route頭域中增加一個非SIPS URI。

在安全範疇內的proxy必須在對話中保持這個安全範疇。

當Record-Route頭域的URI在應答中又重新到達的時候,如果這個URI值需要重寫的時候,這個URI必須是能夠唯一確定的URI。(就是說,請求可能會經過這個proxy好幾次,造成一個或者多個Record-Route頭域值的增加)。16.7節的第8步提供了一個能夠讓這個URI唯一的一個機制。

這個proxy可以在Record-Route頭域中增加一些參數。這些參數在某些請求的應答中會被反射(echo)回來,比如給INVITE請求的200(OK)應答。通過在消息的參數中保持狀態比在proxy中保持狀態更加有效。

如果proxy想在任何類型的對話中都保持在請求的路徑上(比如在跨越防火牆的對話中),它需要給每一個接收到的請求中,都增加Record-Route頭域,即使是它所不能理解的方法的請求也要增加,因爲這些方法可能是對話相關的,具有對話語義的方法。

在Record-Route頭域中增加的URI只是在當這個請求創建對話的時候有效。舉一個例子,對於一個對話-有狀態的proxy(dialog-stateful proxy),當在對話結束後,如果再收到一個請求,這個請求的Request-URI的值中包含這個URI,那麼它就可以選擇拒絕這個請求。一個對話狀態無關的proxy,當然,沒有對話結束的概念,但是他們可以再這個值中填寫足夠多的信息,這樣就可以在以後的請求來到的時候做對話的ID的比較,並且可以選擇拒絕不匹配這個信息的請求。終端不能在對話外使用這個對話中的Record-Route頭域的URI。參見12節描述的終端使用Record-Route頭域的細節。

當proxy需要查看所有對話中的消息的時候,我們就需要Record-routeing。但是,他會降低處理性能和影響擴展性,因此proxy應當只在特定情況下使用record-route。任何初始化一個對話的SIP請求都可以適用Record-Route。在本文檔中,只有INVITE請求是可以適用的,以後的擴展文檔可能包含其他的方法。

5、        增加附加的頭域
在這一步,proxy可能增加其他適當的頭域。

6、        處理路由信息
proxy可以有一個本地的策略,這個策略要求請求在傳遞到目的地之前,必須經歷一個proxy集合。這樣的proxy必須能夠確保所有的類似的proxy都是鬆路由(loose routers)的。通常,只有當這些proxy都是在相同的區域管理的時候,我們纔可能知道這些proxy是否都是鬆路由的。這個proxy的集合是通過一組URI的集合表示(每一個都包含一個lr參數)。這個集合必須被放置到Route頭域中,並且放置在其他頭域值之前。如果Route頭域不存在,必須增加一個Route頭域,包含這組URI的列表。
如果proxy有一個本地策略要求請求經過一個指定的proxy,在壓棧Route頭域之外的一個方法就是旁路下邊的第10步的邏輯轉發,而是直接發送這個請求到這個指定的proxy的地址,端口,和協議。如果請求有一個Route頭域,這個額外的方法就不能用了,除非它知道下一個節點proxy是一個鬆路由的節點。否則,使用上邊講的增加Route頭域的方法會更有效,更靈活,適應性更好,並且操作更一致。而且,如果Request-URI包含了一個SIPS URI,這個proxy必須用TLS來進行通訊。

如果請求的拷貝中包含了Route頭域,這個proxy必須檢查這個Route頭域的第一個值。如果這個URI並沒有包含lr參數,那麼proxy必須根據下列步驟修改這個請求:

- proxy必須把Request-URI放在Route頭域中的最後一個值。
- proxy必須把第一個Route頭域的值放在Request-URI中,並且從Route頭域中刪去。

把Request-URI添加到Route頭域的最後是爲了讓Request-URI的信息能夠通過嚴格路由的proxy。”Popping”彈掉第一個Route頭域值到Request-URI中是爲了能夠讓嚴格路由元素能夠接收到這個請求(並且用它自己的在Request-URI中的URI和在Route頂部下一個節點的URI)。

7、        確定下一個節點的地址,端口和通訊協議。
proxy可以有自己的策略來決定發送請求到特定的IP地址,端口和transport,可以和Route的值或者Request-URI的值無關。當本proxy不能確定對應ip,端口,transport的服務器是一個鬆路由(loose router)的時候,這樣的策略就不能使用了。但是,除了Route頭域應當像上邊講的這樣使用,我們並不推薦這樣的發送請求的機制。

在沒有這樣一個替代機制的時候,proxy應用附件[4]的步驟來決定應當向哪裏發送這個請求。如果proxy重新規格化請求,並且發送到一個像上邊6點講的嚴格路由的元素,proxy必須應用這些步驟到請求中的Request-URI。否則,如果Route頭域存在,proxy必須應用這些步驟到Route頭域的第一個值;如果Route不存在,proxy必須應用這些步驟到Request-URI。這些步驟最終得到一個序列集合(地址,port,transport)。與使用那個URI作爲附件[4]處理的輸入,如果Request-URI指定了一個SIPS URI,那麼proxy必須把輸入[4]的URI當作是SIPS URI然後遵循[4]的處理步驟進行處理。

就像在附件[4]中講述的,proxy必須嘗試序列集合中的第一組元素,並且依次嘗試序列集合中的每一組元素,直到成功爲止。

對於每一組的嘗試,proxy必須按照這組的通訊要求,對消息進行適當的格式化,並且用一個新的客戶端事務(第8到第10點講述的),進行請求的發送。

由於每一組的發送都是使用心得客戶端事務,這就體現了一個新的分支。因而,第8步插入的Via頭域中的分支參數必須每組發送的都不一樣。

如果客戶端事務報告發送請求失敗,或者它自身的狀態機超時,proxy就應當繼續處理序列集合中的下一組元素。當遍歷完序列集合之後,請求就不能發送到目的地集合。proxy不需要在應答上下文中放什麼應答,然而在別的方面卻需要就像從目的地集合收到一個408(Request Timeout)終結響應一樣的操作。

8、        增加一個Via頭域值
proxy必須在請求的拷貝中增加一個Via頭域值,並且在其他Via頭域值之前增加。這個值的構造可以參見8.1.1.7。這意味着proxy需要計算自己的分支參數,並且應當是全局唯一的分支,並且包含必要的magic cookie。注意這意味着如果請求循環經過本proxy的時候(也就是數次經過同一個proxy),每次的分支參數都不同。

在proxy構造分支參數的值上,有一個附加的約束,用來進行循環的檢測。一個要檢測循環的proxy應當創建一個由兩部分組成的分支參數。第一部分必須滿足8.1.1.7的約束。第二部分是用來做循環檢測的,並且是從螺旋中判定是否存在循環(請求數次經過同一個proxy是正常的,這是螺旋,但是如果是循環,那就不正常了。假定proxy是X,CX,XY,YZ,ZX,XA,A目的地是正常的,但是如果CX, XY, YZ, ZX, XY, 這就是循環了)。

循環檢測是通過這樣的方法檢測的:當請求返回給一個proxy,與處理請求相關的字段並未改變,那麼這個就是循環了。這個分支參數的後一部分應當反應所有的這些頭域(包括所有的Route,Proxy-Require和Proxy-Authorization頭域)。這是確保如果請求從別處重新路由回來,而且這些字段改變了,那麼這就是一個螺旋而不是循環(參見16.3)。通常建立這個比較值的方法是計算一個hash值,通過基於To tag,From tag,Call-ID頭域,收到請求的Request-URI(而不是經過處理過後的Request-URI),Via頭域的最上一個,Cseq頭域的序列號,任何附加的Proxy-Require或者Proxy-Authorization頭域。具體的hash算法是基於實現相關的,但是MD5(RFC1321[35]),用16進製表達,是一個有道理的選擇。(基於64位表達的是不太合適的)。

如果proxy希望檢測循環,那麼”branch”參數必須用包含可能影響處理請求的全部信息構成,包括輸入的Request-URI和其他可能會影響proxy處理路由的字段,通過計算得到。這是檢測循環所必須的,因爲如果請求在路由相關的字段改變以後,重新發回這個服務器,那麼新的處理可能會發送到另外的地方,而不是造成一個循環。

在branch參數的計算上,請求的方法不能計算進去。但是作爲特例,CANCEL和ACK請求(給非2xx應答的)必須和他們對應的請求有相同的branch值。branch參數用於在服務器處理這些請求的時候體現請求之間的相關性(17.2.3和9.2)

9、        如果需要,增加一個Content-Length頭域
如果請求會通過一個基於流的通訊協議發送到下一個節點,並且發送的請求拷貝中沒有包含一個Content-Length頭域,那麼proxy必須增加一個正確的請求包體大小在這個頭域(20.14)。

10、        轉發請求
一個有狀態的proxy必須爲這個請求創建一個新的客戶端事務(根據17.1節描述的那樣),並且指示事務層用第7步指定的地址,端口和協議進行發送。

11、        設定時鐘C
爲了能夠處理INVITE請求沒有產生終結應答的情況,TU使用一個定時器(稱作定時器C)。當INVITE請求被轉發的時候,必須爲客戶端事務設定一個定時器C。這個定時器C必須大於3分鐘。16.7節的2步講述了這個定時器是如何根據臨時應答來更新的,並且16.8節講述了定時器到時的處理步驟。

16.7 應答的處理
當proxy收到一個應答的時候,它首先嚐試定位一個與這個應答匹配的客戶端事務(17.1.3)。如果沒有匹配,proxy必須作爲無狀態的proxy來處理這個應答(即使這個應答是信息性質的應答)。如果與應答匹配的客戶端事務找到了,那麼這個應答將轉給這個客戶端事務進行處理。

將應答轉給對應的客戶端事務(或者更通常的說法是發出請求的或者相關的事務),並不是爲了更強大的處理能力,它是保證了”晚到”的給INVITE請求的2xx應答能夠正確的轉發。

當客戶端事務把應答交給proxy層,將會執行下列步驟:
1、        尋找適當的應答上下文。
2、        用臨時應答來更新定時器C
3、        從最上邊移除Via
4、        在應答上下文中增加應答
5、        檢查這個應答是否需要立刻發送
6、        如果需要,從應答上下文中選擇最好的終結應答。

如果在與這個應答上下文相關的每一個客戶端事務都結束的以後,還是沒有終結應答轉發,那麼proxy必須選擇從已經收到的應答中,選擇轉發”best”應答回去。

下列步驟必須在每一個被轉發的應答上執行。就像每一個請求有超過一個應答被轉發一樣:至少有一個終結應答和0個或者多個臨時應答。

7、        需要合併認證頭域值。
8、        可選的重寫Record-Route頭域值
9、        轉發應答
10、        產生合適的CANCEL請求。

上述每一步在下邊有詳細的描述:

1、        尋找上下文
proxy通過16.6節定義的方法來在尋找轉發原始請求前創建的”應答上下文” 。在這個上下文中進行後續的處理步驟。

2、        爲臨時應答更新定時器C
對於INVITE事務,如果應答是一個返回碼是101到199的臨時應答(就是說,除了100的臨時應答),proxy必須給這個客戶端事務重新設置定時器C。這個定時器可以設置成爲其他的值,和原始值不一樣,但是這個數字必須大於3分鐘。

3、        Via
proxy從應答中移去Via頭域中最上的值。

如果在這個應答中沒有這個Via頭域值,那麼應答的含義就是說這個應答不應當被這個proxy轉發。本節描述的後續處理步驟也不需要繼續處理,而是用8.1.3節定義的UAC處理規則進行處理(傳輸層處理已經進行)。

這種情況可能會發生,比如,當某一個元素產生一個第10節規定的CANCEL請求。

4、        增加應答到上下文
收到的終結應答都會保存在應答的上下文中,直到收到一個由服務端事務產生的針對這個上下文的終結應答爲止。這個應答是從那個服務端事務中收到最佳終結應答的一個候選。即使這個應答不會被選中作爲最佳應答,這個應答的信息也需要用來構造成爲最佳應答。

如果proxy決定嘗試調用3xx應答返回的聯繫地址,並且把他們添加到目的地集合,它必須在把這個應答添加到應答上下文之前把聯繫地址從應答中移除。不過,proxy不應當在源請求的Request-URI是一個SIPS URI的情況下,嘗試調用非SIPS URI。如果proxy嘗試每一個3xx應答給回的聯繫地址,proxy不應當把這個應答添加到應答上下文中。

從應答中刪去聯繫地址的目的是爲了防止下一個節點嘗試本proxy已經嘗試的地址。

3xx應答可能包含SIP,SIPS和非SIP URI。proxy可以自行決定自己調用那些SIP或者SIPS URI,並且把剩下的放在應答上下文中返回。

如果proxy收到一個對於一個Rquest-URI並非SIP URI的請求的416(不支持的URI scheme)應答,但是原始請求的Request-URI是SIP或者SIPS(就是說,proxy在轉發請求的時候自己調換了請求的SIP或者SIPS爲一個什麼其他的東西),proxy應當增加一個新的URI到目的地集合。這個URI應當是剛纔嘗試的非SIP URI的SIP URI版本。對於電話URL來說,這個就是把電話URL的電話號碼部分放在SIP URI的用戶部分,並且設置SIP URI的主機部分成爲當前請求發送者的區域。19.1.6節有電話URL到SIP URI的轉換細節。

在3XX應答的情況下,如果proxy在416上會產生”遞歸”(因爲嘗試SIP或者SIPS URI而導致遞歸),那麼應當在應答上下文中增加這個416應答。

5、        檢查轉發的應答
當終結應答到達服務端事務的時候,下列應答包必須立刻轉發。
- 任何非100(trying)的臨時應答
- 任何2xx應答。
如果收到一個6xx應答,那麼就不立刻進行轉發,如果是有狀態的proxy,那麼還需要cancel所有的依賴於這個事務的客戶端(在10節中描述的那樣),並且不能在上下文中創建新的分支。

這個是和RFC 2543的不同之處,2543要求proxy立刻轉發6xx應答。對於一個INVITE事務來說,如果立刻轉發6xx應答,會使得2xx應答到達別的分支。這個結果就是讓UAC在2xx應答之後收到一個6xx應答,這個是不允許發生的。在新的規則下,基於接收到一個6xx應答,proxy應當產生一個CANCEL請求,那麼這個會給所有等待的客戶端事務一個487應答,這就是6xx應答應當給上行隊列的一個結果。

在服務端事務上發送了終結應答之後,下列的應答應當立刻被髮送:
- 任何給INVITE請求的2xx應答。

一個有狀態的proxy必須不能立刻轉發其他的應答。特別是,一個有狀態的poxy必須不能轉發任何100(Trying)應答。這些應答是作爲後續將被轉發”最佳”應答的候選,通過上邊的”在上下文中增加應答”的步驟增加到應答上下文中。

任何將被立刻發送的應答都必須遵照”7、 需要合併認證頭域值。”和”8、可選的重寫Record-Route頭域值”來處理。

這一步,合併下一步,確保有狀態的proxy能夠精確轉發一個終結應答到一個非-INVITE請求,或者給一個INVITE請求的非2xx應答或者一個或者多個2xx應答。


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章