sip協議詳解(二)

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都必須支持

 

 

小虎 2006-05-25 00:07

兩種交換方式。會話描述協議(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)

 

 

小虎 2006-05-25 00:07

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應答。

 

6、    選擇最佳的應答

對於一個有狀態的proxy來說,如果根據上邊的步驟,沒有任何終結應答被立刻發送,並且在客戶端事務中的所有的客戶端服務都已經終結,那麼這個proxy必鬚髮送一個終結應答到一個應答上下文的服務端事務層。

 

那麼這個有狀態的proxy就必須從接收到的應答上下文中選擇一個”最佳”的終結應答。如果在上下文中沒有一個終結應答,那麼proxy就必須返回一個408(請求超時)的應答到服務端事務層。

 

如果應答上下文中有終結應答,那麼proxy就必須從這個應答上下文中取得應答來發送。如果應答上下文中有6xx應答,那麼就必須選擇這個6xx應答。如果沒有6xx應答,那麼proxy應當選擇最小的應答(應答返回代碼比較小)。proxy可以選擇對應最小應答系列中的任意一個應答(比如2xx系列中的任意一個應答)。proxy應當給那些提供對影響請求的應答更多的機會,比如在4xx系列中,選擇401,407,415,420或者484應該稍稍優先一些。

 

當proxy收到503(Service Unavailable)應答的時候,不應當轉發到上行隊列中,除非它能夠知道這個後續的請求隊列都能產生503的應答。換句話說,就是轉發503就意味着proxy確實不能處理任何請求,不僅僅是Request-URI裏邊的這個地址不能處理請

 

 

小虎 2006-05-25 00:08

求。如果只有某個應答會產生503,proxy應當產生500應當到上行隊列中。

 

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

 

例如,如果一個proxy轉發一個請求到4個地方,並且收到了503,407,501,和404應答,它可能選擇407(Proxy Authentication Required)應答。

 

1xx和2xx應答可能和建立對話有關。當請求沒有包含一個To tag,UAC使用在應答中的To tag來區分請求創建的對話的多個應答。如果請求中沒有包含To的tag,那麼proxy必須不能爲1xx或者2xx應答增加這個tag到To頭域。一個proxy不能修改1xx或者2xx應答中的To頭域的tag字段。

 

在請求的1xx應答中,如果應答沒有To頭域的tag字段的時候,由於proxy不能添加tag字段到這個To頭域,它就不能產生它自己的非100臨時應答。但是它可以把這個請求分支到其他一個UAS上,這個UAS可以和proxy共享同樣的元素。這個UAS可以返回它自己的臨時應答,進入請求創建早期對話中。這個UAS並沒有必要作爲一個proxy的嚴格處理步驟存在。它可以是一個在proxy內部的一個虛擬的UAS實現。

 

3到6xx的應答是節點到節點傳遞的。當產生了一個3-6系列的應答的時候,每一個節點都作爲UAS一樣,產生它自己的應答,通常基於下行隊列的應答產生自己的應答。對於每一個節點來說,在轉發3到6系列應答回去的時候,如果這個應答沒有包含To tag,那麼這個節點也應當不改變這個to tag。

 

當收到的應當包含了一個To tag,那麼這個proxy不能夠修改這個To tag。

 

恩,實際上在proxy轉發3到6系列應答的時候,如果替換了To tag也不會讓上行隊列所經過的節點有影響,保留原始的tag值可以有助於調試。

 

當proxy需要合併多個應答的信息的時候,從這些應答中選取To tag的方式是任意的,並且產生一個新的To tag可能可以使得調試更加容易。舉例來說,當合並401(Unauthorized)和407(Proxy Authentication Required)信息的時候,或者合併一個未加密的Contact值和未通過驗證的3xx應答的時候,產生一個新的To tag就會讓調試比較容易。

 

7、    合併認證頭域值

如果選擇的應答是401(Unauthorized)或者407(Proxy Authentication Required),那麼proxy就必須從本應答上下文中的所有其他401(Unauthorized)和407應答中搜集WWWAuthenticate和Proxy-Authenticate 頭域值。並且把這些信息增加到這個應答中。最後的401或者407應答中可能會包含多個WWWAuthenticate和Proxy-Authenticate頭域值。

 

由於這個請求的一個或者多個目的地可能是需要請求身份驗證的,所以這個蒐集步驟就是必須的。客戶端需要接收到這些所有目的者的應答並且在下一次嘗試的時候,爲每一個目的地提供相關的身份證明。在26節有相關的說明。

 

8、    Record-Route

如果最終發送的應答中包含Record-Route頭域值,並且是這個proxy所原創提供的值,那麼在發送這個應答前,proxy可能需要重寫這個值。這提供了一個機制,讓proxy能夠給下一個上行節點或者下行節點提供非本機的一個URI地址。這種情況是很常見的,比如,在多地址主機系統就非常有用。

 

如果proxy是通過TLS收到請求的,並且通過非TLS轉發出去,proxy必須重寫在Record-Route頭域中的URI,重寫成SIPS URI。如果proxy通過非TLS接收到請求,轉發是通過TLS轉發的,那麼proxy必須重寫Record-Route請求頭域的URI爲一個SIP URI。

 

proxy提供的新的URI必須滿足同樣的Record-Route頭域的URI約束(16.6節的第四步)。並且遵循下列的修改:

 

URI不應當包含通訊參數除非proxy知道下一個上行(同下行隊列對應的)節點,對於後續的請求都支持相關的通訊參數。

 

如果proxy打算修改應答中的Record-Route頭域,要做的一件事情就是定位插入的Record-Route頭域值。如果請求是螺旋經過的,並且proxy在每次螺旋的時候都插入了Record-Route值,在應答中(必須在反向路徑中的正確位置)找到正確的需要修改的值就需要一點技巧。上邊的規則強調proxy在增加Record-Route頭域值的時候是必須增加唯一的URI,這樣才能找到正確的一個能夠重寫。我們推薦proxy爲每一個URI的user portion增加一個唯一個proxy實例標誌。

 

當應答到達的時候,proxy修改第一個和proxy實例標誌匹配的Record-Route。這個修改導致產生一個在user portion部分去掉proxy實例的URI。到下一個循環回來處理的時候,同樣的算法(用參數從上而下的尋找Record-Route頭域值)會更改這個proxy插入的下一個Record-Route頭域值。

 

對於proxy增加Record-Route頭域值的請求來說,並非每一個應答都包含一個Record-Route頭域。如果應答包含一個Record-Route頭域,那麼就包含這個proxy增加的值。

 

9、    轉發應答

當”合併認證頭域”和”Record-Route”步驟完成以後,proxy可以對這個應答做其他的附加處理。但是這個proxy不能增加、修改、刪除消息體。並且除非另有指示,除了Via頭域值(在16.7節3步)之外,proxy不能刪除任何頭域值。特別是,proxy不能刪除任何可能增加到與處理和這個應答相關的下一個請求的Via頭域值的”接收到”的參數。proxy必須把應答傳遞到跟這個應答上下文相關的服務端事務。這回導致應答發送到最上的Via頭域值的地方。如果服務端事務不在處理這個發送,這個節點必須作爲無狀態proxy轉發這個應答到服務端通訊層。服務端事務可能已經標誌這個發送應答失敗或者內部狀態機已經設置成爲超時狀態。這些錯誤都應當記錄下來用於診斷錯誤,但是協議並沒有要求proxy做補救措施。

 

proxy必須維持應答上下文直到所有相關事務都已經終結,甚至在發送完成終結應答後還需要維持。

 

10、    產生CANCEL請求

如果轉發的應答是一個終結應答,proxy必須給依賴於這個應答上下文的所有客戶端事務,產生CANCEL請求。在收到6xx應答的時候,proxy同樣應當爲所有等待在這個應答上下文的客戶端事務產生CANCEL請求。等待的客戶端事務就是收到了臨時應答,但是沒有收到終結應答(還是出於處理中的狀態),並且沒有任何CANCEL請求與之相關的請求。產生CANCEL請求請參見9.1節。

 

對於要求基於轉發終結應答而CANCEL的客戶端事務並沒有保證終端不會收到給一個INVITE的多個200(OK)應答。基於多餘一個分支的200(OK)應答可能會在CANCEL請求處理前到達。進一步說,後續的擴展可能會改掉這個產生CANCEL請求的要求。

 

16.8 處理定時器C

如果定時器C 被出發了,proxy必須要麼用另外一個數值重新設定定時器,要麼終結客戶端事務。如果客戶端事務已經收到了臨時應答,那麼proxy必須產生一個與之匹配的CANCEL請求。如果客戶端事務還沒有收到臨時應答,那麼proxy必須就像收到一個408(Request Timeout)一樣的處理。

 

允許proxy重設定定時器就意味着允許proxy基於當前條件(比如服務器利用率等等)動態的擴展事務的生命週期。

 

16.9 處理通訊層的錯誤

如果在轉發請求(參見18.4)的時候,通訊層報告了一個錯誤,那麼proxy必須就像收到了一個503(Service Unavailable)應答一樣的處理。

 

如果proxy在轉發應答的時候接收到錯誤,那麼他就丟棄應答。proxy不能由於通訊的原因而cancel任何和這個應答上下文相關的客戶端事務。

 

如果proxycancel了這些客戶端事務,那麼一個惡意的或者出錯的客戶端可以用一個Via頭域導致所有的事務都失敗。

 

16.10 CANCEL處理

一個有狀態的proxy可以給他自己產生的其他請求在任何時候都產生CANCEL請求(參見9.1遵從接收到對應請求的一個臨時應答)。在接收到一個匹配的CANCEL請求的時候,proxy必須取消任何與應答上下文相關的客戶端事務。

 

當INVITE請求有一個Expires頭域並且這個頭域值已經超時的情況下,一個有狀態的proxy可以對這個處於pending的INVITE客戶端事務發出CANCEL請求。可是,通常來說,這是不必要的,因爲相關的終端會發出結束事務的信號。

 

當有狀態的proxy在它自己的服務端事務上處理CANCEL請求的時候,並沒有新的應答上下文會創建。相反,proxy層尋找與這個CANCEL對應請求的現存的應答上下文。如果找到了對應的應答上下文,那麼這個節點應當立刻返回一個200(OK)應答給這個CANCEL請求者。在這個情況下,這個節點就像8.2節定義的UAS一樣的工作。進一步說,這個節點應當爲每一個依賴於這個上下文的客戶端事務產生一個CANCEL請求(就像在16.7節第10步描述的那樣)。

 

如果一個應答上下文沒有找到,這個節點就無法CANCEL這個請求。它就必須像無狀態proxy一樣轉發這個CANCEL請求(可能這個節點把被CANCEL的請求在先前也當作無狀態的proxy轉發了)。

 

16.11 無狀態的proxy

當作爲無狀態的時候,proxy就是一個簡單的消息轉發者。很多無狀態的處理步驟和有狀態的時候很類似。不同的地方在下邊描述。

 

一個無狀態的proxy並沒有事務的概念,或者用於描述有狀態proxy行爲的應答上下文。相反的是,無狀態的proxy處理消息,無論是請求還是應答,都是直接從通訊層處理的(參見18節)。當然,無狀態proxy自己也不重發這些消息。他們只是轉發他們收到的任何重發的消息(他們本身並沒有能力來分辯那些消息是重發的,那些消息是原始消息)。進一步說,當無狀態的處理一個請求的時候,這個節點並不產生它自己的100(Trying)或者其他臨時應答。

 

無狀態的proxy必須用16.3節描述的那樣來驗證一個請求。

 

無狀態的proxy必須遵從16.4到16.5節定義的步驟來處理請求,有如下幾點例外:

o 無狀態的proxy必須從目的地集合中,選擇一個並且只能選擇一個目的地。這個選擇必須是根據消息的頭域並且是和服務器時間無關的。特別是,一個重發的請求必須能夠每次都轉發到相同的目的地。進一步說,CANCEL和非路由的ACK請求必須和他們相關的INVITE請求有相同的轉發目的地。

 

一個無狀態的proxy必須遵循16.6節定義的請求處理步驟,並且有下列的不同:

 

o無狀態proxy的branchID來說,必須要求在時間上和空間上都是唯一的。也就是說,無狀態的proxy不能簡單的使用一個隨機數產生器來計算branchID的第一個部分(16.6節8步)。這是由於請求的重發需要相同的值,並且無狀態的proxy不能區分重發的請求和原始請求。因此,branch參數的組成部分要求唯一,這樣使得重發的時候能夠填寫相同的值。對於無狀態的proxy來說,branch參數必須作爲一個重發無關的消息處理參數存在。

 

我們沒有規定無狀態proxy採用何種手段保證branchID的唯一性。不過,下列步驟是推薦的方法。proxy檢查在接收到請求的最上Via頭域值的branchID。如果它是由magic cookie打頭的,那麼branchID的第一個部分就是當作接收到的branchID的hash值。否則branchID的第一個部分就當作是Via頭域的最上值、To頭域的tag、From頭域的tag,Call-ID頭域,Cseq序列號(除了方法部分),接收到的請求的Request-URI的一個hash值。這些頭域值在不同事務中總是不一樣的。

 

o所有其他的消息轉換(16.6節)必須保證轉發重發的請求的時候能夠轉發到相同的節點。特別是,如果proxy在Record-Route頭域中增加了值,或者在Route頭域中增加了值,proxy必須在轉發重發的請求的時候增加相同的值。至於Via 的branch參數,這就意味着轉發必須是基於時間無關的配置或者請求重發無關的屬性。

 

o一個無狀態proxy決定轉發的地點是像16.6節10步描述的有狀態的proxy一樣。但是請求是直接交給通訊層發送的,而不是交給客戶端事務。

 

由於一個無狀態的proxy必須轉發重發的請求到相同的地方,並且增加標誌性的branch參數,它只能用消息中本身的信息和時間無關的配置來計算。如果配置狀態不是時間無關的(比如,如果路由表更新了),這個改變相關的請求,在這個改動開始以後,到在事務超時的時間範圍內,就不能作爲無狀態的轉發了。這個處理這段時間的請求是實現相關的。通常處理的方法,是把這些請求當作事務有狀態的進行轉發。

 

無狀態的proxy必須不能對CANCEL做特別的處理。CANCEL的處理就像對其他請求的處理一樣進行。特別是,一個無狀態的proxy使用相同的Route頭域來處理CANCEL請求,就像處理其他請求一樣。

 

對於16.7節中定義的應答處理,對於無狀態proxy來說,並不適用。當一個應答到達一個無狀態proxy,proxy必須檢查最上的Via頭域值的sent-by參數。如果這個地址和這個proxy一樣(就是和proxy插入的先前的請求中的值一樣),那麼這個proxy必須從應答中移除這個頭域值,並且轉發這個應答到下一個Via頭域值。這個proxy必須不能增加,修改或者刪除消息體。除非有特別的說明,proxy必須不能移除其他的頭域值。如果地址不匹配本proxy,消息就必須簡單的悄悄扔掉。

 

16.12 Proxy Route處理的總結

