
前面我們很多文章是從比較大的寬泛的角度來討論SIP呼叫。關(guān)于proxy代理的定義,我們需要從官方的定義做尋找答案。根據(jù)RFC3261的定義,SIP proxy是:
SIP proxies are elements that route SIP requests to user agent . servers and SIP responses to user agent clients. A request may traverse several proxies on its way to a UAS. Each will make routing decisions, modifying the request before forwarding it to the next element.
https://tools.ietf.org/html/rfc3261#section-16
為了回答這幾個核心的問題,今天,我們從比較細(xì)節(jié)的流程來分析呼叫處理流程的創(chuàng)建。首先,結(jié)合上面的圖例,我們需要了解proxy在對下游UA繼續(xù)發(fā)送INVITE之前,它需要做哪些方面的處理?事實(shí)上,proxy在對下游發(fā)起INVITE之前,它需要做一些預(yù)處理工作。因?yàn)樾枰鲞@些工作,所以會導(dǎo)致我們對proxy產(chǎn)生的六大疑問,這也是比較重要的關(guān)于SIP創(chuàng)建呼叫所經(jīng)常面對的終極問題。這八個終極問題涉及了Route header,Request URL,MAX-Forwards,Via header,100 Trying,ACK直接互發(fā)。下面,我們逐一針對這些常見的問題進(jìn)行比較細(xì)致的分析。
1、為什么移除Route header
在討論Route功能之前,我們首先說明一下Route的基本概念。在RFC3261中規(guī)定,此頭是用來強(qiáng)制request路由多個proxy,很多環(huán)境環(huán)境可能經(jīng)過多個proxy來處理呼叫。
The Route header field is used to force routing for a request through the listed set of proxies.
https://tools.ietf.org/html/rfc3261#section-20.34
如果用戶需要了解具體的測試場景,用戶可以下載任意一個開源平臺的測試環(huán)境來進(jìn)行測試。

在前面的文章中我們已經(jīng)介紹過,如果UAC需要發(fā)起一個呼叫的話,可以通過INVITE的地址request URL來查詢定位服務(wù)器和注冊服務(wù)器,然后進(jìn)行呼叫。實(shí)際上,我們這里忽略了一個非常核心的header-Route。大家現(xiàn)在看看這個基本的示例:

在實(shí)際發(fā)起請求的過程中,如果Route存在的話,request其實(shí)首先根據(jù)Route對代理服務(wù)器發(fā)起請求,然后再發(fā)送INVITE中的具體賬號信息。這里,Route可以是FQDN格式,此頭則通過DNS查詢來獲得IP地址。
這個請求抵達(dá)下一個proxy以前,此Route地址會在proxy中移除。因?yàn)椋@個Route的任務(wù)已經(jīng)完成。另外,從雙方的INVITE消息中可以看出,針對這個INVITE的請求中,UAS端的消息中已經(jīng)移除了Route header。

因此,筆者提醒讀者必須理解以下“黃金定律”:
- Route 頭決定proxy下一跳的路由地址
- 繼續(xù)下一跳之前,移除Route header
- Route頭比request URL具有更高優(yōu)先級
- 如果Route不存在,則使用request URL
- Route header支持FQDN查詢處理,解析為IP地址
- 路由總是使用最頂端Route header地址(后面介紹)
在以前的介紹中,我們已經(jīng)介紹了Route header, request URL,Contact address和Via 地址。現(xiàn)在,筆者簡單總結(jié)幾個路由地址的區(qū)別:
- Route header是針對下一跳的路由,如果有此頭,則request會根據(jù)此header被路由到下游
- Request URL:如果Route header不存在的話,則請求根據(jù)此header路由。
- Contact header:終端IP地址,為其他終端提供subsequent requests返回路由,例如ACK或者BYE消息發(fā)送。
- Via header: 支持UAC地址或者proxy地址,為下游設(shè)備提供返回響應(yīng)的路由,總是從Via 頂部地址優(yōu)先路由,proxy在轉(zhuǎn)發(fā)到下游前移除自己的地址。
這里我們可以看到,經(jīng)過proxy到達(dá)UAS時,Route需要移除,然后根據(jù)request URL來對UAS做呼叫路由。所以,必須移除Route header,否則,協(xié)議處理的流程會發(fā)生沖突。
2、為什么需要替換Request URL
在以前的很多討論中,我們討論了如何通過AOR查詢對應(yīng)的Contact地址,然后進(jìn)行呼叫的多個示例。現(xiàn)在,讓我們再回顧一次呼叫的流程處理:

首先,呼叫方發(fā)起INVITE以后,定位服務(wù)器會一步步查詢所屬domain地址,然后查詢注冊表,如果有注冊的數(shù)據(jù)內(nèi)容,進(jìn)行AOR和Contact的地址解析,然后proxy替換這個request URL,對Contact進(jìn)行INVITE請求。當(dāng)然,處理過程中也涉及了如果不在正常處理流程中的其他處理方式,我們這里不做進(jìn)一步討論。
根據(jù)以上處理流程我們可以看出,首先,定位服務(wù)器需要查詢此呼叫的AOR是否存在或者屬于此定位服務(wù)器支持的domain。如果此domain存在的話,則進(jìn)行AOR解析,然后替換成Contact地址,proxy最后呼叫這個具體的Contact地址或request URL。但是,這里讀者一定要注意,proxy做了兩個主要的工作。為了滿足前面我們提到的Route 頭優(yōu)先路由和無Route header情況下的處理原則,proxy在對對端發(fā)送INVITE之前,proxy需要移除Route header,只有這樣,INVITE請求才能根據(jù)Request URL進(jìn)行路由。在以下示例消息中,當(dāng)proxy再次對被呼叫方發(fā)起INVITE之前,已經(jīng)移除了Route header,終端路由根據(jù)請求地址來路由。

因此,proxy對下游UAC發(fā)送INVITE之前,替換了AOR地址,使用了請求地址,而且移除了Route header,替換為request URL,這樣可以保證請求時通過請求URL地址路由。
3、為什么MAX-Forwards需要遞減
在比較復(fù)雜的SIP呼叫環(huán)境中,一個呼叫可能經(jīng)過多個domain和定位服務(wù)。如果其中一個proxy設(shè)置錯誤的話,可能導(dǎo)致一個定位查詢的回環(huán),這樣UAC就會收到482 Rejected 的消息。為了防止回環(huán)的發(fā)送,在定位查詢過程中,每成功通過一個proxy,Max-Forwards的計(jì)數(shù)器就會自動減一,直到抵達(dá)最終UAC。

在SIP消息中,如果出現(xiàn)了大量的Via header的話,讀者一定要注意,這可能發(fā)生了回環(huán)。導(dǎo)致這樣錯誤可能是某些地址設(shè)置問題。讀者需要檢查具體的配置文件。

以下是一個檢測到回環(huán)的流程,經(jīng)過多個接點(diǎn)的檢查以后,Max-Forwards可能最后遞減成了0,這樣說明檢測到了loop。經(jīng)過一點(diǎn)時間檢查,最后對UAC發(fā)送482 消息。

4、為什么proxy增加一個Via header
為了讓下游UA獲悉自己的返回地址,每經(jīng)過一個proxy,proxy必須自己主動添加自己的地址作為一個新的Via header。如果呼叫請求經(jīng)過了多個proxy以后,那么這個Via 記錄就會不斷增加,并且,Via 頭根據(jù)順序來進(jìn)行增加,最新的Via header會出現(xiàn)在Via 頭最頂端。所有的Via 添加類似于一個隊(duì)列處理。在增加了一個新的Via以后,proxy必須對最頂端(Via header)返回一個100 Trying? 為什么呢? 因?yàn)椋琾roxy必須對上游proxy說明自己的狀態(tài),通知上游proxy已經(jīng)收到了proxy的請求。
如果雙方呼叫通過了多個跳轉(zhuǎn)以后,每經(jīng)過一個proxy會增加一個自己的Via header,直到抵達(dá)最后的UAC地址。最后的UAC地址則包含了多個Via header。底部是最起始的地址,最頂部是最近地址。

當(dāng)UAC接聽呼叫后,返回響應(yīng)消息時,則按照請求路徑的相反的處理流程依次進(jìn)行處理。首先,通過頂部第一個Via地址返回到 proxy 3, proxy 然后從Via header 移除自己的地址,按照Via 地址,返回到proxy 2。最終返回到發(fā)起呼叫的UAC地址。每經(jīng)過一跳就移除自己本身的Via地址。

5、為什么100 Trying沒有返回
在前面的章節(jié)中,我們通過一封信的方式解釋了INVITE請求中幾個主要的頭字段的功能說明。Via header就是其中之一,其主要作用下游終端知道通過這個地址返回進(jìn)一步的響應(yīng)消息,負(fù)責(zé)返回響應(yīng)的路由路徑管理。根據(jù)RFC3261的規(guī)定,Via 頭的定義是:
The Via header field indicates the transport used for the transaction and identifies the location where the response is to be sent.
https://www.ietf.org/rfc/rfc3261.txt
根據(jù)RFC3261的定義和我們以前的介紹,本身100 Trying就是一個可靠性傳輸?shù)捻憫?yīng)機(jī)制,其主要目的就是讓保證上下游之間的可靠性傳輸,沒有承擔(dān)其他的功能角色。

如果經(jīng)過多個proxy處理時,怎么能夠保證雙方之間的通信是可靠的? 100 Trying是一個高效的方式來實(shí)現(xiàn)這個簡單的功能。如果proxy對下游proxy發(fā)送一個INVITE以后,對端對自己上游發(fā)送一個100 Trying,說明自己已經(jīng)接收到了這個請求,proxy就知道對端的狀態(tài)。因此,proxy此刻的任務(wù)已經(jīng)完成。因此,proxy也不會對上游UAC發(fā)起方發(fā)送100 Trying,避免了資源浪費(fèi)。另外,回顧我們以前的討論,100 Trying不能再次重復(fù)轉(zhuǎn)發(fā),不像其他響應(yīng),例如180 ring。proxy仍然需要對上游UAC發(fā)送180 ring消息,保證會話的完整性。

