P2P通訊標準協議(四)之SIP

有價值炮灰發表於2016-01-05

在前面幾篇文章中我們介紹了建立p2p通訊的一般協議(簇),以及一種完整的NAT傳輸解決方案ICE,
但是對於多使用者的通訊情況,還有一些通用協議來實現標準化的管理,如之前講過的SDP和SIP等,SIP(Session Initiation Protocol),
是屬於應用層的控制協議,主要用於在一個或多個參與者之間建立,修改和中止會話(sessions).會話的型別包括IP電話,
多媒體流分發和多媒體會議等.

SIP簡介

SIP邀請(invitations)用於建立攜帶會話描述(如SDP資訊)的會話,允許參與者使用一系列相容的媒體型別.
SIP使用一種叫代理伺服器的元素來幫助對使用者當前位置進行轉發,對使用者進行驗證和授權,併為使用者提供相應的功能.
SIP同時也提供了註冊函式以允許使用者上傳他們的當前地址供代理伺服器使用.SIP協議執行在多個不同的傳輸協議之上.

SIP支援5個方面來建立和中止多媒體會話:

  • 使用者地址(User location): 決定了用來通訊的終端系統.
  • 使用者狀態(User availability): 決定了被呼叫端的是否願意加入通訊.
  • 使用者效能(User capabilities): 決定了多媒體型別和媒體使用的引數.
  • 會話建立(Session setup): "響鈴",在呼叫端和被呼叫端建立起會話.
  • 會話管理(Session management): 包括傳輸和中止會話,修改會話引數以及呼叫服務.

SIP不是一個垂直整合的通訊系統,而是作為一個元件與其他協議共同運作,如RTP等實時傳輸協議等.另外SIP不提供服務,
只提供可以用來實現各種服務的原語.比如,SIP可以定位使用者並且傳輸一個不透明的物件到其當前地址.如果這個原語用來
傳輸SDP,終端就能得知會話的一些引數;如果同樣的原語用來傳輸一張照片,那也可以實現一種"顯示來電者頭像"的服務.
由此可見,一種原語通常用來實現多種不同的服務.

SIP工作過程

下圖描述了SIP的基本功能:定位一個終端,產生通訊請求,建立會話以及結束會話.

                 atlanta.com  . . . biloxi.com
             .      proxy              proxy     .
           .                                       .
   Alice's  . . . . . . . . . . . . . . . . . . . .  Bob's
  softphone                                        SIP Phone
     |                |                |                |
     |    INVITE F1   |                |                |
     |--------------->|    INVITE F2   |                |
     |  100 Trying F3 |--------------->|    INVITE F4   |
     |<---------------|  100 Trying F5 |--------------->|
     |                |<-------------- | 180 Ringing F6 |
     |                | 180 Ringing F7 |<---------------|
     | 180 Ringing F8 |<---------------|     200 OK F9  |
     |<---------------|    200 OK F10  |<---------------|
     |    200 OK F11  |<---------------|                |
     |<---------------|                |                |
     |                       ACK F12                    |
     |------------------------------------------------->|
     |                   Media Session                  |
     |<================================================>|
     |                       BYE F13                    |
     |<-------------------------------------------------|
     |                     200 OK F14                   |
     |------------------------------------------------->|
     |                                                  |

                     圖 1: SIP會話建立

圖中描述了兩個使用者Alice和Bob交換SIP資訊的過程.(資訊表示為Fn.) 首先,Alice在其PC上使用了SIP終端(假設是軟體電話),
並且通過網際網路打給Bob. 其中我們看到有兩個代理伺服器atlanta和biloxi,用來幫助雙方進行會話建立.這個典型的排列經常
被稱為SIP之梯(SIP trapezoid).

Alice呼叫Bob時,使用的是Bob的SIP身份資訊,一種特定型別URI稱為SIP URI,形式和E-mail地址類似,包含了使用者名稱和主機名.
在本例中,Bob的地址為sip:bob@biloxi.com,biloxi是Bob的SIP服務提供商;同樣,Bob聯絡Alice時也通過其SIP地址sip:alice@atlanta.com
來進行通訊. SIP同樣提供了安全的連結SIPS,和HTTPS類似,主要通過TLS進行內容加密, 加密的地址格式為sips:alice@atlanta.com