在沒有本地策略的情況下,proxy對於包含Route頭域的請求處理可以歸結於如下的步驟:

 

1、    proxy會檢查Request-URI。如果它指向的是本proxy所負責的區域,那麼proxy會用位置服務的結果來替換這個URI。否則,proxy不改變這個URI。

2、    proxy會檢查Route頭域的最上URI。如果這個URI指向這個proxy,這個proxy從Route頭域中移除(這個路由節點已經到達)。

3、    proxy會轉發請求到最上的Route頭域值所標誌的URI,或者Request-URI(如果沒有Route頭域)。proxy通過附件[4]的步驟來產生地址,端口,通訊協議等等用來轉發請求所必須的參數。

 

如果在請求的路徑中,沒有嚴格路由節點,Request-URI會始終標誌着請求的目的地。

 

16.12.1例子

16.12.1.1 基本SIP四邊形

本例子是一個基本的SIP四邊傳送,U1->P1->P2->U2,使用proxy來傳送。下邊是過程。

 

U1 發送:

INVITE sip:[email protected] SIP/2.0

Contact: sip:[email protected]

發給P1,P1是一個外發的proxy。P1並不管轄domain.com,所以它查找DNS並且發送請求到那裏。它也增加一個Record-Route頭域值:

INVITE sip:[email protected] SIP/2.0

Contact: sip:[email protected]

Record-Route: <sip:p1.example.com; lr>

 

P2收到這個請求。這是domain.com所以它查找位置服務器並且重寫Request-URI。它也增加一個Record-Route頭域值。請求中沒有Route頭域,所以它解析一個新的Request-URI來決定把請求發送到哪裏。

INVITE sip:[email protected] SIP/2.0

Contact: sip:[email protected]

Record-Route: <sip:p2.domain.com; lr>

Record-Route: <sip:p1.example.com; lr>

 

在u2.domain.com的被叫方接收到這個請求並且返回一個200OK應答:

SIP/2.0 200 OK

Contact: sip: [email protected]

Record-Route: <sip:p2.domain.com;lr>

Record-Route: <sip:p1.example.com;lr>

 

u2的被叫方並且設置對話的狀態的remote target URI爲:

sip: [email protected]並且它的路由集合是:

(<sip:p2.domain.com;lr>,<sip:p1.example.com;lr>)

 

這個轉發通過P2到P1到U1。現在U1設置它自己的對話狀態的remote target URI爲:sip:[email protected]並且它的路由集合是:

(<sip:p1.example.com;lr>,<sip:p2.domain.com;lr>)

 

由於所有的路由集合元素都包含了lr參數,那麼U1構造最後的BYE請求:

BYE sip:[email protected] SIP/2.0

Route: <sip:p1.example.com;lr>,<sip:p2.domain.com;lr>

 

就像其他所有的節點(包括proxy)會做的那樣,它會使用DNS來解析最上的Route頭域的URI值,這樣來決定往哪裏發送這個請求。這就發到了P1。P1發現Request-URI中標記的URI不是它負責的域,於是它就不改變這個Request-URI。然後看到它是Route頭域的第一個值,於是就從Route頭域中移去,並且轉發這個請求到P2:

BYE sip:[email protected] SIP/2.0

Route: <sip:p2.domain.com;lr>

P2也發現它自己並非負責這個Request-URI的域(P2負責的是domain.com並非u2.domain.com),於是P2並不改變它。它看到自己在Route的第一個值,於是移去這個,並且向u2.domain.com轉發(根據在Request-URI上查找DNS):

BYE sip:[email protected] SIP/2.0

 

16.12.1.2 穿越一個嚴格路由proxy

在這個例子中,對話建立通過4個proxy,每一個增加Record-Route頭域值。第三個proxy是由嚴格路由實現的(RFC 2543)。

U1->P1->P2->P3->P4->U2

 

INVITE請求到達U2包括了:

INVITE sip:[email protected] SIP/2.0

Contact: sip:[email protected]

Record-Route: <sip:p4.domain.com;lr>

Record-Route: <sip:p3.middle.com>

Record-Route: <sip:p2.example.com;lr>

Record-Route: <sip:p1.example.com;lr>

 

並且U2返回了一個200 OK。接着,U2根據第一個Route頭域值發送下邊的BYE請求到P4:

BYE sip:[email protected] SIP/2.0

Route: <sip:p4.domain.com;lr>

Route: <sip:p3.middle.com>

Route: <sip:p2.example.com;lr>

Route: <sip:p1.example.com;lr>

 

P4並不管轄Request-URI指出的域,於是就不更改這個Reqeust-URI。它發現自己在第一個Route頭域中,於是把自己從Route頭域移除。然後準備發送請求到現在的第一個Route頭域值:sip:p3.middle.com,但是它發現這個URI並沒有包含lr參數,於是在發送前,它把這個請求更改成爲:

BYE sip:p3.middle.com SIP/2.0

Route: <sip:p2.example.com;lr>

Route: <sip:p1.example.com;lr>

Route: <sip:[email protected]>

P3是一個嚴格路由,於是它轉發到P2:

BYE sip:p2.example.com;lr SIP/2.0

Route: <sip:p1.example.com;lr>

Route: <sip:[email protected]>

P2看到Request-URI是它放在Record-Route頭域中的值,於是在進一步處理前,它把這個請求改寫爲:

BYE sip:[email protected] SIP/2.0

Route: <sip:p1.example.com; lr>

P2自己並不管轄u1.example.com,於是它根據Route頭域的值,轉發這個請求到P1。

 

P1發現自己在Route頭域的最上,於是把自己移除,得到:

BYE sip:[email protected] SIP/2.0

由於P1並不管轄u1.example.com並且沒有其他的Route頭域,P1會基於Request-URI轉發這個請求到u1.example.com。

 

16.12.1.3 重寫Record-Route頭域值。

在這裏例子中,U1和U2是在不同的私有域空間中,並且他們通過proxy P1開始一個對話,這個P1作爲不同私有namespace的一個網關存在。

U1->P1->U2

U1發送:

INVITE sip:[email protected] SIP/2.0

Contact: <sip:[email protected]>

 

P1使用自己的定位服務並且發送下邊的信息到U2:

INVITE sip:[email protected] SIP/2.0

Contact: <sip:[email protected]>

Record-Route: <sip:gateway.rightprivatespace.com;lr>

 

U2發送200 OK應答回給P1:

SIP/2.0 200 OK

Contact: <sip:[email protected]>

Record-Route: <sip:gateway.rightprivatespace.com;lr>

 

P1重寫它的Record-Route頭域參數,提供成爲U1能夠使用的參數,並且發送給P1:

SIP/2.0 200 OK

Contact: <sip:[email protected]>

Record-Route: <sip:gateway.leftprivatespace.com;lr>

 

稍後,U1發送接下來的BYE到P1:

BYE sip:[email protected] SIP/2.0

Route: <sip:gateway.leftprivatespace.com;lr>

 

P1轉發到U2:

BYE sip:[email protected] SIP/2.0

 

17事務

SIP是一個基於事務處理的協議:部件之間的交互是通過一系列無關的消息交換所完成的。特別是,一個SIP 事務由一個單個請求和這個請求的所有應答組成,這些應答包括了零個或者多個臨時應答以及一個或者多個終結應答。在事務中,當請求是一個INVITE(叫做INVITE事務),當終結應答不是一個2xx應答的時候,事務還包括一個ACK。如果應答是一個2xx應答,那麼ACK並不認爲是事務的一部分。

這個分開的原因是基於傳遞全部200(OK)應答到UAC的INVITE請求的重要性所決定的。要把所有的200應答全部發給UAC,那麼UAS獨自負責這些應答的重新傳送(參見13.3.1.4),UAC肚子負責挨個ACK確認(參見13.2.2.4)。由於ACK的重傳只由UAC發起,所以在自己的事務中進行重傳會比較有效。

 

事務分爲客戶端和服務端兩方。客戶端的事務是客戶端事務,服務器端的事務就是服務端事務。客戶端事務發出請求,並且服務端事務送回應答。客戶端和服務端事務都是邏輯上的概念,他們可以被無數部件所包含。特別是,他們在UA中和有狀態的proxy服務器中存在。以第四節的例子來說明。在這個例子中,UAC執行客戶端事務,它的外發proxy執行服務端事務。外發proxy同時也執行客戶端事務,把請求發送到一個那發proxy的服務端事務。這個proxy也同時執行一個客戶端事務,把請求發到一個UAS的服務端事務上去。這個在圖四中比較明白:

 

 

小虎 2006-05-25 00:08

+---------+         +---------+            +---------+         +---------+

|        +-+|Request |+-+     +-+|Request    |+-+    +-+|Request |+-+        |

|        |C||------->||S|     |C||------->    ||S|    |C||------->||S|        |

|        |l||         ||e|     |l||            ||e|    |l||         ||e|        |

|        |i||         ||r|     |i||            ||r|    |i||         ||r|        |

|        |e||         ||v|     |e||            ||v|    |e||         ||v|        |

|        |n||         ||e|     |n||            ||e|    |n||         ||e|        |

|        |t||         ||r|     |t||            ||r|    |t||         ||r|        |

|        | ||         || |     | ||            || |    | ||         || |        |

|        |T||         ||T|     |T||            ||T|    |T||         ||T|        |

|        |r||         ||r|     |r||            ||r|    |r||         ||r|        |

|        |a||         ||a|     |a||            ||a|    |a||         ||a|        |

|        |n||         ||n|     |n||            ||n|    |n||         ||n|        |

|        |s||Response||s|     |s||Response    ||s|    |s||Response||s|        |

|        +-+|<-------|+-+     +-+|<-------    |+-+    +-+|<-------|+-+        |

+---------+         +---------+            +---------+         +---------+

UAC                Outbound            Inbound               UAS

Proxy                Proxy

圖4: 事務關係

無狀態的proxy並沒有客戶端或者服務端的事務。事務是一邊基於UA或者有狀態的proxy,另外一邊也基於UA或者有狀態的proxy。在SIP事務範疇下,無狀態的proxy是用作透明轉發很有效。客戶端事務的用處是用於從一個元素中接收一個請求,這個客戶端是內嵌的(這個元素就是”事務用戶”或者TU;它可以是一個UA或者有狀態的proxy),並且可靠的把這個請求傳送到一個服務端事務。

 

客戶端事務也負責接收應答並且把應答轉交TU處理,過濾掉重發的應答或者不允許的應答(比如給ACK的應答)。另外,在INVITE請求的情況下,客戶端事務也負責產生給2xx應答的ACK請求。

 

類似的,服務端事務也負責從通訊層接收請求並且轉發這個請求到TU。服務端事務過濾重發的請求。並且服務端事務從TU接收應答並且轉發到通訊層來發送。在INVITE事務的情況下,它需要接收給非2xx應答的終結應答的ACK請求。

 

2xx應答和它的ACK請求通過特定的方式來接收和處理。這個應答只會被UAS重發,並且它的ACK只由UAC產生。由於呼叫者知道整個已經接收呼叫的用戶集合,所以需要這種端到端的處理。由於這樣的特別處理,2xx應答的重發是基於UA核心的,並非基於通訊層。類似的,給2xx應答的ACK處理也是由UA核心處理的,每個路徑上的proxy僅僅轉發這些INVITE的2xx應答以及他們的ACK。

 

17.1 客戶端事務

客戶端事務是通過維持一個狀態機來提供服務的。

 

TU和客戶端事務通過一個簡單的接口進行通訊。當TU希望初始化一個新的事務,它創建一個客戶端事務並且通過設置ip地址,端口和transport來把一個SIP請求交給它傳送。然後客戶端事務開始執行它自己的狀態機。合乎規格的應答會從客戶端事務傳送給TU。

 

總共有兩種類型的客戶端事務狀態機,根據TU傳遞的請求的方法不同來區分的。一個用於處理INVITE請求。這種狀態機對應的是一個INVITE客戶事務。另外一個是用來處理其他所有的非INVITE請求的。它對應的是非INVITE客戶事務。對於ACK來說,是不存在客戶事務的。如果TU希望送一個ACK請求,它直接交給通訊層進行通訊處理。

 

INVITE事務和其他事務是不同的,因爲它的時間週期很長。通常,對於INVITE請求的應答來說,都需要人的參與,這樣會導致在應答INVITE請求之前會有很長的延時。在三方握手(人,兩方機器)的時候也會有很長的延時。在另一方面,其他請求的響應都是很快就完成的。因爲其他非INVITE請求事務是雙方的握手,TU能夠立刻對非INVITE請求作出應答。

 

17.1.1 INVITE客戶事務

17.1.1.1 INVITE事務概述

INVITE請求包含了一個三方的握手。客戶端事務發送一個INVITE,服務端事務回送一個應答,客戶端事務發送一個ACK。對於非可靠傳輸(比如UDP),客戶端事務每隔T1重發請求,每次重發後間隔時間加倍。T1是一個估計的循環時間(round-trip time,RTT),缺省設置成爲500ms。幾乎所有的事務定時器都以T1爲單位,並且調整T1的值也就調整了那些定時器的值。請求不會在可靠的通訊協議上重新發送。在接收到1xx應答以後,重發機制完全停止,並且客戶端等待更進一步的應答。服務端事務可以發送附加的1xx應答,這個應答並非由服務端事務可靠傳輸。最後,服務端事務會發送一個終結應答。對於非可靠的傳輸協議,應答會間隔時間來重發,對於可靠的傳輸協議,它只發送1次。對於客戶端事務所接收的每一個終結應答,客戶端事務都發送一個ACK,用於終止應答的重發送。

 

17.1.1.2 正式的描述

INVITE客戶端事務的狀態機在圖5中展示。初始狀態,”calling”,必須保證TU是用INVITE請求來初始化一個新的客戶端事務。客戶端事務必須把請求發送到通訊層來進行發送(18節)。如果使用的是非可靠傳輸的通訊層,客戶端事務必須啓動一個定時器A並且由缺省值T1組成。如果是一個可靠的通訊協議,那麼客戶端事務不應當啓動定時器A(定時器A控制請求的重發送)。對於任何通訊協議來說,客戶端事務必須啓動一個定時器B並且有着64×T1秒的缺省值(定時器B控制事務的超時)。

 

當定時器A觸發了,客戶端事務必須重發這個請求,把請求交給通訊層進行發送,並且重新設置定時器爲2*T1。在傳輸層中重傳的定義是指把剛纔通過傳輸層發送的消息,再次交給傳輸層重新發送一次。

 

當定時器A在2×T1後觸發了,請求必須再次重傳(如果客戶端事務依舊還是在這個狀態的話)。這個處理必須持續下去,這樣請求才能每重發一次以後定時器延時1倍。重發機制只有當客戶端事務在”calling”狀態的時候才能進行。

 

