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頭域對於支持不同會話描述格式的時候特別有用。
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中。
所有支持INVITE請求的UA都必須支持兩種交換方式。會話描述協議(SDP)(RFC 2327[1])在所有的UA中都必須得到支持,並且它的用法和請求/應答的構造必須遵循[13]中定義的步驟。
在上邊講述的會話請求/應答模型中,只能適用於在包頭域Content-Disposition值是”session”的包體情況。因此,有可能INVITE和ACK請求中都包含一個包體信息(比如,INVITE包含一個photo(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請求被髮送給客户事務層進行處理後,UAC等待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的時候都是有效的。)
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並沒有包含某個會話描述,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請求來結束。