SIP基於一種類HTTP的請求/響應傳輸模型.每次傳輸包含一個呼叫了特定方法或函式的請求,以及至少一個響應.在本例中,
傳輸開始時Alice傳送了一個INVITE請求到Bob的SIP URI. INVITE請求包含一系列頭部(header)欄位.頭部欄位被稱為屬性,
提供了關於報文的額外資訊. 產生INVITE請求的終端包含了一個獨特的通話識別符號,目的地址,Alice的地址以及Alice
希望與Bob建立的會話的型別資訊. 一個INVITE請求的例子如下,其中Alice的SDP資訊沒有顯示出來:

  INVITE sip:bob@biloxi.com SIP/2.0
  Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds
  Max-Forwards: 70
  To: Bob <sip:bob@biloxi.com>
  From: Alice <sip:alice@atlanta.com>;tag=1928301774
  Call-ID: a84b4c76e66710@pc33.atlanta.com
  CSeq: 314159 INVITE
  Contact: <sip:alice@pc33.atlanta.com>
  Content-Type: application/sdp
  Content-Length: 142

  (Alice的SDP資訊,略)

                     圖 2: Alice傳送的請求報文

其中第一行包含了方法的名字(INVITE).後面的一些行則是一系列頭部區段,各個頭部欄位的含義在下一節會說到.

由於Alice不知道Bob的準確地址,因此報文會先傳送到Alice的SIP服務提供商,atlanta.com. 這個地址是可以在Alice
的終端(軟體電話)上面進行配置的,當然也可以通過DHCP之類的協議來發現. SIP伺服器接收SIP請求並按照其目的
進行轉發. 在本例中, 代理伺服器接收INVITE請求後,給Alice返回100(Trying)響應,表示請求正在進行轉發.
SIP響應用一個三位數來表示狀態,包含了和INVITE請求中同樣的To, From, Call-ID, CSeq 和 branch(via內)引數,
從而允許Alice的終端將其與請求相聯絡. 代理伺服器atlanta.com通過DNS等方法得到Bob的服務提供商地址.
並且在轉發的報文中的via欄位加上自己的地址資訊. biloxi.com代理伺服器接收到INVITE請求,並且返回100響應給
atlanta.com. 代理伺服器查詢其資料庫(通常稱為定位服務),其中包含了Bob的當前IP地址. 同時代理在轉發請求前
也在頭部的via欄位加上自己的地址.

Bob的終端(SIP電話)接收到INVITE請求後,會提示Bob這是來自Alice的來電.同時Bob的終端返回180響應,
表示正在呼叫,響應一直轉發回到Alice的終端,從而使Alice也能知道對方電話正在響.每個代理都通過頭部的Via
欄位來決定響應的傳送方向,並且從via頂部去掉自己的地址資訊. 因此雖然傳送請求的時候用到DNS和定位服務,
但是傳送響應的時候則不需要.

在本例中,Bob決定接起電話. 此時Bob的SIP電話傳送200響應表示呼叫被應答.200響應包含了資訊體(SDP)
表明Bob希望建立的會話型別.因此,這形成了兩次SDP資訊交換過程:Alice傳送給Bob,然後Bob傳送給Alice.
這個兩次交換提供了基本的協商能力,並且基於簡單的offer/answer模型. 如果Bob
不想接電話或者正在與別人通話,就會返回一個錯誤響應,從而不建立多媒體會話. Bob傳送的200響應結構大體如下:

  SIP/2.0 200 OK
  Via: SIP/2.0/UDP server10.biloxi.com
     ;branch=z9hG4bKnashds8;received=192.0.2.3
  Via: SIP/2.0/UDP bigbox3.site3.atlanta.com
     ;branch=z9hG4bK77ef4c2312983.1;received=192.0.2.2
  Via: SIP/2.0/UDP pc33.atlanta.com
     ;branch=z9hG4bK776asdhds ;received=192.0.2.1
  To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
  From: Alice <sip:alice@atlanta.com>;tag=1928301774
  Call-ID: a84b4c76e66710@pc33.atlanta.com
  CSeq: 314159 INVITE
  Contact: <sip:bob@192.0.2.4>
  Content-Type: application/sdp
  Content-Length: 131
  
  (Bob的SDP資訊,略)

                     圖 3: Bob傳送的響應報文