缺省的T1是500ms。T1是一個RTT的估計時間,是在客戶端和服務端的一個事務處理的估計時間。節點可以(不推薦)使用更小的T1值,比如私有網絡,並不接到INTERNET的網絡可以設置小一點。T1也可以設置成爲大一點的值,並且我們建議如果當我們知道RTT值比較大的時候(比如高延時的網絡)應當設置T1成爲大一點的值。不管T1如何取值,本節要求的重傳機制要求的指數延時是必須使用的。

 

當定時器B觸發的時候,如果客戶端事務是依舊在”calling”狀態,那麼客戶端事務應當通知TU發生了超時。客戶端事務必須不能產生ACK。64×T1是和在不可靠通訊鏈路上傳輸7個請求的時間相同。

 

如果客戶端事務在”calling”狀態接收到一個臨時應答,那麼就把狀態切換到”proceeding”狀態,客戶端事務不應當再次重新發送請求了。進一步說,臨時應答必須傳送給TU。在”proceeding”狀態的任何臨時應答都必須傳送給TU。

 

當在”calling”或者”proceeding”狀態的時候,如果接收到一個應答碼是300-699的應答,那麼就必須把狀態切換到”Completed”。客戶端事務必須把收到的應答轉給TU,並且客戶端事務必須傳生ACK請求,即使通訊層是可靠傳輸的(在17.1.1.3節中有描述怎樣根據應答創建一個ACK請求)並且把ACK交給傳輸層進行傳送。ACK必須和原始請求發送到相同的地址,端口和用同樣的transport。當客戶端事務進入”Completed”狀態的時候,應當開始一個定時器D,缺省值是在非可靠通訊上是至少32秒,在可靠通訊上是0秒。定時器D反應了服務端事務在非可靠傳輸的情況下,在”completed”狀態維持的時間。這個是和INVITE請求服務端事務定時器H相同的,定時器H的缺省值是64*T1。不過,客戶端事務不知道服務端事務使用的T1值,所以我們用絕對值32秒來代替T1用作定時器D的缺省值。

 

在”completed”狀態下,受到的任何終結應答的重傳都應當產生一個ACK應答到通訊層來重新發送,但是新近收到的應答卻不能傳送給TU。一個應答是否是重傳的定義是根據這個應答是否和客戶端事務按照17.1.3定義的規則匹配。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圖5: INVITE客戶端事務

 

如果在客戶端事務狀態是”Completed”的時候,定時器D觸發,那麼客戶端事務必須轉到終結狀態。當客戶端狀態是”calling”或者”proceeding”狀態的時候,接收到一個2xx應答必須導致客戶端事務進入”terminated”狀態,並且應答必須交給TU處理。處理這個應答的方法依賴於TU是否是一個proxy核心還是是UAC核心。UAC核心會給應答產生ACK,proxy核心會轉發一個200(OK)應答到上行隊列。這個在proxy和UAC之間,對200(OK)的不同的處理是導致對應答的處理不在事務層進行的原因。

 

當客戶端事務進入”terminate”狀態以後,客戶端事務必須立刻銷燬。這樣才能保證正確操作。原因是當給一個INVITE請求的2xx應答的不同處理;對於proxy轉發的時候和對UAC處理ACK的時候是不一樣的。因此,每一個2xx都需要交給proxy 核心(這樣才能被轉發),或者交給UAC核心(這樣才能被ACK確認)。這期間沒有事務層的處理。無論應答是否由通訊層收到,如果通訊層找不到匹配的客戶端事務(用17.1.3的方式),那麼應答就應當交給核心處理。這是由於與之匹配的客戶端事務已經被第一個2xx應答所銷燬,後續的2xx應當就匹配不成功了,於是就交給核心來處理。

 

17.1.1.3 構造ACK請求

本節定義了在客戶端事務中構造ACK請求的方法。UAC核心爲2xx應答產生ACK請求必須使用13節描述的方法,而不是用下邊的方法。

 

在客戶端事務中構造的ACK請求必須包括與原始請求相同的Call-ID, From, Request-URI頭域值(就是說和在客戶端事務發到通訊層的請求中的這些頭域值相同)。在ACK請求中的To頭域必須和被確認的應答的To頭域值相同,因此通常和原始請求有所不同,不同點在增加了附加的tag參數。ACK必須包含一個單個的Via頭域,並且必須和原始請求的最上邊一個Via頭域值相等。ACK的Cseq頭域必須包含和原始請求的Cseq的序列號相同,但是方法參數應當是”ACK”。

 

如果INVITE請求的應答是有Route頭域的,這些Route頭域必須也在ACK中。這是確保ACK能夠正確路由通過下行隊列的無狀態的proxy。

 

雖然請求可以包含一個包體,但是ACK的包體卻比較特別,因爲請求不能因爲不能理解包體而拒絕這個請求。因此,我們不建議在給非2xx應答的ACK請求中放置包體,但是如果放置了,並且假設給INVITE的應答不是415應答,那麼包體的類型應當嚴格和INVITE請求中定義的那樣。如果是415應答,那麼ACK的包體應當和415應答中的Accept列出的類型一致。

 

例如:有如下請求

INVITE sip:[email protected] SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKkjshdyff

To: Bob <sip:[email protected]>

From: Alice <sip:[email protected]>;tag=88sja8x

Max-Forwards: 70

Call-ID: 987asjd97y7atg

Cseq: 986759 INVITE

 

給非2xx終結應答的ACK請求應當是:

ACK sip:[email protected] SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKkjshdyff

To: Bob <sip:[email protected]>;tag=99sa0xk

From: Alice <sip:[email protected]>;tag=88sja8x

Max-Forwards: 70

Call-ID: 987asjd97y7atg

Cseq: 986759 ACK

 

17.1.2 非INVITE客戶端事務

17.1.2.1 非INVITE事務概覽

非INVITE事務並不使用ACK。他們只是簡單的請求-應答的交互。對於非可靠的通訊來說,請求是間隔倍增T1的時間重新傳輸(直到間隔時間達到T2)。如果收到了一個臨時應答,在非可靠通訊上,重傳繼續知道達到T2。只有當重傳的請求收到的時候,服務端事務會重傳其發出的最後一個應答,既可以是臨時的應答也可以是終結應答。這就是爲什麼請求在收到一個臨時應答之後還需要一直重傳的原因;他們能夠確保收到一個終結應答。

 

不像INVITE事務,非INVITE事務不需要對2xx應答做特別處理。UAC對一個非INVITE請求來說,只會產生一個單個的2xx應答。

 

17.1.2.2 正式的描述

在圖6中講述了非INVITE客戶端事務的狀態機。這個狀態機和INVITE客戶端事務的狀態機非常像。

 

當TU用請求來初始化一個新的客戶端事務的時候,首先進入的是“trying”狀態。當進入這個狀態的時候,客戶端事務應當初始化一個定時器F,這個定時器F應當有一個初始值64×T1秒。這個請求必須交給通訊層來發送。如果使用的是非可靠傳輸的通訊協議,客戶端事務必須還設置定時器E,初始值是T1。如果定時器E觸發了,並且還是在”trying”狀態,那麼定時器需要設置成爲MIN(2*T1,T2),並且重新發送;如果再次觸發了,那麼就再設置成爲MIN(4*T1,T2),每次都是倍增,知道T2。這個過程會一直繼續,直到重發的間距是T2爲止。缺省的T2是4秒,並且它大概是一個在沒有立刻響應的情況下,非INVITE服務端事務處理一個請求的時間。根據缺省的T1和T2,那麼間隔就會是:500ms,1s,2s,4s,4s,4s以次類推。

 

如果定時器F觸發了,並且客戶端事務依舊是在”trying”狀態,那麼客戶端事務應當通知TU這個超時,並且轉入”terminate”狀態。如果在”trying”狀態的時候收到了一個臨時應答,那麼這個應答必須轉給TU處理,並且客戶端事務轉到”proceeding”狀態。如果在”trying”狀態收到了一個終結應答(200-699的應答碼),那麼應答必須交給TU,並且客戶端事務必須轉到”Completed”狀態。

 

如果定時器E在”Proceeding”狀態觸發了,那麼請求必須交給通訊層進行傳輸,並且定時器E必須重新設置成爲T2秒。如果定時器F在”Proceeding”狀態觸發了,那麼必須通知TU超時了,並且客戶端事務必須轉到終結狀態。如果在”Proceeding”狀態的時候收到了一個終結應答(狀態碼200-699),這個應答必鬚髮送給TU,並且客戶端事務必須轉到”Completed”狀態。

 

一旦客戶端事務進入”Completed”狀態,對於非可靠傳輸的情況,客戶端事務必須設置一個定時器K=T4秒,對於可靠傳輸的情況,設置定時器K=0秒。這個”Completed”狀態維持的目的是爲了緩衝可能會收到的其他重發的應答(這是爲什麼客戶端事務在這裏爲非可靠傳輸維持一段時間的原因)。T4代表了網絡在客戶端和服務端事務中傳輸信息可能的時間。缺省的值T4=5秒。當應答具有相同的事務匹配的時候,根據17.1.3的判定,這個應答就是重發的應答。如果定時器K在這個狀態被觸發,客戶端事務必須轉到”Terminate”狀態。

 

當事務進入終結狀態,就必須立刻終止了。

 

17.1.3 客戶端事務匹配應答

當客戶端事務的通訊層收到一個應答,他必須決定是否由客戶端事務來處理這個應答,這樣17.1.1和17.1.2才能夠正確執行。在Via頭域的最上邊的branch參數就是用來做這個的。一個應答和一個客戶端事務匹配的話,就有兩個條件:

1、    如果應答Via最上邊的branch參數和創建這個客戶端事務的請求的Via最上邊的branch參數相同。

2、    如果Cseq頭域的方法參數和創建事務的請求的方法相同。這是因爲CANCEL方法的事務和源請求的事務不同,但是卻有相同的branch參數所決定的。

 

如果一個請求是廣播發送的,他可能從不同的服務器上得到不同的應答。這些應答的最上邊的Via都有相同的branch參數,但是在To tag中是不同的。當收到了第一個應答,基於上邊的規則,將會判定是這個客戶端事務的應答,其他的應答將會視同爲重發。這並不是錯誤的情況;多點傳送SIP只是提供了一個根本的”尋找最接近的單點”服務的方法,這樣就限定了只需要處理一個單個應答。詳情參見18.1.1。

 

 

 

17.1.4 處理通訊錯誤。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圖6:非INVITE客戶端事務

當客戶端事務發送一個請求到通訊層發送的時候,如果通訊層報告發送失敗,那麼需要執行下列步驟。

 

客戶端事務應當通知TU這個通訊失敗,並且客戶端事務應當直接轉到”Terminate”狀態。TU處理通訊失敗的機制在附件[4]中描述。

 

17.2 服務端事務

服務端事務是用來傳輸請求到TU並且可靠的傳輸應答的。它是通過狀態機來實現的。服務端事務是當請求到達的時候由核心創建的,事務的處理也是主要圍繞着對應請求的(也就是說並非全部都是和對應請求相關)。

 

和客戶端事務對應的,狀態機依賴於是否接收的請求是INVITE請求。

 

17.2.1 INVITE服務端事務

INVITE服務端事務的狀態圖在圖7表達。

 

當爲一個請求創建了服務端事務的時候,服務端事務進入”proceeding”狀態。除非服務端事務知道TU在200ms之內會生成臨時或者終結應答(在這種情況下,TU可能會產生100Trying應答),他必鬚生成100(Trying)應答。這個臨時應答是用來停止客戶端重發請求的,這個可以避免網絡風暴。這個100(Trying)應答是根據8.2.6節描述的步驟構造的,除此之外: 如果接收的請求頭中的To頭域沒有tag標誌,那麼原來描述的可以增加tag標記,更改成爲不應該增加tag標誌。這個請求必須交給TU處理。

 

TU可以給服務端事務任意數量個臨時應答。只要服務端事務在”proceeding”狀態,每個臨時應答都應當交給通訊層發送。這些臨時應答並非被通訊層可靠的發送(他們並不重新發送臨時應答)並且臨時應答並不改變服務端事務的狀態。如果在”proceeding”狀態,收到一個請求的重發請求,那麼就需要把從TU最近收到的那個臨時應答重新交給通訊層發送一次。請求是否是重發的請求,是基於17.2.3來判定的匹配相同服務端事務的請求。

 

如果,在”proceeding”狀態,TU發送了一個2xx應答給服務端事務,服務端事務必須把這個應答交給通訊層進行發送。這個並非由服務端事務進行重發;對於2xx應答的重發是由TU處理的。服務端事務必須轉到”Terminated”狀態。

 

當在”Proceeding”狀態的時候,如果TU交給服務端事務一個300到699的應答,那麼應答必須交給通訊層進行發送,並且狀態機必須進入”Completed”狀態。對於非可靠傳輸的情況,必須設置定時器G=T1秒,對於可靠傳輸的情況,不設置定時器G(=0的情況就是不設置)

這個是和RFC2543所不同的,2543要求應答都要重發,甚至在可靠傳輸的情況下。

 

當進入了”Completed”狀態,必須爲所有的傳輸,設置一個定時器H=64×T1秒。定時器H決定何時服務端事務取消重發應答。這個值和定時器B的取值一樣,是等同於客戶端事務會重試發送請求的時間。如果定時器G觸發了,那麼應答會交給通訊層再次發送,並且定時器設置成爲MIN(2*T1,T2)秒。依此類推,當定時器G再次觸發,那麼定時器G的值會翻倍,直到T2。這個和非INVITE客戶端事務的”trying”請求的重發機制是一樣的。進一步說,當在”Completed”狀態的時候,如果接收到重發的請求,服務端事務應當把應答交給通訊層再次發送。

 

當服務端事務在”Completed”狀態的時候,如果收到了一個ACK請求,服務端事務必須轉到”Confirmed”狀態。因爲定時器G會在這個狀態被忽略,所有的應答重發都會被終止。

 

如果在”completed”狀態的時候,定時器H觸發了,就意味着沒有收到ACK請求。在這個情況下,服務端事務必須轉到”Terminated”狀態,並且必須通知TU事務失敗。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圖7:INVITE服務端事務

 

設定”Confirmed”狀態的目的是爲了處理任何附加的ACK消息,這是由重發的終結應答所觸發的。當進入這個狀態,如果是在不可靠傳輸協議,那麼就要設定一個定時器I=T4秒,如果是可靠傳輸協議,那麼就設定I=0。當定時器I觸發了,服務端事務必須轉到”Terminated”狀態。

 

當服務端事務狀態處於”Terminated”狀態,這個事務必須立刻銷燬。和客戶端事務一樣,這是爲了保證給INVITE的2xx應答的可靠性。

 

17.2.2 非INVITE服務端事務

對非INVITE服務端事務的狀態機是在圖8中表示。

 

 

小虎 2006-05-25 00:09

當收到一個不是INVITE或者ACK的請求的時候,狀態機會初始化成爲”trying”狀態。並且這個請求會交給TU處理。當在”trying”狀態,任何重發的請求會被忽略。一個請求在通過17.2.3節的步驟,匹配現有的服務端事務,將被認爲是重發的請求。

 