6、為什么使用Route set路由
前面我們介紹過Route set和Via的區(qū)別,讀者可以回顧前面的討論來了解Via的作用。Proxy通過Via獲得了最終地址,雙方已經(jīng)確認(rèn)了地址消息,proxy完成了其工作。雙方確認(rèn)了狀態(tài)以后,UAC會保存對端的Contact地址作為響應(yīng)的直接地址。下來,UA1 可以根據(jù)這個Contact地址,直接對UA2發(fā)送ACK確認(rèn)。

但是,讀者一定要注意,當(dāng)UAC2收到UAC1 的地址后,在返回200 OK時,UAC2會把自己的地址添加到Contact中作為Contact地址,這樣做的目的是通知UAC1,UAC2的地址就是200 OK中的這個Contact地址。UAC雙方最終的通信是通過Route set來進(jìn)行,雙方保存了Contact地址以后,雙方可以直接通過Contact地址發(fā)送ACK或者BYE消息。
因此,這里UAC之間的直接通信是通過Route set來實(shí)現(xiàn),而不是Via 地址。
7、為什么UAC之間直接發(fā)送ACK
首先,我們需要理解proxy的角色,proxy的主要功能就是定位被呼叫方的Contact地址和進(jìn)行下一跳代理功能。為了協(xié)助UAC進(jìn)行通信,Proxy經(jīng)過多個定位和路由最終找到UAC,UAC已經(jīng)對proxy發(fā)送了100 Trying,其主要任務(wù)已經(jīng)完成。為了降低系統(tǒng)資源的負(fù)載,proxy不再參與其流程處理,雙方通過route set的地址可以直接發(fā)送ACK確認(rèn)消息,因此不再需要通過proxy繼續(xù)處理ACK或BYE消息。

注意,我們這里沒有涉及有狀態(tài)代理和無狀態(tài)代理的內(nèi)容,那是另外一個話題,不在我們這里討論的范圍。讀者不要對這里的討論產(chǎn)生疑惑,我們僅討論UAC之間ACK的發(fā)送問題。
8、不同域名之間的呼叫處理
兩個不同域名用戶之間的呼叫中,proxy需要進(jìn)行地址查詢或定位查詢,找到相應(yīng)的domain定位服務(wù)器。如果被呼叫方用戶不屬于自己domain的用戶,則根據(jù)domain來查詢DNS,通過DNS獲得domain地址以后,再繼續(xù)查詢確認(rèn)好的定位服務(wù)器。定位服務(wù)器最終找到相應(yīng)的用戶帳戶。那么,現(xiàn)在的問題來了,上游proxy如何處理這個AOR地址和路由呢?下面,我們看看具體的處理流程。首先,第一個proxy需要添加自己的地址,然后發(fā)送到下游定位服務(wù)器。

這里比較關(guān)鍵的服務(wù)是在第二個定位服務(wù)器和代理的處理上。第二個定位服務(wù)器在對自己終端發(fā)送INVITE之前,必須先移除Route地址,proxy使用request URL地址來做呼叫路由。為什么需要這樣處理?因?yàn)椋鶕?jù)前面講到的route header的使用原則,在最后的INVITE中如果沒有Route header,則使用request URL。因此,第二個proxy在發(fā)送INVITE之前,必須移除Route header,使用requerst URL做呼叫路由。這樣就完成了不同domain之間的呼叫。
9、總結(jié)
我們今天通過八個問題的討論,基本上回答了很多SIP用戶的關(guān)于Proxy處理的疑惑。這八個終極問題涉及了Route header,Request URL,MAX-Forwards,Via header,100 Trying,ACK直接互發(fā),和不同域名之間的呼叫流程。另外,筆者也針對幾個主要的路由地址歸納了幾個不同,方便讀者進(jìn)一步清晰地了解SIP路由的概念。最后,筆者需要讀者重點(diǎn)掌握SIP頭路由的幾個黃金定律,它們是決定如何路由的前提條件。
參考資料:
https://www.tutorialspoint.com/session_initiation_protocol/session_initiation_protocol_proxies_and_routing.htm
https://www.slideshare.net/JonasBorjesson/aboutsip-routing
https://tools.ietf.org/html/draft-byerly-sip-hide-route-00


關(guān)注微信公眾號:asterisk-cn,獲得有價值的Asterisk行業(yè)分享
Asterisk freepbx 中文官方論壇:http://bbs.freepbx.cn/forum.php
Asterisk freepbx技術(shù)文檔: www.freepbx.org.cn
融合通信商業(yè)解決方案,協(xié)同解決方案首選產(chǎn)品:www.hiastar.com
Asterisk/FreePBX中國合作伙伴,官方qq技術(shù)分享群(3000千人):589995817
Asterisk freepbx 中文官方論壇:http://bbs.freepbx.cn/forum.php
Asterisk freepbx技術(shù)文檔: www.freepbx.org.cn
融合通信商業(yè)解決方案,協(xié)同解決方案首選產(chǎn)品:www.hiastar.com
Asterisk/FreePBX中國合作伙伴,官方qq技術(shù)分享群(3000千人):589995817