Bob的SIP電話增加了一個tag引數到報文頭部,這個tag會被兩個端點合併到對話裡,並且會在(本次通話)所有以後的請求和響應中包含.
Contact頭包含了一個Bob能直接連線的URI,Content-Type 和 Content-Length表示訊息體(沒貼出來)的格式資訊.
在本例中,代理伺服器也可以擴充自己的功能,比如當接收到Bob返回的486(Busy Here)響應,則可以向Bob的語音信箱等
傳送INVITE請求;一個代理伺服器可以同時向多個地址傳送請求,這種並行查詢的特性通常稱之為分叉(forking).

在200(OK)響應返回到Alice的軟體電話上之後,電話停止響鈴,並通知Alice對方已經接聽,同時傳送一個ACK報文到Bob
的終端,表示響應已經收到. 在本例中,ACK直接傳送給Bob,而不通過兩個代理伺服器,是因為兩個端點都知道了對方的地址,
因此不需要再通過代理去查詢.

收到ACK之後,Alice和Bob就可以互相通訊了. 通訊完成之後,假設Bob先結束通話電話,併產生一個BYE報文,直接傳送給Alice,
Alice收到後確認請求,並返回200(OK)響應,從而結束此次會話.注意這裡沒有傳送ACK,因為ACK只有在確認INVITE請求的響應時才傳送.

註冊(Registration)是另一個SIP常用的操作. 使用者通過註冊使得代理伺服器能知道其當前的地址資訊. 例如Bob
可以在初始化時,向biloxi.com傳送註冊請求(REGISTER),後者也稱為註冊商(registrar).
註冊商會將Bob的SIP URI和其當前地址相關聯起來(通常稱為繫結),並把這個對映資訊儲存到伺服器端的資料庫中,
亦即上文說到的定位服務. 通常註冊伺服器和對應域名的代理伺服器都是同一地址的,因此要知道SIP伺服器的型別的
區別體現在邏輯上而不是物理上.

SIP協議結構

SIP是一個分層的協議,這意味著其行為由一系列同級但獨立的段(stage)描述. SIP的最底層為語法和編碼,其中編碼由
BNF語法(Backus-Naur Form grammar)指定; SIP第二層為為運輸層(transport layer),定義了客戶端和服務端如何傳送和接收
請求和響應;第三層為事務層(transaction layer),事務層是SIP的基礎元件,一次事務包括髮送的請求和對應的響應,
事務層處理應用層的重傳,請求/響應匹配和超時等;事務層之上稱為事務使用者(TU, transaction user),每個SIP
實體(除了無狀態的代理),都是一個TU.

所有的SIP元素,包括使用者客戶端(UAC),伺服器(UAS),無狀態(stateless)或者全狀態(stateful)的代理,
以及註冊商,都包含一個區分彼此的核心(core). 其中除了無狀態的代理,其他元素的核心都是事務使用者.
UAC和UAS的核心行為依賴於方法,對於所有方法有一些通用規則,這裡不細說. 對於UAC而言,這些規則支配
著請求報文的構造.

SIP報文格式

SIP是基於文字(text-based)的協議,並且使用UTF-8字符集.一條SIP報文要麼是從客戶端到服務端的請求,
要麼是服務端到客戶端的響應;兩種型別的報文都包含一個起始行,一個或者多個頭部區域,一個表示頭部結束的空行,
以及(可選的)正文部分(message body),每個部分以CRLF隔開:

     generic-message  =  start-line
                         *message-header
                         CRLF
                         [ message-body ]
     start-line       =  Request-Line / Status-Line

除了字符集的區別,大多數SIP的報文和頭部語法都與HTTP/1.1相同,雖然如此,但SIP不是HTTP協議的擴充.

SIP Request

SIP請求的報文首行都包含一個請求行(Request-Line),請求行又包括方法名,請求URI以及協議版本,
並以SP(空格)分割除了在行尾,請求行不允許出現任何回車(CR)和換行(LF),元素中也不能出現行間的空字元(LWS, linear whitespace).

  Request-Line  =  Method SP Request-URI SP SIP-Version CRLF