當處於”trying”狀態,如果TU交給服務端事務一個臨時應答,服務端事務應當進入”Proceeding”狀態。這個應答必須交給通訊層進行發送。在”Proceeding”狀態下從TU收到的任何應答都必須交給通訊層進行發送。如果一個重發的請求在”proceeding”狀態下收到了,那麼最近發出的一個臨時應答應當再次交給通訊層進行重發。如果在”Proceeding”狀態下,TU交給服務端事務一個終結應答(應答碼是200-699),那麼服務端事務必須進入”Completed”狀態,並且應答必須交給通訊層進行發送。

 

當服務端事務進入了”Completed”狀態,對於不可靠傳輸協議來說,必須設定一個定時器J=64×T1秒,對於可靠傳輸來說,設定爲0秒(就是不設定定時器)。當在”Completed”狀態下,當服務端事務收到了一個重發的請求的時候,服務端事務必須交給通訊層終結應答來重新發送。在”Completed”狀態下,任何其他TU傳遞下來給服務端事務的終結應答都必須被拋棄。服務端事務保持這個狀態直到定時器J觸發,當定時器J觸發了以後,服務端事務必須進入”Terminated”狀態。

 

17.2.3 爲服務端事務匹配請求。

當服務端從網絡上收到一個請求以後,他必須和現有的事務進行判定。這個是根據下邊的規則來判定的。

 

首先要檢查請求中的Via頭域的最上一個branch參數。如果他以”z9hG4bk”開頭,那麼這個請求一定是由客戶端事務根據本規範產生的。因此,branch參數在該客戶端發出的所有的事務中都是唯一的。根據下列規則我們可以判定請求是否和事務匹配:

 

1、    請求中的最上的Via頭域的branch參數和創建本事務的請求的最上的Via頭域的branch參數一樣,並且:

2、    請求的最上的Via頭域的sent-by參數和創建本事務的請求的最上的Via頭域的send-by參數一樣,並且:

3、    請求的方法和創建本事務的方法一樣。這有一個例外,就是ACK,ACK對應的創建本事務的請求方法是INVITE。

這個匹配規則用於INVITE和非INVITE事務。

 

send-by參數被用於匹配過程,這是因爲有可能存在無意/惡意的相同的不同客戶端傳來的branch參數。

 

如果最上的Via頭域的branch參數不存在,或者沒有包含那個”z9hG4bk”,那麼就用下列步驟進行判定。這是爲了和RFC2543進行兼容的。

 

如果是INVITE請求,並且這個INVITE請求的Request-URI,To tag,From tag,Call-ID,Cseq,和最上的Via頭域都和創建事務的INVITE請求的這些字段匹配,那麼這個INVITE請求就是匹配這個事務的INVITE請求。在這個情況下,INVITE就是創建這個事務的INVITE請求的一個重發。ACK請求在匹配創建事務的INVITE請求的Request-URI, From tag, Call-ID ,Cseq序列號(非方法字段), 最上的Via頭域,並且To tag和服務端事務發出的應答的To tag相同,這個ACK就是這個事務的ACK。當這些頭域比較完成,那麼這個匹配也就完成了。在ACK比較中包含To tag的比較是爲了在proxy上能夠區別給2xx的ACK和給其他應答的ACK,這個proxy可能會轉發全部的應答(這個會在某種罕見的情況下發生。特別是,當一個proxy分支一個請求,接着宕機了,應答會轉發到別的proxy,這個proxy可能會終止轉發多重應答到上行隊列)。一個匹配INVITE請求事務的ACK請求,如果這個INVITE請求已經被前一個ACK請求所匹配,那麼這個ACK請求就是上一個ACK請求的重發。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圖8: 非INVITE 服務端事務

 

對於所有的其他請求方法,如果請求的Request-URI,To tag,From tag,Call-ID, Cseq(包括Cseq中的方法字段),以及Via頭域的最上值,都和創建服務端事務的請求想匹配,那麼這個請求就是這個事務的匹配請求。匹配是基於針對每一個頭域值的判定進行的。當非INVITE請求和現有事務匹配了,那麼它就是創建這個事務的請求的一個重發。

 

由於匹配規則中包含了Request-URI,服務器不能匹配應答對應到事務。所以當TU傳送了一個應答到服務端事務,它必須爲這個應答指定傳送到那個服務端事務。

 

17.2.4 處理通訊錯誤

當服務端事務發送一個應答到通訊層要發送的時候,如果通訊層報告發送失敗,那麼就需要執行下列的步驟:

 

首先,附件[4]的步驟需要執行,這就是說需要把應答發送一個備份的地點。如果這個也失敗了,基於[4]中對失敗的定義,服務端事務應當通知TU發送失敗,並且把狀態切換到終止狀態。

 

18 通訊(transport)

通訊層負責請求和應答在網絡上的實際傳輸。這包括了在面向連接的通訊方式下的請求和應答所使用的連接管理。

 

通訊層負責管理像TCP/SCTP之類通訊協議的長連接,或者在這些協議上的TLS連接,並且包括管理打開這些連接的使用者的管理。這包括了客戶端或者服務端通訊層打開的連接,這樣在客戶端服務端通訊函數可以共享這些連接。這些連接採用一組用遠端的地址,端口,通訊協議標誌的索引來進行管理。當通訊層打開了一個連接,這個連接的索引就設置成爲遠端的IP,端口,還有打開這個連接的通訊層的實例 。當通訊層接收了一個連接,那麼這個連接的索引就被設置成爲連接方的源IP地址,port,還有通訊層的實例transport。注意,由於源端口port通常是臨時創建的,但是由於通過附件[4]的步驟不能知道它是臨時創建的還是配置的,所以通訊層被動接收的連接通常是不被重複使用的。這就是說,如果兩個proxy再一個”peering”(點對點)的關係中,使用一個面向連接的通訊協議通常有兩個連接要使用,每個都是自己作爲主動方連接的。

 

我們建議在實現中,當發送(或者接收)完成最後一個消息之後,依舊維持這個連接一段時間(這段時間可以是實現自己定義的時間)。這段時間應當是至少等於本節點的事務從創建到結束的最長時間。這是爲了讓事務能夠在他們所創建的同一個連接上完成(比如,在這個連接上完成請求,應答的處理,在INVITE的情況下的給非2xx的ACK應答等等)。這通常意味着至少64×T1秒(參見17.1.1.1中關於T1的定義)。不過,如果當本程序的TU使用的是一個比較大的定時器C(參見16.6節11步)的時候,也可以選取一個比較大的值。

 

所有的SIP元素都必須實現基於UDP和TCP的通訊。SIP元素還可以實現其他的協議。

 

要求UA支持TCP是對RFC2543的一個重要改進。這是由於需要處理更大的消息,就像接下來講到的那樣,必須使用到TCP協議。因此,即使是SIP元素不要發送大的消息,但是由於它可能收到大消息並且處理這些消息,所以,要求支持TCP。

 

18.1 客戶Clients

18.1.1 發送請求

通訊層的客戶端負責發送請求和接收應答。通訊層的用戶把請求交給通訊層的實例進行處理,包括IP地址端口,通訊層實例,還有可能有多點廣播的TTL。

 

如果請求的大小和MTU差是在200個字節以內的,或者它是大於1300字節的,並且路徑MTU的大小是未知的,那麼請求必須遵循RFC2914[43]控制阻塞的傳輸協議,比如使用TCP。如果這導致了Via最上邊指定的通訊協議的改變,那麼Via最上邊的值就必須也隨之改變。這使得在UDP傳輸上的消息的分割,並且也提供了大消息的傳輸阻塞控制。不過,在實現上,必須能夠支持達到最大包大小的消息的處理。對於UDP來說,包含了IP和UDP頭的大小是65535個字節。

 

在消息的大小和MTU之間的200個字節的”buffer”,提供了一個機制使得在SIP的應答中,可以超過請求的大小。比如在INVITE請求的應答中,增加了Record-Route頭域值。有了這個額外的buffer,應答可以大概比請求大170個字節,而且在Ipv4上不用進行分塊傳輸(假設沒有IPSec,大概IP/UDP會使用30個字節)。當MTU是未知的時候,選取1300是基於假設Ethernet的MTU是1500字節的基礎上。

 

如果SIP元素是因爲消息大小的限制,所以基於TCP發送一個請求,並且消息如果不是因爲大小的限制,會使用UDP來發送,並且如果建立連接產生一個ICMP 協議不支持的錯誤,或者導致TCP reset,那麼這個元素就應當用UDP重試這個請求。這只是爲了向後兼容RFC 2543針對不支持TCP的實現。在本規範以後的改動中,這部分內容會有修訂。

 

如果客戶端向多個地址發送請求,那麼必須增加”maddr”參數到Via頭域值上,並且這個參數值指定多個目的地址,對於Ipv4來說,應當增加”ttl”參數=1,IPV6的多點傳送在本規範中沒有定義,會在後續的標準中描述。

 

這些規則定義了SIP的多點傳送。首要的目的是爲了提供”尋找最接近的單點”服務(”single-hop-discovery-like”),這個服務將請求轉發到一組類似的服務器,並且只需要處理其中任意一個服務器的應答。這個功能主要用於註冊服務。實際上,基於17.1.3的事務處理規則,客戶端事務會接收第一個應答,並且因爲其他應答包含同樣的Via的branch參數,而視這些應答爲重發應答。

 

在請求發送嵌,客戶端通訊層必須在Via頭域中增加一個”sent-by”欄。這個字段包含了一個IP地址或者主機名,端口。我們推薦使用FQDN方法描述這個主機名。這個字段在某些特定情況下,用於發送應答。如果端口不存在,缺省的值依賴於通訊協議。對於UDP,TCP和SCTP來說是5060,TLS是5061。

 

對於可靠傳輸協議,應答通常簡單的通過連接發送,並且這個連接是收到對應請求的連接。因此,客戶端傳輸層必須準備在發出請求的同一個連接上接收應答。在出現錯誤的情況下,服務端可能會嘗試新建立一個連接來發送應答。爲了能夠處理這種情況,通訊層必須準備接收一個從源IP建立的新連接,這個連接的IP是請求發起的源IP,port是在”sent-by”字段中指定的port。這也同樣要求準備接收從任意地址和端口來得新連接上接收應答,這個端口是由服務器根據附件[4]的5節所講述的步驟來選取的。

 

對於非可靠的傳輸協議,客戶端通訊層必須準備從發送請求的那個原始IP地址上接收應答。(因爲應答會送到原始地址去),並且端口號是在”sent-by”字段的端口好。進一步說,和可靠傳輸一樣,早某些情況下,應答會發往不同的地方。客戶端必須能夠準備從其他地址和端口上接收應答,這個端口是由服務器根據附件[4]的5節所講述的步驟來選取的。

 

對於多點傳送的情況來說,客戶端通訊層必須準備從相同的多點傳輸組上接收應答,這個組的地址和端口和發出請求的組相同(就是說,它必須是發送請求的那個多點傳輸組的一個成員)。

 

如果請求發送的目的IP地址,端口和transport都和現有的一個連接相同,那麼建議使用這個連接來發送請求,同時也允許新建立一個連接來發送。

 

如果請求通過多點發送,那麼它發送的一組地址,端口和TTL都是由通訊層的用戶提供。如果請求是通過不可靠通訊協議發送,那麼發送的IP地址和端口也是由通訊層的用戶提供。

 

18.1.2 接收應答

當應答接收到的時候,客戶端通訊層檢查最上的Via頭域值。如果”sent-by”參數不符合客戶端通訊層在請求中插入的值,那麼這個應答必須悄悄丟棄。

 

如果由任何客戶端事務存在,客戶端通訊層使用17.1.3的步驟來匹配現存的事務和這個接收到的應答。如果匹配到了,應答必須交給事務層進行處理。否則,應答必須交給核心去處理(無論是有狀態的proxy,還是無狀態的proxy,還是UA的核心)。處理這些”stray”(迷路)的應答是基於核心的策略的(如果是proxy就會轉發,如果是UA就會忽略,等等)。

 

18.2 服務端

18.2.1 接收請求

一個服務器應當能夠接收從任何IP地址、端口和協議上過來的請求。他們是通過對這個服務器的SIP或者SIPS URI(附件[4])的DNS查找,得到這個服務器的地址然後連接和發送的請求的。在這裏,”handing out”(發佈)包含了在REGISTER請求或者轉發應答的Contact頭域中放一個URI,或者在請求或者應答的”Record-Route”頭域中放一個URI。這個URI可以通過放在網頁或者名片上被”handing out”(發佈)。同樣的我們也建議服務器在公網上監聽缺省的SIP端口(TCP/UDP是5060,5061是在TCP上的TLS)。如果是在局域網上,或者私有網上,或者一個物理服務器上運行好幾個服務實例,那就很自然的可以設置成不同的。對於服務器監聽UDP的任何端口和界面,都必須在TCP上也進行同樣的監聽。這是因爲可能消息還需要通過TCP進行傳輸,比如消息過大的情況。所以,在相反的情況下就不需要了。如果一個服務器在TCP監聽了,那麼它不一定需要在UDP上也進行相應的監聽。當然服務器也可以因爲某些原因在特定地址和端口上監聽UDP。當服務端事務從任意一個通訊層上接收到一個請求的時候,它必須檢查最上的Via頭域的”sent-by”參數。如果”sent-by”參數的主機部分包含了一個主機名,或者它包含的IP地址和包的源地址不同,服務器必須增加一個”received”參數到這個Via頭域值中。這個參數必須包含收到的包的原地址。由於服務端必須把應答發送給收到請求的那個源IP地址,所以這個可以用來幫助服務端通訊層發送應答。

 

一個服務端通訊層收到的請求可能是這樣的(部分):

 

INVITE sip:[email protected] SIP/2.0

Via: SIP/2.0/UDP bobspc.biloxi.com:5060

 

請求是從源IP:192.0.2.4收到的。在請求轉交到上層之前,通訊層增加了一個”received”參數,這樣請求的部分就是:

INVITE sip:[email protected] SIP/2.0

Via: SIP/2.0/UDP bobspc.biloxi.com:5060;received=192.0.2.4

 

接着,服務端通訊層嘗試和服務端事務做匹配。這個使用的是17.2.3節定義的規則。如果匹配上一個服務端事務,那麼請求就交給那個事務去處理。如果沒有匹配到事務,請求就交給核心去處理,可能會創建一個新的服務端事務來處理。注意當UAS核心給INVITE請求發送一個2xx應答的時候,服務端事務已經銷燬了。這就是說,當ACK收到的時候,不會有匹配的服務端事務,並且基於這個規則,ACK回交給UAS核心來處理。

 

18.2.2 發送應答

服務端事務使用最上邊的Via頭域值來決定把應答發送到哪裏。它必須遵從如下步驟來發送:

o 如果”sent-protocol”是一個可靠的傳輸協議比如TCP或者SCTP,或者在其上的TLS,應答必須用現存的到原始請求(創建這個事務的請求)的連接進行發送(如果連接還存在的情況下)。這個要求服務端通訊層保留服務端事務和通訊層連接的相關性。如果連接不存在了,服務端應當創建一個新的連接,如果存在”received”參數,就用對應的在”received”參數中指定的IP地址。如果存

 

 

小虎 2006-05-25 00:10

在”sent-by”參數,那麼就用”sent-by”指定的port,如果不存在,那麼就用缺省的port。如果對應的連接已經失效,那麼服務器應當採用附件[4]的步驟來決定使用那個IP地址和端口來建立連接並且發送應答。

 

o 否則,如果Via頭域包含一個”maddr”參數,就必須把應答轉發到maddr所指明的地址,並且使用”sent-by”所指定的端口,如果沒有sent-by參數,那麼就使用5060缺省參數。如果地址是一個多點地址,應答應當使用”ttl”參數所指定的TTL,或者如果沒有指定”ttl”參數,則使用TTL=1的參數。

 

o 否則(對於非可靠傳輸),如果Via的最上頭域包含一個”received”參數,那麼應答必鬚髮送到”received”參數所指定的地址,並且使用”sent-by”所指定的端口,如果沒有sent-by參數,那麼就使用5060缺省參數。如果這步失敗了,比如,如果得到一個ICMP”端口不能到達”的錯誤,那麼就應當根據附件[4]的第5節的步驟來決定應當把應答發送到哪裏。

 

o 否則,如果沒有receiver-標記,那麼應答應當使用附件[4]的第5節指定的步驟,送到”sent-by”參數指定的地址。

 

18.3 分塊

在面向消息的通訊協議中(比如UDP),如果消息有一個Content-Length頭域,那麼消息體就有可能包含很多字節。並且收到的包中除了這個消息體的Content-Length字節意外,還有通訊層附加的通訊包字節,那麼這部分額外的字節應當被丟棄。如果通訊包在沒有收到完整的Content-Length字節的消息體就終止了,這就意味着出錯了。如果這個消息是一個應答,那麼這個消息必須被丟棄。如果消息是一個請求,那麼本程序應當給出一個400(Bad Request)應答。如果消息沒有包含一個Content-Length頭域,消息體的結束點就是消息體的結束點。

 

在面向流的通訊協議中(比如TCP),Content-Length頭域標誌這包體的大小。在面向流的通訊協議中,必須使用Content-Length字段。

 

18.4 錯誤處理

錯誤的處理取決於出現錯誤的消息是請求還是應答。

 

如果通訊層的用戶要求在一個非可靠傳輸協議上發送一個消息,並且結果是一個ICMP錯誤,那麼錯誤處理的方法依賴於ICMP錯誤類型。當通訊層遇到主機、網絡、端口或者協議無法到達的錯誤,或者參數錯誤的時候,應當通知通訊層的用戶發送失敗。Source quench和TTL exceeded ICMP錯誤應當被忽略。

 

如果通訊層用戶要求在一個可靠傳輸協議上發送一個請求,並且結果是一個連接錯誤,通訊層應當通知通訊層用戶這個發送錯誤

 

19 常見消息部件(Common Message Components)

在SIP消息中,有一些很長用的部件。(甚至在SIP消息外這些部件也存在)。這些部件值得我們單獨討論一下。

 

19.1 SIP和SIPS統一資源標記

SIP或者SIPS 的URI用來標記一個通訊用的資源。就像其他所有的URI一樣,SIP和SIPS URI可以放在網頁上,email消息裏,或者打印出來的名片上等等。在這些URI裏邊包含了足夠的信息來發起和維持到這個資源的一個通訊會話。

 

一個通訊資源的例子包含下列內容:

 

o 一個在線服務的用戶

o 一個多線電話

o 消息系統中的郵箱

o 網關服務的PSTN電話號碼

o 一個組織中的一個部門(比如”銷售”,或者”helpdesk”)

 

SIPS URI定義了對資源的訪問是安全的。這就意味着,特別是,在UAC和這個資源的主機之間的通訊是基於TLS的。從資源的主機到用戶之間的通訊是加密安全的,這個安全機制是依賴於主機的實現的。任何用SIP URI描述的資源,只要想通過加密的形式進行通訊,都可以通過簡單改變一下資源描述府就可以”升級”成爲一個SIPS URI。

 

19.1.1 SIP和SIPS部件

“sip:”和”sips:”描述符是遵循RFC2396[5]的規範定義的。他們使用類似mailto URL的格式定義,允許有SIP請求頭域字段和SIP消息體的規範。這使得在網頁上或者email中,可以用URI來初始化一個會話,這個會話有特定的主題,媒體類別,緊急類型。這個SIP或者SIPS URI的格式規範在25節定義。一個SIP URI的通常格式是這樣的:

 

sip: user:password@host:port;uri-parameters?headers

 

這個和SIPS URI的格式是相同的,只是SIPS用”sips”來代替sip。這些符號,和符號的擴展,具有下列意義:

 

user: 這是在主機的特定資源地址。”主機”(host)在這裏通常指的是一個域名。URI中的”userinfo”包含了這個用戶域,口令域,並且包含其後的一個@。URI的用戶信息部分是可選的,或者說是可以沒有的;當目的主機沒有用戶的概念或者主機本身就是資源的目標,那麼這個URI的用戶信息部分就是可以沒有的。如果在SIP或者SIPS URI中有@,那麼用戶部分必須不能爲空的。如果主機部分可以處理電話號碼地址,比如說是一個internet電話網關,那麼根據RFC2806[9]定義的電話號碼域應當出現在用戶信息部分。在19.1.2節有關於在SIP或者SIPS URI中的電話號碼描述域的額外說明

 

password:password字段是和用戶相關的。SIP或者SIPS URI語法允許增加password這個字段,這種用法我們是不推薦的,因爲把身份認證信息放在明碼錶示的地方(比如URI)會帶來很大的安全風險。比如,通訊層在這個字段用了一個PIN碼,那麼就會暴露這個PIN碼而帶來安全隱患。

 

注意密碼字段只是一個用戶信息擴展的一部分。實現上並沒有標記一個特別的密碼部分,可以簡單的把”user:password”當作一個簡單的用戶串來對待。

 

host:主機提供了SIP資源。host部分包含了一個完整的主機名字或者IPV4/IPV6的地址。我們強烈建議如果可能,就使用完整格式的主機名字。

 

port: 端口號是請求將被送出的端口。

 

URI 參數:請求將使用這個URI來構造。

 

URI參數在hostport部件之後增加,用分號分開。

URI參數有如下格式:

 

參數名’=’參數值

 

雖然在同一個URI中允許有任意多個URI的參數,但是同一個參數名只能出現1次。

 

這個擴展機制包括了transport,maddr,ttl,user,method和lr參數

 

transport參數決定在[4]中定義的發送SIP消息的通訊機制。SIP可以使用任何網絡通訊協議。參數名字是爲UDP(RFC 768[14]),TCP(RFC 761 [15])和SCTP(RFC2960[16])定義的。對於一個SIPS URI,transport參數必須指向一個可靠的通訊協議。

 

maddr參數指明瞭聯繫這個用戶的服務器的地址,它會覆蓋在host域中的地址。當給定了一個maddr參數,URI中的port和transport部件將會在maddr中指出。[4]描述了正確的transport,maddr,hostport規範,用於獲得發送請求到目的地所需要的目的地址,端口,通訊協議。

 

maddr字段用作簡單的去掉源路由的方法來使用的。它允許一個URI指定一個必須經過的proxy來到達目的地。我們強烈建議不要把maddr參數用於這個目的(我們反對把maddr用於這個機制)。在實現上應當使用本文中描述的Route機制,如果有需要,則建立一個pre-existing(預先設置的)路由集合(參見8.1.1.1)。它提供了一個完整的URI來描述需要經過的節點。

 

ttl參數決定了UDP多點報文的生存週期,並且只能用於maddr是一個多點地址並且通訊協議是UDP的情況。例如,爲了指定一個到[email protected]的呼叫,使用多點廣播到239.255.255.1,並且ttl=15,那麼應該使用下邊的一個URI:

 

sip:[email protected];maddr=239.255.255.1;ttl=15

 

有效的電話描述(telephone-subscriber)的字串一個集合是有效的用戶字串的子集。我們用用戶URI參數來區別電話號碼和用戶名(長得像電話號碼的用戶名)。如果用戶串使用了電話號碼描述的字串,用戶參數值”phone”應當增加。即使沒有這個參數,如果本地用戶名的命名限制機制允許的情況下,SIP和SIPS URI的接受方也可以把這個@以前的部分解釋爲電話號碼。

 

從URI中構建SIP請求所需要的method域,可以由method參數指定。

 

如果指定了lr參數,就標誌着這個資源的擁有者是根據本規範來實現的路由機制。這個參數回用於proxy放在Record-Route頭域的URI中,也可以出現在pre-existing(預先設置)的路由集合中。

 

這個參數是用來和RFC2543定義中的嚴格路由機制向後兼容所使用的,並且rfc2543bis 改變爲bis-05。如果一個元素準備發送一個基於沒有包含這個參數的URI請求,那麼我們可以假定這個請求的接受方是根據嚴格路由的規範實現的,並且會重新規格化這個消息來保護在Request-URI中的內容。

 

由於URI參數機制是可以擴展的,SIP元素應當悄悄跳過那些不認識的uri參數。

Headers:頭域是從給定URI創造的請求的頭域部分。

在SIP請求中的頭域可以在URI中用”?”來給出。頭域名(hname)和頭域值(hvalue)都是用&符號間隔的頭域名=頭域值的格式。特定的頭域名”body”的頭域值就是SIP請求的消息體。

 

表1總結了在URI的不同情況下SIP和SIPS URI的部件用法。擴展的列描述了在SIP消息歪的URI,例如在網頁上或者名片上的情況。項目中的’m’是強制必須的意思,’o’是可選的,’-‘是不允許的。處理URI的元素應當忽略掉URI中出現的任何不允許的部件。在表格中的第二列是如果該元素不存在的時候的缺省值。’--'表示本元素不是可選的,或者沒有缺省值的意思。

 

在Contact頭域中的URI在頭域出現的不同地方有着不同的約束。一個是在消息建立和維持一個對話的時候(INVITE請求以及它對應的200(OK)應答),一個是在註冊和轉發消息的時候(REGISTER,以及對應的200(ok)應答,以及給任何方法的3xx系列的應答)

19.1.2 Character Escaping Requirements(字符轉碼要求)

   default    Req-URI    To    From    reg./redir.Contact    dialogContactR-R/Route    external

user    - -    o    o    o    o    o    o

password    - -    o    o    o    o    o    o

host    - -    m    m    m    m    m    m

port    (1)    o    -    -    o    o    o

user-param    ip    o    o    o    o    o    o

method    INVITE    -    -    -    -    -    o

maddr-param    - -    o    -    -    o    o    o

ttl-param    1    o    -    -    o    -    o

transp.-param    (2)    o    -    -    o    o    o

lr-param    - -    o    -    -    -    o    o

other-param    - -    o    o    o    o    o    o

headers    - -    -    -    -    o    -    o

 

(1):缺省的通訊端口是依賴於通訊協議的。對於使用UDP,TCP,SCTP的sip來說,是5060,對於使用基於TCP的TLS來說,是5061。

(2)缺省的通訊協議是和sip/sips相關的,對於sip來說,是UDP,對於sips來說,是TCP。

 

表1:對於SIP頭域值,Request-URI及其引用的使用和缺省值。

 

基於RFC2396[5]的要求和指引,當需要把字符串封裝到SIP URI的時候,使用””%” HEX HEX”機制來進行轉碼。根據RFC2396[5]:

 

任何指定URI部件保留的字符集都是由這個部件定義的。通常,如果URI的語義由於組成字符被它的US-ACII編碼[5]的escape碼替代而改變的時候,這個字符就是保留字符。除了USASCII字符(RFC2396[5])之外,比如空格和控制字符,以及URI所使用的分隔符,必須進行轉碼。URI必須不能包含任何未經轉碼的空白和控制字符。

 

對於每一個部件來說,由BNF擴展的合法字符集合規定了那些字符是可以不經轉碼的。其他字符都必須經過轉碼。

 

比如,”@”不是user部件的字符集中的字符,所以,userj@sOn,必須把@符號進行編碼,成爲”j%40sOn”

 

在25節中的hname和hvalue的符號展示了在URI的保留字符中,在頭域名和頭域值中所有需要被轉碼的字符集合。

 

user部件的電話描述(telephone-subscriber)部分由特別的轉碼考慮。在RFC2806[9]關於電話描述部分中未被保留的字符集,由很多字符組成,他們在SIP URI中的不同語法部分的時候,都需要做轉碼。在電話描述中出現的任何字符,只要不在BNF針對user部分的擴展規則中出現的,都需要做轉碼。

 

注意在SIP或者SIPS URI中,host部分不允許做字符的轉碼(%不在它的擴展部分中)。在以後的Inernationalized Domain Names完成以後,這個限制可能就會改了。當前實現中不允許把在host部分收到的轉碼字符進行轉碼處理。因爲這個轉碼處理和IDN要求的處理不一樣。

 

19.1.3 SIP和SIPS URI例子

sip:[email protected]

sip:alice:[email protected];transport=tcp

sip:[email protected]?subject=project%20x&priority=urgent

sip:+1-212-555-1212:[email protected];user=phone

sips:[email protected]

sip:[email protected]

sip:atlanta.com;method=REGISTER?to=alice%40atlanta.com

sip:alice;[email protected]

 

最後一個URI例子有一個user域”alice;day=Tuesday”。上邊定義的轉碼規則中允許”;”在這個字段中不進行轉碼。在本協議的設計概念中,這個字段是不透明的。這個字段的值只對負責這個資源的SIP元素有用。

 

19.1.4 URI比較

在本規範中,部分操作需要比較兩個SIP或者SIPS URI是否相等。比如,在這個規範中,註冊服務器需要比較在REGISTER 請求中綁定的Contact URI(參見10.3)。SIP和SIPS URI根據如下步驟進行比較:

 

o SIP和SIPS URI永遠不等。

o SIP/SIPS URI的userinfo是大小寫敏感的。這包括了含有password或者按照電話描述格式的userinfo的比較。對於URI的其他部分的比較,除了有特別指出之外,都是大小寫不敏感的。

o 參數的順序和頭域的順序對於比較SIP/SIPS URI不起作用。

o 在保留字符集之外的字符(參見RFC2396[5]),等同於他們的””%” HEX HEX”格式。

o IP地址就算是等同於通過DNS查找到的主機名對應的IP地址,IP地址也不能和主機名等同。

o 兩個URI如果相同,那麼user,password,host,port部分必須相同。

 

有user部分的URI和沒有user部分的URI是不相等的。有password部分的URI和沒有password部分的URI也是不同的。

 

一個不帶可選部件的URI和帶了這些部件但是值是缺省值的URI是不等的。例如,如果一個URI省略了port部件,並不等於一個定義了5060port部件的URI。同樣的規則適用域transport-參數,ttl-參數,user-參數,method部件等等。

 