其中:

  • Method: 表示方法,RFC3261定義了六個方法,分別是:
    • REGISTER: 用來註冊聯絡人資訊.
    • INVITE, ACK, CANCEL, BYE: 這四個方法用於會話的建立.
    • OPTIONS: 用來發現伺服器的效能(capabilities).
  • Request-URI: 即SIP或者SIPS URI,用來表示請求要送往的服務或使用者資訊,其中不能包括控制字元,
    也不能包含在"<>"之中.
  • SIP-Version: SIP的版本號,與RFC3261對應的是"SIP/2.0".和HTTP/1.1的處理類似,但不同點為SIP處理
    版本號是以字串的格式,雖然這在實踐中並沒什麼太大關係.

SIP Response

SIP響應與請求不同,其起始行為狀態行(Status-Line),狀態行包括協議版本,狀態碼以及對應的狀態文字說明,
和請求行類似,個元素以空格分隔,中間也不能出現換行和回車.

Status-Line  =  SIP-Version SP Status-Code SP Reason-Phrase CRLF

狀態碼(Status-Code)由3位數字組成,表示請求的結果. 狀態碼的第一位表示響應的種類:

  • 1xx(表示100-199,下同): 臨時響應(Provisional),表示請求已經被收到,但還在處理之中.
  • 2xx: 成功(Success), 請求被成功接收,理解以及被接受.
  • 3xx: 重定向(Redirection), 可能需要重新選擇傳送地址以完成請求.
  • 4xx: 客戶端錯誤(Client Error), 請求包含錯誤的語法,或者不能被伺服器完成.
  • 5xx: 服務端錯誤(Server Error), 伺服器處理一個合法的請求失敗.
  • 6xx: 失敗(Global Failure),

Header Fields

SIP報文的頭部和HTTP的頭類似, 也有同樣的性質,如在多個頭部區域指定同一個屬性的值時可以合併成一個頭部,
並使field-value以逗號分隔等,頭部的格式如下:

  field-name: field-value

冒號兩邊可以加任意空格,但是一般不建議這樣做,而是使filed-name和冒號間不留空格,並使冒號和field-value只見留一個空格.
以圖2中Alice的Request請求報文為例,大致介紹其中一些常見的Header field-name:

  • Via: 包含了傳送方想接受此次請求對應響應的地址,這裡是pc33.atlanta.com,
    並且還包含了識別此次傳輸事務的分支引數(branch parameter).
  • Max-Forwards: 用來限制請求的最大跳數(max hops),在每個hop之後遞減少.
  • To: 包含了此次請求的目的使用者的顯示姓名Bob(display name)以及SIP/SIPS URI(sip:bob@biloxi.com)
  • From: 包含了此次請求的傳送方的顯示姓名Alice和URI, 除此之外還有一個tag引數,包含了隨即的字串,
    將用於新增在URI中,主要用於驗證和區分.
  • Call-ID: 包含了此次通話中全域性不同的標識,由隨機字串和傳送端的主機名或IP地址組合而成.To,From和Call-ID
    欄位完全定義了一個Alice和Bob的端到端的SIP關係,並表示為當前對話(dialog).
  • CSeq: 或者寫為Command Sequence,包含了一個整數(CSeq號)和方法名,CSeq號在本次對話中隨著每次新的請求而遞增.
  • Contact: 包含代表直接連線Alice的SIP/SIPS URI. 和Via段不同的是,Via告訴其他單位要往那傳送響應,
    而Contact告訴其他單位要往哪發(以後的)請求.
  • Content-Length: 訊息體的長度.
  • Content-Type: 訊息體(message body)的格式, 如SDP資訊則為"application/sdp",關於SDP可以參考前一篇部落格P2P通訊標準協議(三)之ICE.

後記

本文簡單介紹了SIP協議的結構和報文格式, 其中有很多細節都沒有深入, 因此篇幅只有原文/RFC3261的十分之一,
如果要根據協議來設計實際的應用,還是需要仔細看一遍協議的原文. 至此, P2P通訊系列的介紹也就告一段落了.
P2P的去中心化,一直是個很令人振奮的話題,無論是在資訊科技上,還是在金融,政治上,都有無限潛力.
最近的一系列文章主要是P2P入門以及實現簡單的VOIP應用, 下一階段應該會研究下內容分發協議(如bittorrent),
不過以我的拖延症來看,那肯定是很久之後的事了.

本文連線http://www.cnblogs.com/pannengzhi/p/5103914.html
個人部落格 jekyll.pppan.net
文章可自由轉載,但請註明出處

相關文章