定義sip:user@host,和定義sip:user@host:5060(根據RFC2543的變體)不相等。當從URI中取得地址的時候,相同的URI可以取得相同的地址。sip:user@host:5060始終可以得到端口5060。URI: sip:user@host根據[4]所定義的DNS SRV機制,可能可以得出其他的端口來。

 

o URI uri參數部件按照如下規則進行比較

-    任何在兩個URI中出現的uri參數都必須一樣

-    user,ttl,或者方法uri參數如果只在一方出現,即使和缺省值相等,也判定爲兩個URI不相等。

-    包含maddr參數的URI和沒有包含maddr參數的不相等。

-    其他uri參數,如果在一方出現,則在比較的時候忽略。

 

o URI頭部件的比較是不能忽略的。任何在header部分出現的域都必須在雙方URI中進行匹配和比較。比較規則參見20節。

 

下列URI是相等的:

sip:%[email protected];transport=TCP

sip:[email protected];Transport=tcp

 

sip:[email protected]

sip:[email protected];newparam=5

sip:[email protected];security=on

 

sip:biloxi.com;transport=tcp;method=REGISTER?to=sip:bob%40biloxi.com

sip:biloxi.com;method=REGISTER;transport=tcp?to=sip:bob%40biloxi.com

 

sip:[email protected]?subject=project%20x&priority=urgent

sip:[email protected]?priority=urgent&subject=project%20x

 

下列URI是不相等的:

 

SIP:[email protected];Transport=udp         (用戶名不同)

sip:[email protected];Transport=UDP

 

 

sip:[email protected]                (端口不同)

sip:[email protected]:5060

 

sip:[email protected]            (通訊協議不同)

sip:[email protected];transport=udp

 

sip:[email protected]            (通訊協議和端口不同)

sip:[email protected]:6000;transport=tcp

 

sip:[email protected]        (header部件不同)

sip:[email protected]?Subject=next%20meeting

 

sip:[email protected]        (就算是phone21.boxesbybob.com

sip:[email protected]            解析到192.0.2.4也不能算相等的。)

 

注意相等性是不能傳遞的。

比如 sip:[email protected] 和sip:[email protected];security=on相等

sip:[email protected] 和 sip:[email protected];security=off相等

但是:

sip:[email protected];security=on和sip:[email protected];security=off不等。

 

19.1.5 從URI中產生請求

對於實現而言,需要能夠直接從一個URI來構造請求。URI可以是從名片,網頁,或者甚至從某些協議內部得到(比如登記的聯繫信息等等)。

 

協議的實現必須包括構造請求的Request-URI中的transport,maddr,ttl,或者user參數。如果URI包含了method參數,那麼它的值必須和構造的請求的方法一樣。並且method參數不能放在Request-URI中。不認識的URI參數必須放在消息的Request-URI中。

 

實現中應當把URI中出現的header或者包體部分包含入消息本身,並且當作是請求自己的組成部分。

 

在實現中,不應當保留那些明顯危險的頭域字段:From,Call-ID,Cseq,Via和Record-Route。

 

並且實現中,也不應當保留任何請求的Route頭域值,這樣可以避免無知的客戶端進行惡意攻擊。

 

實現中也不應當保留那些可能會導致錯誤登記地址或者誤導能力的頭域字段,這些包括:Accept,Accept-Encoding,Accept-Language,Allow,Contact(在對話中使用),Organization,Supported,和User-Agent。

 

實現上應當檢查每一個請求中所描述的頭域的正確性,包括:Content-Disposition, Content-Encoding,Content-Language, Content-Length, Content-Type, Date, Mime-Version, Timestamp。

 

如果從給定URI構造的請求不是一個合法的SIP請求,那麼這個URI就是非法的URI。實現上禁止處理和傳送非法的SIP請求。它應當嘗試追查爲何會有一個非法的URI。

 

很多情況都可以得到一個非法的請求。這包括但是不限於,頭域的語法錯誤,非法的URI參數合併,或者錯誤的消息體描述等等。

 

發送從URI構造的請求可能會導致實現上的能力不夠。比如:URI可能指定了尚未實現的通訊協議或者通訊擴展。那個這個具體的實現上來說,應當拒絕發送這些請求,而不是修改這個請求來適應具體實現的處理能力。對於具體實現來說,它不能發送包含它自己不能理解的擴展部分的請求。

 

比如,從一個包含了未知的或者擺明了不支持的Request頭域參數或者method參數的URI中,構造的請求就是不能發送的。

 

19.1.6 關聯SIP URI和tel URL

如果tel URL(RFC 2806[9])轉換成爲一個SIP或者SIPS URI,那麼tel URL的整個電話描述(telephone-subscriber),機器參數,都需要放在SIP或者SIPS URI的userinfo部分。

 

因此:tel:+358-555-1234567;postd=pp22 會變成:

sip:+358-555-1234567;[email protected];user=phone

或者

sips:+358-555-1234567;[email protected];user=phone

而不是

sip:[email protected];postd=pp22;user=phone

或者

sips:[email protected];postd=pp22;user=phone

 

通常來說,相等的”tel”URL轉換成爲SIP或者SIPS URI以後,不一定能得到相同的SIP或者SIPS URI。因爲SIP和SIPS URI的userinfo部分是根據大小寫敏感的字串。由大小寫不敏感的tel URL以及重新排序的tel URL參數並不改變tel URL的相等性,但是在轉換成爲SIP或者SIPS URI之後,卻影響了他們的相等性。

 

例如:

tel:+358-555-1234567;postd=pp22

tel:+358-555-1234567;POSTD=PP22

是等價的,但是

sip:+358-555-1234567;[email protected];user=phone

sip:+358-555-1234567;[email protected];user=phone

卻是不等價的。

 

類似的:

tel:+358-555-1234567;postd=pp22;isub=1411

tel:+358-555-1234567;isub=1411;postd=pp22

是等價的,但是

sip:+358-555-1234567;postd=pp22;[email protected];user=phone

sip:+358-555-1234567;isub=1411;[email protected];user=phone

卻不等價

 

爲了避免這個問題,在構造放在SIP或者SIPS URI中的userinfo部分的電話描述域的時候,應當轉換大小寫不敏感的電話描述域爲小寫,並且除了isdn-subaddress和post-dial,把電話描述的參數按照參數名進行排序, 因爲他們需要按順序出現在參數的第一個。(在下邊是除了未來擴展參數意外的全部tel URL大小寫不敏感的部分)。

 

根據上邊的描述,全部:

tel:+358-555-1234567;postd=pp22

tel:+358-555-1234567;POSTD=PP22

 

轉換成爲

sip:+358-555-1234567;[email protected];user=phone

 

並且全部:

tel:+358-555-1234567;tsp=a.b;phone-context=5

tel:+358-555-1234567;phone-context=5;tsp=a.b

轉換成爲:

sip:+358-555-1234567;phone-context=5;[email protected];user=phone

 

19.2 Option Tags

Option tags是一個唯一標誌,用來指明SIP中的新options(擴展)的。這些tags在Require(20.32節),Proxy-Require(20.29節),Supported(20.37節)和Unsupported(20.40節)頭域中使用。注意這些options是以option-tag=的形式作爲這些頭域的參數存在的(25節有關定義符號)。

 

Option tags是根據標準的RFC擴展定義的。這是和過去的試驗有所不同,這是協會爲了保證多個廠商之間能夠持續互相協作(20.32節、20.37節的討論)。option tags的IANA註冊可以保證查找很容易。

 

19.3 Tags

“tag”參數用於SIP消息中的To和From頭域。它作爲一個通用的機制的一部分來唯一標誌一個對話,這個機制用Call-ID和兩個從對話參與者的tag來標誌一個對話。當UA在對話外發出一個請求時,它只包含了From tag,提供了對話ID的”一半”。對話根據應答創建完成,這個應答在To頭域中提供了對話ID的另一半。SIP請求的分支意味着一個單個請求可以創建多個對話。這個也解釋了爲何需要對話兩方的標誌;如果沒有被叫方的標誌,呼叫方不能分辯和消除由單個請求創建的多個對話。

 

當UA產生一個tag並且增加進一個請求或者應答的時候,它必須是一個全局唯一的,並且是密碼隨機數起碼是32位的隨機數。這個要求是爲了讓UA能夠在同一個INVITE請求中,在給這個INVITE的應答中,在To頭域產生一個不同的tag,和原始INVITE請求在From頭域中產生的tag不同。這是因爲UA可以邀請自己到一個會話,常見的是在PSTN網關的”hairpinning”(髮夾)呼叫。類似的,對不同呼叫的兩個INVITE也有不同的From tag,並且給這兩個呼叫的兩個應答也有不同的To tag。

 

在全局唯一要求之外,產生tag的算法是實現相關的。Tag對於容錯系統比較有用,在容錯系統下,當主服務器故障的時候,對話會在另外一個服務器上進行恢復。UAS可以產生一個tag,讓備用服務器能夠認識到這個請求是在故障服務器上的對話,並且能夠決定是否恢復對話和對話相關的狀態。

 

20 頭域

頭域的語法描述在7.3節。本節列出了頭域的全部列表,包括了語法註釋,含義,和用法。通過本節,我們使用[HX.Y]指當前HTTP/1.1 的RFC2616[8]的規範的X.Y節。每個頭域都有示例給出。

 

關於與方法和proxy處理有關的頭域字段在表2和表3中有處理。

 

“where”列描述了在頭域中能夠使用的請求和應答的類型。這列的值是:

R:頭域只能在請求中出現;

r:頭域只能在應答中出現;

2xx,4xx,等等:一個數字的值區間表示頭域能夠使用的應答代碼。

c:頭域是從請求拷貝到應答的。

如果”where”欄目是空白,表示頭域可以在所有的請求和應答中出現。

 

“proxy”列描述了proxy在頭域上的操作

a:如果頭域不存在,proxy可以增加或者連接頭域

m:proxy可以修改現存的頭域值

d:proxy可以刪除頭域值

r:proxy必須能讀取這個頭域,因此這個頭域不能加密。

 

接下來6個欄目與在某一個方法中出現的頭域有關:

c:條件;對頭域的要求依賴於消息的內容

m:頭域是強制要有的。

m*:頭域應當被髮送,但是客戶端/服務端都需要準備接收沒有這個頭域的消息。

o:頭域是可選的。

t:頭域應當被髮送,但是客戶端/服務端都需要準備接收沒有這個頭域的消息。客戶端/服務端都需要準備接收沒有這個頭域的消息。如果通訊的協議是基於面向流的協議(比如TCP),那麼頭域值必須被髮送。

*:如果消息體不爲空,那麼頭域值就緒要的。(細節請參見20.14,20.15和7.4節)

-:這個頭域是不適用的。

 

“Optional”意味着這個元素可以在請求或者應答中包含這個頭域,並且UA可以忽略在請求或者應答中存在的這個頭域(這條規則有一個例外,就是Require頭域,在20.32節有描述)。”mandatory”(強制)頭域是必須在請求中存在的頭域,並且也必須是UAS接收到一個請求時能夠理解的頭域。一個強制頭域必須也在應答中出現,並且UAC也能處理這個頭域。”Not applicable”(不適用)意味着頭域不能在請求中出現。如果一個UAC錯誤的把這個頭域放在請求中,在UAS收到的時候必須被忽略。同樣的,如果應答中的”不適用”的頭域,也就是說UAS不能在應答中放置的頭域,如果出現了,那麼UAC也必須在應答中忽略掉這個頭域。

 

一個UA必須忽略他們所不能處理的擴展的頭參數。

 

本規範也定義了常用的頭域名的縮寫,用於縮小消息的大小。

 

在Contact,From,To頭域中都包含一個URI。如果這個URI包含一個逗號,問號或者分毫,那麼這個URI必須使用尖括號括起來(<和>)。所有的URI參數都必須在這些括號內。如果URI並非用尖括號括起來的,那麼用分號分開的參數將被視同與header參數而不是URI參數。

 

20.1 Accept

Accept頭域的語法定義遵從[H14.1]。除了如果沒有Accept頭域,服務器應當認爲Accept缺省值是application/sdp以外,語義也是和HTTP/1.1類似的語義。

 

空的Accept頭域意味着不接受任何格式。

 

 

小虎 2006-05-25 00:10

Header field    where    proxy    ACK    BYE    CAN    INV    OPT    REG

Accept    R        -    o    -    o    m*    o

Accept    2xx        -    -    -    o    m*    o

Accept    415        -    c    -    c    c    c

Accept-Encoding    R        -    o    -    o    o    o

Accept-Encoding    2xx        -    -    -    o    m*    o

Accept-Encoding    415        -    c    -    c    c    c

Accept-Language    R        -    o    -    o    o    o

Accept-Language    2xx        -    -    -    o    m*    o

Accept-Language    415        -    c    -    c    c    c

Alert-Info    R    ar    -    -    -    o    -    -

Alter-Info    180    ar    -    -    -    o    -    -

Allow    R        -    o    -    o    o    o

Allow    2xx        -    o    -    m*    m*    o

Allow    r        -    o    -    o    o    o

Allow    405        -    m    -    m    m    m

Authentication-Info    2xx        -    o    -    o    o    o

Authorization    R        o    o    o    o    o    o

Call-ID    c    r    m    m    m    m    m    m

Call-Info        ar    -    -    -    o    o    o

Contact    R        o    -    -    m    o    o

Contact    1xx        -    -    -    o    -    -

Contact    2xx        -    -    -    m    o    o

Contact    3xx    d    -    o    -    o    o    o

Contact    485        -    o    -    o    o    o

Content-Disposition            o    o    -    o    o    o

Content-Encoding            o    o    -    o    o    o

Content-Language            o    o    -    o    o    o

Content-Length        ar    t    t    t    t    t    t

Content-Type            *    *    -    *    *    *

Cseq    c    r    m    m    m    m    m    m

Date        a    o    o    o    o    o    o

Error-Info    300-699    a    -    o    o    o    o    o

Expires            -    -    -    o    -    o

From    c    r    m    m    m    m    m    m

In-Reply-To    R        -    -    -    o    -    -

Max-Forwards    R    amr    m    m    m    m    m    m

Min-Expires    423        -    -    -    -    -    m

MIME-Version            o    o    -    o    o    o

Organization        ar    -    -    -    o    o    o

表2: 頭域概覽,A-O

 

 

Header field    where    proxy    ACK    BYE    CAN    INV    OPT    REG

Priority    R    ar    -    -    -    o    -    -

Proxy-Authenticate    407    ar    -    m    -    m    m    m

Proxy-Authenticate    401    ar    -    o    o    o    o    o

Proxy-Authorization    R    dr    o    o    -    o    o    o

Proxy-Require    R    ar    -    o    -    o    o    o

Record-Route    R    ar    o    o    o    o    o    o

Record-Route    2xx,18x    mr    -    o    o    o    o    -

Reply-To            -    -    -    o    -    -

Require        ar    -    c    -    c    c    c

Retry-After    404,413,480,486        -    o    o    o    o    o

Retry-After    500,503600,603        -    o    o    o    o    o

Route    R    adr    c    c    c    c    c    c

Server    r        -    o    o    o    o    o

Subject    R        -    -    -    o    -    -

Supported    R        -    o    o    m*    o    o

Supported    2xx        -    o    o    m*    m*    o

Timestamp            o    o    o    o    o    o

To    c(1)    r    m    m    m    m    m    m

Unsupported    420        -    m    -    m    m    m

User-Agent            o    o    o    o    o    o

Via    R    amr    m    m    m    m    m    m

Via    rc    dr    m    m    m    m    m    m

Warning    r        -    o    o    o    o    o

WWW-Authenticate    401    ar    -    m    -    m    m    m

WWW-Authenticate    407    ar    -    o    -    o    o    o

表3:頭域概覽,P-Z (1)和可能的附加tag一起拷貝。

 

例子:

Accept: application/sdp;level=1,application/x-private,text/html

 

20.2 Accept-Encoding

Accept-Encoding頭域類似Accept,但是限定了接收應答中的內容的編碼[H3.5]。參見[H14.3]。在SIP中的語義和在[H14.3]中的定義是一致的。

 

一個空的Accept-Encoding頭域是允許的。他等同於Accept-Encoding:identity,這就是說,只有identity編碼,也就是說沒有編碼的情況,是允許的。

 

如果沒有Accept-Encoding頭域存在,那麼服務端應當使用缺省值:identity。

 

這個和HTTP的定義略有不同,HTTP指出如果本頭域不存在,那麼任何編碼形式都可以使用,只是推薦identity編碼而已。

 

例如:

Accept-Encoding:gzip

 

20.3 Accept-Language

Accept-Language頭域用來在請求中指定首選的的語言的,這個首選的語言是在應答中的消息體中的的原因分析,會話描述,或者狀態報告的。如果沒有Accept-Language存在,那麼服務端應當假設所有的語言客戶端都可以接受。

 

Accept-Language頭域遵從[H14.4]節定義的語法。對於SIP來說,也同樣支持對語言通過”q”參數來進行排序。

 

例如:

 

Accept-Language: da, en-gb; q= 0.8, en;q=0.7

 

20.4 Alert-Info

當INVITE請求有一個Alert-Info頭域的時候,Alert-Info頭域就包含的是給UAS的一個額外的信息。當在180(Ringing)應答中出現的時候,Alter-Info頭域給出了UAC一個額外的回鈴信息。這個頭域的一個典型用法就是讓proxy增加這個頭域用來體哦你嘎一個與衆不同的振鈴效果。

 

Alter-Info頭域可能會帶來潛在的安全隱患。這個隱患以及相應的處理在20.9節有講述,這個隱患和Call-Info頭域的隱患是相同的。

 

另外,用戶應當可以有選擇的屏蔽這個特定。

 

這個可以保護用戶不因爲使用了未受信任節點發送過來的這個頭域而導致的破壞。

例如:

Alter-Info: <http://www.example.com/sounds/moo.wav>

20.5 Allow

Allow頭域列出了UA支持的方法列表。

 

如果要提供UA頭域,那麼所有隻要是UA支持的方法,包括ACK和CANCEL都必須列在這個Allow頭域中。如果沒有Allow頭域出現,一定不能以爲UA什麼方法都不支持。應當解釋成爲發送這個消息的UA並沒有告訴大家它支持什麼方法。

 

在應答中提供Allow頭域比在OPTIONS請求/應答中會減小所需要的消息數量。

 

例如:

Allow: INVITE,ACK,OPTIONS,CANCEL,BYE

 

20.6 Authentication-Info

Authentication-Info 頭域提供了和HTTP類別相同的認證方法。UAS可以在給一個順利通過認證的請求的2xx應答中包含這個頭域,並且是使用基於Authorization頭域的分類。

 

這個頭域的語法和語義遵循RFC2617[17]的規範。

 

例如:

 

Authentication-Info: nextnonce=”47364c23432d2e131a5fb210812c”

 

20.7 Authorization

Authorization頭域包含了了UA進行認證的信任書。22.2節概述了對Authorization頭域的用法,22.4節講述了和HTTP 認證一起使用的時候的語法和語義。

 

這個頭域,和Proxy-Authorization,並不遵循通常的多頭域值的規則。雖然它不是由逗號分割的列表,這個頭域名可以出現多次,並且不能應用7.3節的規則合併成爲單個頭域。

 

在下邊的例子中,在分類參數兩邊沒有引號括起來。

 

Authorization:Digest username=”Alice”, realm=”atlanta.com”, 

nonce = ”84a4cc6f3082121f32b42a2187831a94”,

response=”7587245234b3434cc3412213e5f113a5432”

 

20.8 Call-ID

Call-ID頭域用來唯一區別一個特定的邀請或者一個特定客戶端的所有註冊項。單個多媒體會議可以分解成爲多個不同Call-ID的呼叫,例如,當一個用戶數次邀請單個個體加入同一個會議的時候。Call-ID是大小寫敏感的並且是字節/字節比較的。

 

Call-ID頭域的簡寫就是i

 

例子:

Call-ID: [email protected]

i:[email protected]

 

20.9 Call-Info

Call-Info頭域提供了對呼叫方或者被叫方的附加信息,如果出現在請求中則是呼叫方的信息,如果出現在應答中則是被叫方的。”purpose”參數中存放了效果圖URI。”icon”參數包含了一個呼叫方或者被叫方的圖標。”info”參數描述了簡要的呼叫方或者被叫方的信息,例如,通過放置一個網頁進行介紹等。”card”參數提供了一個名片,比如,基於vCard[36]或者LDIF[37]格式。如果附加新的標記,那麼可以通過27節描述的步驟通過在IANA註冊來附加。

 

對Call-Info的使用可能會帶來一些安全隱患。如果一個被叫方接到一個惡意呼叫方提供的URI,被叫方可能會由顯示一個不合適的內容,或者危險的或者非法的內容,等等。因此,我們建議UA只顯示那些它能夠檢驗並且信任發送方身份的Call-Info頭域中的內容。這個對於對方UA來說不需要。proxy可以在請求中加入這個頭域。

 

例如:

Call-Info:  ;purpose=icon,

http://www.example.com/alice/;purpose=info

 

20.10 Contact

Contact頭域提供了一個URI,這個URI的含義取決於是在請求還是在應答中。

 

Contact頭域包含了一個顯示的名字,一個包含參數的URI,還有header參數組成。

 

本文檔定義了一個Contact參數”q”和”expires”。這些參數只有當Contact頭域在REGISTER的請求或者應答,或者3xx的應答中才有效。在其他規範中可能會定義一個附加的參數。當頭域值包含一個顯示的名字,那麼帶參數的URI應當用”<”和”>”括起來。如果沒有”<”,”>”括起來,所有URI後邊的參數都將視爲header參數,而不是URI參數。顯示姓名可以是符號,或者引號引起來的字符串(如果很長的話)。

 

即使”display-name”是空的,如果”addr-spec”包含一個逗號或者分號,或者?的話,也必須使用”name-addr”的格式。這在display-name和”<”之間可以有也可以沒有LWS(線性空白)

 

這些關於顯示名字,URI和URI的參數,header參數的規則同樣對To和From頭域適用。Contact頭域的的角色很像HTTP中的Location頭域的角色。但是HTTP頭域只允許1個地址,沒有其他說明。由於URI中可以包含逗號和分號,所以他們在header或者參數分隔符上是錯誤的。

 

Contact頭域的縮寫是m(“moved”)。

例子:

 

Contact: “Mr.Watson” <sip:[email protected]>;q=0.7;

expires=3600,

“Mr. Watson” mailto:[email protected] ;q=0.1

 

m: <sips:[email protected]>;expires=60

 

20.11 Content-Disposition

Content-Disposition頭域描述了消息體,或者消息的多個部分,或者消息體的一個部分應被UAC或者UAS怎樣解釋。這個SIP頭域擴展了MIME Content-Type(RFC 2183[18])。

 

SIP定義了Content-Disposition幾個新的”disposition-types”。如果取值”session”意味着消息體位呼叫(calls)或者早期(pre-call)媒體,描述了一個會話。取值”render”表示了消息體可是被顯示或者展示給用戶。注意”render”比”inline”更適合避免MIME消息體作爲一個大的消息的一部分做展示(由於SIP消息的MIME消息體經常不被展示給用戶)。出於向後兼容的考慮,如果Content-Disposition頭域不存在,服務器應當假設Content-Type爲application/sdp的部屬方式是”session”,爲其他方式的時候是”render”。

 

部屬方式“icon”表示消息體部分包含了一個用於表示呼叫者或者被叫者的icon圖像,當UA收到這個消息,就可以展示一下,或者在對話過程中一致展示。”alert”意味着消息體部分包含了信息,比如是一段聲音,應當由UA展示給用戶提示用戶這個請求,通常是初始化對話的請求;這個altering消息體可以是一個在180Ringing臨時應答發出後的一個鈴聲。

 

所有需要展示給客戶的具有”disposition-type”的MIME消息體,都應當只在這個消息有適當的安全認證的時候展示。

 

處理參數,handling-param,描述了UAS在接收到這個內容類型或者部屬類型是它所不支持的消息體的時候,應當如何操作。這個參數定了了”optional”和”required”兩個值。如果處理參數沒有,那麼這個處理參數缺省值就是”required”。處理參數在RFC3204[19]中定義和描述的。

 

如果這個頭域不存在,那麼MIME類型決定了缺省的內容部屬。如果沒有MIME類型,那麼缺省值就是”render”

例如:

 

Content-Disposition: session

 

20.12 Content-Encoding

Content-Encoding頭域是對”media-type”(媒體類型)的一個修正。當存在這個頭域的時候,它的值就是對包體內容編碼的附加說明,並且因此必須根據本字段應用正確的解碼機制,這樣才能得到正確的Content-Type頭域指出的媒體類型的解碼。Content-Encoding首要應用於在不丟失媒體類型標記的情況下對消息體進行壓縮處理。

 

如果包體應用了多個編碼,那麼包體編碼必須按順序在這個字段中進行列出。

 

所有的Content-Encoding的值都是大小寫不敏感的。IANA是這個編碼方式的註冊機構。參見[H3.5]獲得Content-coding的語法定義。

 

客戶端可以在請求中進行包體的內容編碼。服務端也可以在應答中進行內容編碼。服務端必須只能應用客戶端在請求中的Accept-Encoding頭域中列出的編碼類型。

 

Content-Encoding簡寫是e。

例如:

Content-Encoding:gzip

e: tar

 

20.13 Content-Language

參見[H14.12].例如:

Content-Language: fr

 

20.14 Content-Length

Content-Length頭域標誌了消息體的大小,給消息的接受者,以10進製表示的數字。應用程序應當使用這個字段標誌的大小來傳送消息體,而不關心消息體的媒體類型是什麼。如果是基於流的通訊協議(比如TCP),那麼本頭域必須提供。

 

消息的大小並不包含CRLF分開的頭域和包體。任何大於或者等於0 的Content-Length都是合法的長度。如果消息中不包含包體,那麼Content-Length必須設置成爲0

 

對Content-Length的忽略能夠簡化創建一個類似cgi一樣動態生成應答的腳本。(???)

 

這個頭域的簡寫是l

 

例如:

Content-Length:349

l:173

 

20.15 Content-Type

Content-Type頭域標誌了發給對方的消息體的媒體類型。”media-type”是在[H3.7]中定義的。如果消息體不爲空,那麼Content-Type頭域就必須存在。如果消息體是空的,並且笨頭域存在,那麼就表示了特定類型的媒體的包體是0長度(比如空的音頻文件)。

 

本頭域的簡寫是c

例如:

Content-Type: application/sdp

c: text/html;charset=ISO-8859-4

 

20.16 Cseq

請求中的Cseq頭域包含了一個單個的數字序列號和請求的方法。這個序列號必須是表示成爲一個32位的無符號整數。在Cseq的請求方法部分是大小寫敏感的。Cseq頭域是爲了在會話中對事務進行排序的,提供事務的唯一標誌,並且區分請求和請求的重發。如果序列號相等,並且請求的方法相等,那麼兩個Cseq頭域就是相等的。

例如:

Cseq:4711 INVITE

 

20.17 Date

Date頭域包含了日期和時間。和HTTP/1.1不同,SIP只支持最近的RFC1123[20]格式的日期。如同在[H3.3]中,SIP限制了在SIP-date中的時區是”GMT”,但是在RFC1123中支持任意的市區。RFC1123的日期是大小寫敏感的。Date頭域反應的時間是請求或者應答被髮送的那一刻的時間。

 

Date頭域可以用來簡化沒有後備電池的終端系統,讓他們能夠獲得當前的時間。但是由於是GMT格式的,所以,它要求客戶端知道和GMT的時差。

 

例如:

Date:Sate,13 Nov 2010 23:29:00 GMT

 

20.18 Error-Info

Error-Info頭域提供了對有錯誤應答碼的應答的附加信息。

 

SIP UAC具有從彈出的窗口PC界面,到只有聲音的電話或者網關過來的終端界面。與其強制服務器產生一個錯誤來選擇是發送一個帶有詳細原因說明的錯誤代碼應答,還是播放一段聲音,不如使用Error-Info頭域把兩個都發送。讓UAC來決定用什麼來展示給呼叫方。

 

UAC可以把在Error-Info中的一個SIP或者SIPS URI當作是轉發的一個Contact地址,並且據此產生一個新的INVITE,這樣可以建立預先錄製的聲明會話。如果是非SIP URI,那麼可以展示給用戶。

 

例如:

 

SIP/2.0 404 The Number you have dialed is not in service

Error-Info: <sip:[email protected]>

 

20.19 Expires

Expires頭域給定了消息(或者內容)過期的相關時間。這個字段的精確定義是方法相關的。對於一個INVITE的超時時間並不影響這個INVITE請求建立的實際的會話。不過,會話描述協議可以描述在一個會話上的的時間限制。

 

這個頭域的值是一個以秒計數的整數,從0到(2**32)-1,從收到請求開始計數。

 

例如:

Expires:5

 

20.20 From

From頭域表示了請求的來源地。這個可能和對話的來源的不同,被叫方到呼叫方的請求會在From頭域使用被叫方的地址。

 

選項”display-name”是展示給界面的。如果客戶標誌停留在隱藏狀態,那麼系統應當使用”Anonymous”作爲顯示名字。即使是”displayname”是空的,如果”addr-spec” 包含一個逗號,?,或者分毫,那麼就必須使用”name-addr”格式。相關的格式在7.3.1節描述。

 

如果From的URI相等,並且參數也相等,那麼這兩個頭域就是相等的。如果擴展參數在一個頭域中存在,但是在另外一個頭域中不存在,那麼當這兩個頭域做比較的時候,這個參數將被忽略。這意味着顯示名字的存在與否不影響比較的結果。

 

參見20.10處理顯示名字,URI和URI參數,以及頭域參數的規則。

 

From頭域的簡寫是f

例子:

From: “A. G. Bell” <sip:[email protected]> ; tag=a48s

From: sip:[email protected];tag=887s

f: Anonymous <sip:[email protected]>;tag=hyh8

 

20.21 In-Reply-To

In-Reply-To頭域列舉了本次呼叫相關的或者返回的Call-ID。這些Call-ID可以備客戶端cache起來,這樣可以在這個頭域中返回。

 

這允許自動呼叫奮發系統來路由這些返回的呼叫到第一個呼叫的原始請求地點。這也允許被叫方過濾呼叫,這樣只有在呼叫中原始請求建立的呼叫纔會被接受。這個字段不是對請求驗證的一個替代。

例如:

 

In-Reply-To: [email protected],[email protected]

 

20.22 Max-Forwards

Max-Forwards頭域必須在任何一個SIP請求中使用,來限制中間轉發請求到下一個節點的proxy或者gateway的個數。這個在客戶端trace一個請求,如果路由失敗或者在中間出現循環的時候特別有用。

 

Max-Forwards是一個0-255的整數,表明了在這個請求消息中允許被轉發的剩餘次

 

 

小虎 2006-05-25 00:11

數。每當服務器轉發這個請求一次,這個數字就減一。建議的初始值是70。當不能確定有無循環路由的時候,必須在頭域中增加本頭域。比如,一個B2BUA應當增加這個頭域。

 

例如:

Max-Forwards:6

 

20.23 Min-Expires

Min-Expires頭域包含了一個服務器所支持的內部狀態(soft-state)的最小的刷新時間間隔。這個包括被登記服務器所登記的Contact頭域。這個頭域包含了一個以秒計數的整數,從0到(2**32)-1。在423(Interval Too Brief)應答中,本頭域的用法在10.28,10.3,和21.4.17中有描述。

例如:

Min-Expires:60

 

20.24 MIME-Version

參見[H19.4.1]

例如:

MIME-Version: 1.0

 

20.25 Organization

Organization頭域包含了發出請求或者應答的SIP節點所屬的組織名字。這個字段可以用來讓客戶端軟件過濾呼叫。

例如:

 

Organization: Boxes by Bob

 

20.26 Priority

Priority頭域標誌了客戶端評價的請求緊急程度。Priority頭域描述了SIP應當處理人工或者UA發過來的請求的優先級。舉例來說,這可能是決定呼叫轉發和處理的優先要素。對於判定優先級來說,如果消息沒有包含Priority字段,那麼處理的時候應當當作”normal”優先級處理。Priority頭域不影響通訊資源的優先順序,比如路由上的包轉發的優先級或者訪問PSTN網關的優先級。本頭域有”non-urgent”,”normal”,”urgent”,和”emergency”取值,另外的取值可以在別處定義。我們強烈建議”emergency”只用於影響到生命、身體、或者財產危急時候才使用。其他情況下, 本頭域沒有額外的語義。在RFC2076[38]中,定義了”emergency”。

 

例如:

Subject: A tornado is heading our way!

Priority: emergency。

 

或者

Subject: Weekend plans

Priority: non-urgent.

 

20.27 Proxy-Authenticate

Proxy-Authenticate頭域用來進行認證使用的。這個頭域的用法在[H14.33]中定義。參見22.3節關於本字段的細節討論。

 

例如:

Proxy-Authenticate: Digest realm=”atlanta.com”,

domain=”sip:ss1.carrier.com”,qop=”auth”,

nonce=”f84f1cec41e6cbe5aea9c8e88d359”,

opaque=””,stale=FALSE,algorithm=MD5

 

20.28 Proxy-Authorization

Proxy-Authorization頭域允許客戶端向一個要求認證的proxy證明自己(或者證明它的使用者)的身份。一個Proxy-Authorization頭域包含了與UA認證信息相關的信任書,這個信任書是給proxy和/或者本請求相關的域的。

 

參見22.3節關於這個頭域的定義。

 

本頭域,連通Authorization頭域,並不遵循常用的多頭域名(多個相同頭域名的合併)的規則。雖然不是用逗號分割的列表,這個頭域名可以出現多次,並且不能用7.3.1描述的通常規則合併成爲一個頭域。

 

例如:

Proxy-Authorization: Digest username=”Alice”,realm=”atlanta.com”,

nonce=”c60f3082ee1212b402a21831ae”,

response=”245f23415f11432b3434341c022”

 

20.29 Proxy-Require

Proxy-Require頭域用來表示請求中一定要求proxy支持的相關的特性。參見20.32關於這個頭域的使用。

 

例子:

Proxy-Require:foo

 

20.30 Record-Route

Record-Route頭域是proxy在請求中增加的,用來強制會話中的後續請求經過本proxy的。本頭域的用法在16.12.1節有描述。

 

例子:

Record-Route: <sip:server10.biloxi.com;lr>,

<sip:bigbox3.site3.atlanta.com;lr>

 

20.31 Reply-To

Reply-To頭域包含了邏輯上返回目的地URI,這個可以和From頭域不同。比如,URI可以用來返回未接電話或者未建立的會話。如果用戶希望保留匿名,那麼這個頭域應當從請求中去除或者改變,這樣可以避免透露個人隱私信息。

 

即使”display-name”是空的如果”addr-spec”包含了逗號、問號、或者分號,那麼就需要使用”name-addr”的格式來填寫。這個語法在7.3.1中定義。

 

例如:

Replay-To: Bob <sip:[email protected]>

 

20.32 Require

Require頭域用於UAC告訴UAS關於要求UAS支持那些特性。雖然這是一個可選的頭域,但是如果Require頭域存在,那就一定不能掠過不處理。

 

頭域包含一個option tag的列表,這個列表在19.2節中描述。每一個option tag定了一個要處理請求要求UAS必須支持的SIP擴展。通常,這用於定義一個需要支持的擴展頭域的集合。複覈本規範的UAC應當值包含規範的RFC擴展。

例如:

Require:100rel

 

20.33 Retry-After

Retry-After頭域可以用於500(Server Internal Error)或者503(Service Unavailable)應答,用來標誌大約本服務還會處於不可用狀態多久。在404(Not Found),413(Request Entity Too Large), 480(Temporarily Unavailable),486(Busy Here), 600 (Busy), 或者603(Decline)應答中用於標誌何時被叫方會恢復正常。這個字段的值是一個秒爲單位的正整數(十進制),從應答生成開始的一個正整數。

 

對於回叫的時間,可以有一個附加的說明。”duration”參數標誌了被叫方變成正常狀態的時間長度。如果沒有定義,那麼服務可以被看作是永遠有效。

 

例如:

Retry-After: 18000;duration=3600

Retry-After:120 (I’m in a meeting)

 

20.34 Route

Route頭域用於強制一個請求經過一個proxy路由列表。Route頭域的使用在16.12.1節定義:

例如:

Route: <sip:bigbox3.site3.atlanta.com;lr>,

<sip:server10.biloxi.com;lr>

 

20.35 Server

Server頭域包含了關於UAS處理請求所使用的軟件信息。

 

服務器的特定軟件版本可能會使服務器由於特定軟件安全漏洞導致服務器收到攻擊。實現上應當使得Server頭域是一個可以配置的選項。

 

例如:

 

Server:HomeServer v2

 

20.36 Subject

Subject頭域提供了呼叫的一個概覽,允許呼叫不用分析會話描述就可以大致過濾。會話描述並不需要和INVITE邀請使用相同的主題標誌。

 

Subject的縮寫是s

例如:

Subject: Need more boxes

s: Tech Support

 

20.37 Supported

Supported頭域列舉了UAC或者UAS支持的擴展。

 

Supported頭域包含了一個option tag的列表,在19.2節描述的option tag,他們是這個UAS或者UAC所支持的。遵循本規範的UA必須只包含遵循標準RFC擴展的option tag。如果本字段是空的,意味着不支持任何擴展。

 

Supported頭域的縮寫是k

例如:

 

Supported: 100rel

 

20.38 Timestamp

Timestamp頭域描述了當UAC發送請求到UAS的時間戳。

 

參見8.2.6節關於如何給請求產生一個包含這個頭域的應答。雖然沒有定義本字段的標準行爲,我們允許對擴展應用或者SIP應用獲得RTT預計時間。

例如:

Timestamp:54

 

20.39 To

To頭域定義了邏輯上請求的接收者。選項”display-name”意味着展示給客戶的界面。”tag”參數提供了對話識別機制。

 

參見19.3節關於”tag”參數的些界描述。

 

對於To頭域的比較是和對From頭域的比較相同的。參見20.10節的比較規則來比較display name,URI和URI參數,以及頭域的參數。

 

To頭域的縮寫是t。

下邊是一個To頭域的例子:

To: The Operator <sip:[email protected]>;tag=287447

t: sip:[email protected]

 

20.40 Unsupported

Unsupported頭域列出了不被UAS支持的特性列表。參見20.32。

例如:

Unsupported:foo

 

20.41 User-Agent

User-Agent頭域包含了發起請求的UAC信息。本頭域的語義在[H14.43]定義。

 

UA所使用的版本號情況可能會導致由於這個版本的安全漏洞二遭受攻擊。所以在實現上應當使得User-Agent頭域是可以配置的。

例如:

User-Agent:Softphone Beta1.5

 

20.42 Via

Via頭域是用來描述請求當前經歷的路徑的,並且標誌了應答所應當經過的路徑。Via頭域的branch ID參數提供了事務的標誌,並且用於proxy來檢查循環路由。

 

Via頭域包含了用於發送消息的通訊協議,客戶端主機名或者網絡地址,可能還有接收應答所用的端口號碼。Via頭域還可以包含參數”maddr”,”ttl”,”received”和”branch”,這些定義在其他節中描述。對於遵循本規範的實現,這個branch參數的值必須用magic cookie”z9hG4bK”打頭(8.1.1.7節)。

 

這裏定義的通訊協議是”UDP”,”TCP”,”TLS”,和”SCTP”,”TLS”意思是基於TCP的TLS。當請求發送到一個SIPS URI上時,協議依舊標記着時”SIP”,但是通訊協議是TLS。

 

Via: SIP/2.0/UDP erlang.bell-telephone.com:5060;branch=z9hG4bK87asdks7

Via: SIP/2.0/UDP 192.0.2.1:5060 ;received=192.0.2.207

;branch=z9hG4bK77asjd

 

Via頭域的縮寫是v

 

在這個例子中,從多源(multi-homed)主機的消息有兩個地址,192.0.2.1和192.0.2.207。發送者猜錯了發送的網絡界面(以爲是在192.0.2.1上發送的)。Erlang.belltelephone.com發現了這個不匹配,並且給這個節點的Via增加了一個參數,包含了實際包接收到的地址。

 

在SIP URI語法下,並不要求填寫主機名或者網絡地址和端口號。特別是,允許在”:”或者”/”兩遍的LWS(線形空白)。例如:

 

Via: SIP / 2.0 / UDP first.example.com: 4000;ttl=16

;maddr=224.2.0.1 ;branch=z9hG4bKa7c6a8dlze.1

 

即使本規範要求所有的請求中都包含branch參數,本頭域的BNF描述中,branch參數是可選的。這就和RFC2543元素可以進行互操作,因爲RFC2543沒有添加branch參數。

 

如果他們的發送協議和sent-by域相等,都有相同的參數集合,並且參數都相等,那麼兩個Via頭域就是相同的。

 

20.43 警告

Warning頭域用來給應答的狀態添加附加說明使用的。Warning頭域值是在應答中包含的,並且包括了一個3位的警告代碼,主機名,和警告正文。

 

“warn-text”應當是一個自然語言,給個人用戶接收應答時候來響應的。這可以通過現有的各種信息來決定這個warn-text,比如用戶的位置,Accept-Language域,或者應答重的Content-Language等等。缺省語言是idefault[21]。

 

下邊列出了當前定義的”warn-code”,並且有英文描述的推薦的warn-text。這些井蓋描述了會話描述中的各種可能的失敗情況。第一個warn-code的數字是”3”表示這是一個SIP規範的警告信息。警告信息300到329是保留用於標誌在會話描述中的保留字錯誤的,330到339是會話描述中基本網絡服務相關警告,370到379是關於會話描述重的QoS參數數量相關的警告,390到399是上邊未列除的雜項警告信息。

 

300 Incompatible network protocol:(不兼容的網絡協議),One or more network protocols contained in the session description are not available.(在會話描述中的一個或者多個網絡協議不適用)

 

301 Incompatible network address formats(不兼容的網絡地址格式):One or more network address formats contained in the session description are not available. (會話描述中的一個或者多個網絡地址格式不合法)

 

302 Incompatible transport portocol(不兼容的通訊協議):One or more transport protocols described in the session description are not available. (會話描述中的一個或者多個通訊協議不存在)。

 

303:Incompatible bandwidth units(不兼容的帶寬單位): One or more bandwidth measurement units contained in the session description were not understood.(會話描述中的一個或者多個帶寬單位不支持)。

 

304 Media type not available(媒體類型不存在): One or more media types contained in the session description are not available. (會話描述中的一個或者多個媒體類型不存在)。

 

305 Incompatible media format(媒體格式不兼容): One or more media formats contained in the session description are not available.(會話描述中的一個或者多個媒體格式不兼容)。

 

306 Attribute not understood(媒體屬性不支持): One or more of the media attributes in the session description are not supported.(會話描述中的一個或者多個媒體屬性不支持)。

 

307 Session description parameter not understood(會話描述參數不支持): A parameter other than those listed above was not understood.(列出的會話描述參數不支持)。

 

330 Multicast not available(多點傳輸不允許): The site where the user is located does not support multicast.(用戶定位的這個服務器不支持多點傳送)。

 

331 Unicast not available(Unicast不支持): The site where the user is located does not support unicast communication (usually due to the presence of a firewall)。(用戶定位的節點不支持unicast通訊(通常由於在防火牆之後))。

 

370 Insufficient bandwidth(帶寬不足): The bandwidth specified in the session description or defined by the media exceeds that known to be available.(會話描述的帶寬要求或者媒體要求的帶寬超過限制)。

 

399 Miscellaneous warning(雜項警告): The warning text can include arbitrary information to be presented to a human user or logged. A system receiving this warning MUST NOT take any automated action.(這個警告信息可以包含給用戶的任意信息或者做日誌記錄。接收到這個警告的系統禁止做任何自動操作)。

 

1xx和2xx消息是HTTP/1.1使用的。

附加的”warn-code”是IANA定義的,在27.2節有附加說明。

例如:

Warning: 307 isi.edu "Session parameter ’foo’ not understood"

Warning: 301 isi.edu "Incompatible network address type ’E.164’"

 

20.44 WWW-Authenticate

WWW-Authenticate頭域包含了認證信息,參見22.2節有關的詳細說明。

例如:

WWW-Authenticate:Digest realm=”atlanta.com”,

domain=”sip:boxesbybob.com”,qop=”auth”,

nonce="f84f1cec41e6cbe5aea9c8e88d359",

opaque="", stale=FALSE, algorithm=MD5

 

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