一,SIP協議簡述
源文:https://datatracker.ietf.org/doc/rfc3261/?include_text=1
簡單說,就是我想給你打電話,我倆怎么建立這個對話連接,於是SIP協議就是用來約定這個行為的
二:RFC中的關鍵術語的解析
1,UAC/UAS/UA
UAC:User Agent Client用戶代理客戶端
UAS:User Agent Server用戶代理服務端
UA:A logical entity that can act as both a user agent client and user agent server.
水鬼:何為User,我們都是用戶,我們如果想要打電話怎么利用SIP協議建立連接呢?當然要有個小程序,所以這個小程序就是UA, 在建立連接的時候總有一個人是發起者,那么他就扮演了Client的角色,被訪問的就是服務端
所以UA是實體,而UAC和UAS表示的是扮演的角色
2, TU:Transaction User
SIP協議的工作方式是以事物為單位的,一旦一個人想要打電話,要經歷撥號,連接,應答等,中間有多次交互,這樣一整套交互就是一個事物。
事物在工作過程中就是調用傳輸層的協議為我發數據,所以這個TU就是位於傳輸層的上層,在SIP協議中扮演了和傳輸層打交道的那個。
這個用戶包含如下三個核心(core): UAC core, UAS core, and proxy core.
這些core其實是代碼層面的概念,因為要實現sip協議,肯定要為不同的功能編寫函數,於是
UAC core:對於UA在行使其作為client的角色時,需要實現的功能函數
UAS core:server的角色
proxy core: 針對其proxy的功能的實現,何為proxy,類似與移動聯通,我給小名打電話,中間肯定有很多proxy幫我們轉發
二,事務的工作原理簡介
sip是一個事務協議, 即兩個組件之間的交互需要一系列獨立的message交換。進一步說就是,一個sip事務,包括一個請求和針對這個請求的多個應答,這些應答包括0個或多個臨時的應答以及一個或多個最終的應答。
具體的場景就是,一個事務從一個INVITE請求開始,如果最終應答不是2xx,那事務還包括一個ACK,否則ACK不認為是事務的一部分。
The 2xx response and its ACK receive special treatment. This response is retransmitted only by a UAS, and its ACK generated only by the UAC. This end-to-end treatment is needed so that a caller knows the entire set of users that have accepted the call. Because of this special handling, retransmissions of the 2xx response are handled by the UA core, not the transaction layer. Similarly, generation of the ACK for the 2xx is handled by the UA core. Each proxy along the path merely forwards each 2xx response to INVITE and its corresponding ACK.
SIP Uniform Resource Locators
SIP URLs are used within SIP messages to indicate the originator (From), current destination (Request-URI) and final recipient (To) of
a SIP request, and to specify redirection addresses (Contact). A SIP URL can also be embedded in web pages or other hyperlinks to indicate
that a particular user or service can be called via SIP. When used as a hyperlink, the SIP URL indicates the use of the INVITE method.
wxy:sip類型的url是用來定位sip請求消息是來自哪里(from),當前的目的地(Request-URI),和最終的接收端(to),以及用來指定redirection地址(contact).
一個sip url也可以被嵌入到網頁中,或者其他超連接上,用一指示某特定的用戶或者服務可以通過sip協議去呼叫該地址。當使用了超鏈接,sip url嗲標INVITE 方法。
Request-URI
The Request-URI is a SIP URL as described in Section 2 or a general URI. It indicates the user or service to which this request is being addressed. Unlike the To field, the Request-URI MAY be re-written by proxies. wxy:Request-URI可以是sip url(上一節說的),也可以是一個通用uri.他代表請求應該如何被尋址,不像To field,這個Request-URI是可以被proxy重寫的。
When used as a Request-URI, a SIP-URL MUST NOT contain the transport-param, maddr-param, ttl-param, or headers elements. A server that receives a SIP-URL with these elements removes them before further processing.
wxy:當sip url作為Request-URI,他必須包含傳輸參數,廣播參數,或者頭元素。一個服務器在接收到sip-url后會首先除去這些元素再做進一步處理
Typically, the UAC sets the Request-URI and To to the same SIP URL, presumed to remain unchanged over long time periods. However,
if the UAC has cached a more direct path to the callee, e.g., from the Contact header field of a response to a previous request,
the To would still contain the long-term, "public" address, while the Request-URI would be set to the cached address. Proxy and
redirect servers MAY use the information in the Request-URI and request header fields to handle the request and possibly rewrite
the Request-URI. For example, a request addressed to the generic address sip:sales@acme.com is proxied to the particular person,
e.g., sip:bob@ny.acme.com , with the To field remaining as sip:sales@acme.com. At ny.acme.com , Bob then designates Alice as the
temporary substitute. The host part of the Request-URI typically agrees with one of the host names of the receiving server.
If it does not, the server SHOULD proxy the request to the address indicated or return a 404 (Not Found) response if it is unwilling
or unable to do so. For example, the Request-URI and server host name can disagree in the case of a firewall proxy that handles
outgoing calls. This mode of operation is similar to that of HTTP proxies. If a SIP server receives a request with a URI indicating
a scheme other than SIP which that server does not understand, the server MUST return a 400 (Bad Request) response. It MUST do this
even if the To header field contains a scheme it does understand. This is because
proxies are responsible for processing the Request-URI; the To field is of end-to-end significance.
wxy:典型的,uac設置R-URI 和 To為同一個sip URL,然而如果uac已經該
三,報文解析
1.first line
例子:INVITE sip:bob@biloxi.com SIP/2.0
含義:INVITE類型的請求,使用sip協議/服務,向位於biloxi.co這個sip服務器上的bob發邀請,版本為SIP/2.0
------------------------+ /r/n,作為分隔,不屬於任何------------------------
2,message header
每一個header由 /r/n結束,即一個header field包含了 /r/n
via:
While the Via header field tells other elements where to send the response,
(via是用來告訴其他人向哪里發送應答)
The Via header field indicates the transport used for the transaction and identifies the location where the response is to be sent. A Via header field value is added only after the transport that will be used to reach the next hop has been selected (which may involve the usage of the procedures in [4]).
(當transport選擇了下一跳到哪里后,就添加一個via) When the UAC creates a request, it MUST insert a Via into that request. The protocol name and protocol version in the header field MUST be SIP and 2.0, respectively. The Via header field value MUST contain a branch parameter. This parameter is used to identify the transaction created by that request. This parameter is used by both the client and the server.
(當一個UA創建一個請求時,他必須在請求中insert一個via,並且這個via必須包含一個branch 參數,
這個參數用來定位此次事物是由哪個請求創建的,這個參數既給client用,也給server用,必須是全空間唯一的)
Request Forwarding:
The proxy MUST insert a Via header field value into the copy before the existing Via header field values.
Forward response:
The proxy removes the topmost Via header field value from the response.
If no Via header field values remain in the response, the response was meant for this element and MUST NOT be forwarded.
This will result in the response being sent to the location now indicated in the topmost Via header field value.
Route
The Route request-header field determines the route taken by a request. Each host removes the first entry and then proxies the request to the host listed in that entry, also using it as the Request-URI
wxy:路由字段用於確認請求向哪里發送,每一個host在接收到這個請求后,首先移除最上層的條目,然后根據接下來的條目確認向哪里轉發這個請求,一般用這個ip作為Request-URI。
The Record-Route request and response header field is added to a request by any proxy that insists on being in the path of subsequent requests for the same call leg. It contains a globally reachable Request-URI that identifies the proxy server. Each proxy server adds its Request-URI to the beginning of the list.
wxy:the path of subsequent requests for the same call leg。這是什么意思,我的理解就是一次通話,從最開始的invite請求,之后還要有ring請求,bye請求等,所以叫做subsequent request
請求每到一個proxy,就會把自己的ip添加到請求中,這個ip是一個全局可達的Request-URI類型地址,添加的方式是添加到list的beginning上
The server copies the Record-Route header field unchanged into the response. (Record-Route is only relevant for 2xx responses.) The calling user agent client
copies the Record-Route header into a Route header field of subsequent requests within the same call leg, reversing the order of requests, so that the first
entry is closest to the user agent client. If the response contained a Contact header field, the calling user agent adds its content as the last Route header.
Unless this would cause a loop, any client MUST send any subsequent requests for this call leg to the first Request-URI in the Route request header field and
remove that entry. The calling user agent MUST NOT use the Record-Route header field in requests that contain Route header fields. Some proxies, such as those
controlling firewalls or in an automatic call distribution (ACD) system, need to maintain call state and thus need to receive any BYE and ACK packets for the call.
wxy:服務器在接收到請求后,會不做任何改變將route拷貝到response中(route只用於2xx對應的response)。這時候打電話的那個uac接收到應答后,把應答中的這些route翻轉過來,最下面的一個變成最上面的
后續的請求,也就按照從上到下的路由轉發請求
即 A(請求)-->張三--李四-->王五--->B, 收到的請求,route從最里層到最外層分別是3,4,5; B封裝應答的時候,原樣不變
A<------------------B(應答)
A收到應答后,把route翻轉,從最外層到最里層是3,4,5,這樣A再發請求到B的時候,還是會沿着3,4,5這條路徑走下去
Stateless Proxy
對於無狀態proxy,轉發請求和轉發應答的原則跟上面說的一致,但更多的還有如下的規則:
When a response arrives at a stateless proxy, the proxy MUST inspect the sent-by value in the first (topmost) Via header field value.
If that address matches the proxy, (it equals a value this proxy has inserted into previous requests) the proxy MUST remove that header
field value from the response and forward the result to the location indicated in the next Via header field value.
當一個應答到達了無狀態proxy,proxy必須期待 first via的sent-by值是匹配自己,因為這樣意味者當初請求就是從我這里轉發出去的,然后proxy必須要將這個via從response中移除
然后根據下一個via中的值去定位,接下來應答應該向哪里轉發
wxy:uac創建請求,添加第一個via,這個via實際就是自己,到了第一個proxy,
proxy會將自己的位置信息封裝到一個新via中並添加到header中,via-proxy,via-origin
最后,請求到達了終點服務器uas根據接收到的請求,via保持原來的,生成應答后,發送給proxy
proxy接收到帶有兩個via的應答后,首先取出topmost via,也就是via-proxy進行比對看看是不是自己,然后剝離,然后根據via-origin將應答轉發給origin
uac接收到應答,檢查via-origin必須是自己
例子:Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds
1, the address (pc33.atlanta.com) at which Alice is expecting to receive responses to this request
2,the Contact header field tells other elements where to send future requests.
------------------------+ /r/n,作為分隔,不屬於任何,另外如果沒有body,也是需要由這個結束的分隔---------------(也就是說header的結束有兩個 /r/n,一個屬於行的,一個屬於header的)
3,message body
每一個filed之間------+ /r/n,作為分隔,不屬於任何filed,最后一個filed之后也是有/r/n,------
整個body的長度是只所有的內容 + 所有的/r/n,這個值等同header中的content_length的值
---------------------------------
REGISTER報文
The REGISTER request allows a client to let a proxy or redirect server know at which address(es) it can be reached.
A client MAY also use it to install call handling features at the server.
客戶通過發注冊請求讓proxy或者重定向服務器知道去哪里找客戶,客戶也可以使用這個請求在這個服務器上安裝一些call handling功能
wxy:我們有13xxx這個電話號碼,但是得讓基站知道我們物理位置在哪,那么通過的方式就是我的電話到達某個區域,就會在這個區域的基站上注冊
A client uses the REGISTER method to register the address listed in the To header field with a SIP server.
Clients may register from different locations, by necessity using different Call-ID values. Thus,
the CSeq value cannot be used to enforce ordering. Since registrations are additive, ordering is
less of a problem than if each REGISTER request completely replaced all earlier ones. 一個客戶,可能位於不同的地方去注冊,所以不可避免的會使用不同的Call-ID值,所以不能用Cseq的值去作為一個順序的標記。
由於注冊機制是一個增量的,所以順序號不夠用的問題要比注冊請求會完全覆蓋掉之前的麻煩
We define "address-of-record" as the SIP address that the registry knows the registrand,
typically of the form "user@domain" rather than "user@host". In third-party registration,
the entity issuing the request is different from the entity being registered.
我們用aor即address-of-rocord作為登記者的sip地址,這是一個registry即注冊服務器用來識別登記者的一個地址,
典型的格式是 用戶@域名,而不是 用戶@主機名。對於第三方注冊,表示注冊請求請求報文的發起者和想要注冊的那個不是一人
To: The To header field contains the address-of-record whose registration is to be created or updated.
該頭域存放的是一個將要添加或更新的aor
From: The From header field contains the address-of-record of the person responsible for the registration.
For first-party registration, it is identical to the To header field value.
該頭域存放的是一個aor條目,用來代表是誰負責這次注冊。對於first-party注冊,這個字段域to字段相同
Request-URI: The Request-URI names the destination of the registration request, i.e., the domain of the registrar.
The user name MUST be empty. Generally, the domains in the Request-URI and the To header field have the same value;
however, it is possible to register as a "visitor", while maintaining one's name. For example, a traveler
sip:alice@acme.com (To) might register under the Request-URI sip:atlanta.hiayh.org , with the former as the To header
field and the latter as the Request-URI. The REGISTER request is no longer forwarded once it has reached
the server whose authoritative domain is the one listed in the Request-URI.
Request-URI表示的是注冊請求的目的地,即注冊服務器的域名。用戶名部分必須要是空的。一般來說,Request-URI中的域名和To頭域中的域名相同。
然后,如果是一個"visitor"要注冊,這時候就要攜帶用戶名。比如sip:alice@acme.com (TO 頭域中的內容是這樣的) ,而Request-URI中的內容是:
sip:atlanta.hiayh.org。一旦請求報文到達某個服務器,而這個服務器的認證域名就是Ruest-URI列表中的一個域名,那么請求就算是到了....
Call-ID: All registrations from a client SHOULD use the same Call-ID header value, at least within the same reboot cycle.
wxy:這個call-id和之后的invite沒有什么關系
Cseq: Registrations with the same Call-ID MUST have increasing CSeq header values. However, the server does not reject out-of-order requests.
Contact: The request MAY contain a Contact header field; future non-REGISTER requests for the URI given in the To header field
SHOULD be directed to the address(es) given in the Contact header.
對於之后的非registry請求,如果想發請求給我(To 字段中的URI),則請求因該被轉發到contact中給定的地址。
小小結:我alice是個移動客戶端,別人想給我發請求並不知道我具體在哪里,所以我需要提前向sip服務供應商報備我的信息,即注冊
1,首先是Rquest-Line,也就是first line,主要說明注冊大廳是誰,即我要向誰注冊,具體說就是傳輸層我將請求發給服務器,應用層我告知我是想向這個域名對應的注冊服務器注冊
一般情況下,我們是不是想,我向誰發報文,自然而然這個目的地的域名不就自動有了么,但是貌似上層應用可能包含多個域名,所以這里還是要指定下
包含三部分 1)Method:REGISTRY 2)Request-URI:表示注冊服務器的域名 3)
2,然后是Message Header
TO表示是誰要注冊,From表示這個注冊請求是從誰那里發出來的,如果是第三方幫忙注冊,則二者不同。 注冊的內容是一個aor形式的條目,是 用戶名@注冊服務器域名,用這個aor代表用戶,是用戶的對外地址
Contact表示這個注冊用戶的實際物理地址,內容的格式是 用戶名@用戶自己的host
終極小例:
A user at host saturn.bell-tel.com registers on start-up, via multicast, with the local SIP server named bell-tel.com.
In the example, the user agent on saturn expects to receive SIP requests on UDP port 3890. 某用戶的主機域名是saturn.bell-tel.com,然后想要在udp:3890上接收其他人的sip請求
wxy:這里不知道你是否有疑惑,為什么用戶的域名和sip注冊服務器的域名有這么重和,我的理解是域名這東西是一個級連形式,一層層由域名服務器提供服務
而我們在注冊的時候,往往都是向我們最近的sip服務器(比如手機向基站注冊,就是我的范圍內最近的基站)注冊,逐個服務器同事具備域名解析功能,
所以我這么已注冊,既得到一個專有的sip名字,也得到一個這個專網的域名,當然saturn是我真正網絡內host名
C->S: REGISTER sip:bell-tel.com SIP/2.0
Via: SIP/2.0/UDP saturn.bell-tel.com
From: sip:watson@bell-tel.com
To: sip:watson@bell-tel.com
Call-ID: 70710@saturn.bell-tel.com
CSeq: 1 REGISTER
Contact: <sip:watson@saturn.bell-tel.com:3890;transport=udp>
Expires: 7200
用戶watson向注冊服務中心bell-tel.com發注冊請求,注冊名稱為:watson@bell-tel.com,對應實際的物理地址是saturn.bell-tel.com:2890
The registration expires after two hours. Any future invitations for watson@bell-tel.com arriving at sip.bell-tel.com will
now be redirected to watson@saturn.bell-tel.com, UDP port 3890.
If Watson wants to be reached elsewhere, say, an on-line service he uses while traveling, he updates his reservation after first
cancelling any existing locations:
如果watson想要能夠被別的其他訪問,
C->S: REGISTER sip:bell-tel.com SIP/2.0
Via: SIP/2.0/UDP saturn.bell-tel.com
From: sip:watson@bell-tel.com
To: sip:watson@bell-tel.com
Call-ID: 70710@saturn.bell-tel.com
CSeq: 2 REGISTER
Contact: *
Expires: 0
C->S: REGISTER sip:bell-tel.com SIP/2.0
Via: SIP/2.0/UDP saturn.bell-tel.com
From: sip:watson@bell-tel.com
To: sip:watson@bell-tel.com
Call-ID: 70710@saturn.bell-tel.com
CSeq: 3 REGISTER
Contact: sip:tawatson@example.com
Now, the server will forward any request for Watson to the server at example.com, using the Request-URI tawatson@example.com.
For the server at example.com to reach Watson, he will need to send a REGISTER there, or inform the server of his current location
through some other means.
It is possible to use third-party registration. Here, the secretary jon.diligent registers his boss, T. Watson:
使用第三方代注冊,由jon代替他的老板watson去注冊
C->S: REGISTER sip:bell-tel.com SIP/2.0
Via: SIP/2.0/UDP pluto.bell-tel.com
From: sip:jon.diligent@bell-tel.com
To: sip:watson@bell-tel.com
Call-ID: 17320@pluto.bell-tel.com
CSeq: 1 REGISTER
Contact: sip:tawatson@example.com
The request could be sent to either the registrar at bell-tel.com or the server at example.com.
In the latter case, the server at example.com would proxy the request to the address indicated in the
Request-URI. Then, Max-Forwards header could be used to restrict the registration to that server.
---------------------------------
INVITE報文:
我們知道,客戶端在和sip server交互使用的端口號可以是任意的,根據實驗發現如下規律
1,注冊報文只是注冊地址,不包括端口號,即核心就是Contact字段,這個字段並沒有端口號
所以,客戶端使用哪個端口號去注冊,發出去的INVITE報文都不是說一定會和注冊的使用一個------這個還需要等會再實驗一下,
但是,一般情況下,因為注冊完就會發請求,所以往往時使用相同的端口號!!!!!
而是默認都會使用5060,一旦被占用就會使用別的udp端口 -----但是有一次實驗,好想也使用了5060,所以這個還有待於進一步驗證。
2,使用任意端口號發INVITE報文時,受影響header field包括:Contact,Via,他們的uri中port部分和傳輸層的端口號相同。
3,當有uac呼叫我的時候,常常會看到server會轉發過來兩個INVITE請求,第一個是向着我的5060端口,第二個是向着我的實際端口號
這兩個INVITE請求內容除了上述字段的port不同之外,包括body再內(rtp/rtcp),其余都是相同的
注:以上是實驗所得,還未來得及去rfc中找理論支撐。
Ring報文:
ring報文中的Contact是被呼叫者的實際(具體)的聯系方式,
wxy:當呼叫發起方得到這個信息后,並不會直接以sip協議和被呼叫者通信,而是拿着這個聯系方式給sip服務器發消息,告訴他我要和這個人通話....
---------------------------------
四,客戶端UAC的行為特點(或者是實現的要求)
1,有關狀態機
我要打電話了,我首先會發送INVITE請求,在發送前,TU首先會創建一個事物,初始狀態為“calling”,然后發請求,如何發?
1)如果是基於不可靠的連接,他會先起一個定時器A,時常為T1,然后發請求,如果T1時間內沒有收到應答,則重發,此時定時器設置為2T1,再之后為double上一次的時間,但最長不能長於定時器B(64個T1)
2)如果是基於可靠的連接,沒有定時器A,但有定時器B,因為是基於連接的,所以不怕收不到應答,但是一旦收不到,B時間到了,一樣完蛋
2,有關連接(connection)
對於使用TCP,SCTP,或者TLS協議的,都是基於連接的,所以再進行傳輸之前,首先要建立連接。
對於傳輸層來說,需要管理這些連接,無論是自己主動發起的還是被動連接的,當然這個連接是雙方一起建立的,所以對於這些連接信息,sip協議是share的。
sip協議針對每一個連接用一個index來記錄,一個index由連接的遠端(另一頭)的地址,port,協議類型 這三元組來生成。有以下幾個特點
1)對於連接的發起者index=目的ip + 目的port + 傳輸協議
2)對於連接的接收者index=源ip + 源port + 傳輸協議,
對於這些已經建立好的連接,sip的兩個UA在有傳輸任務的時候,其實是可以復用的。但由於源port往往是隨機的,所以無法復用,那么如果之前的接收者想作為發起者發送數據了
他就需要重新建立一條連接。最終,對於經常有交互的UA之間,會存在兩條建立好的連接,然后不斷不斷的復用着
四,關於sip如何使用傳輸層的
(一),客戶端發送請求
1,客戶端在構造請求報文的時候,會在 Via 字段的頭域中嵌入一個"sent-by"字段,這個字段包含兩部分:ip地址或域名, 端口號。這個字段用來指導服務器向哪里回應答,如果沒有port,則會使用缺省端口(udp/tcp/sctp:5060; tls:5061)
1)如果是可靠傳輸
因為是基於連接的,所以從哪里接收就向哪里回應。
但如果server接收請求后,如果連接斷了,那server就會重起連接,所以發送端必須在自己"sent-by"中指定的地址:端口上時刻准備着,准備着被連接
2)如果是不可靠的傳輸
發送端嵌入了"sent-by"字段,那么你就應該在"sent-by"字段中指定的地址+port上時刻准備着,准備着接收服務端的應答
(二),客戶端接收應答
當應答回來了,客戶端會檢查應答中的 VIa 頭域中的"sent-by"字段,一般來說這個字段是來自當初請求中的該字段,所以我回檢查他是否和我的配置匹配,如果不匹配則銷毀之
(三),服務端接收請求
1,作為服務器,你要注意在可能的接口上做好被連接的准備。所謂可能的接口,舉個例子:你對外說xx域名代表我,那么這個域名背后的ip:port一定是可以被連接,說白了就是發布出去的路,自己一定保證路是通的。
2,所有接口上的udp/tcp/sctp:5060, tls:5061必須是可以接收連接的。(不過也有例外,私網環境和一個機器上部署多實例sip服務器的場景)
3,如果一個服務器為UDP協議在一個port上listen了,那么他必須也要為TCP協議在該port上監聽,那是因為當包很大時,雙方可能隨時切換協議進行傳輸。
反之,則不必。
而且一般來說,服務端不需要為udp監聽某個地址和port,因為已經在為tcp監聽了,當然也有寫特殊的需求
水鬼子:這里着實有點蒙,udp為什么要監聽? tcp監聽了udp就不需要了么? 原文是:
A server need not listen for UDP on a particular address and port just because it is listening on that same address and port for TCP.
There may, of course, be other reasons why a server needs to listen for UDP on a particular address and port.
4,如果接收到的包的源ip和"sent-by"字段指定的ip不同,則server需要在Via 的頭上增加一個字段,叫"received",表示實際接收包的ip,這個字段用來幫助傳輸層去回復應答,也就是說這個應答必須要回給接收到請求的那個源地址,說白了就是哪里收到回哪里
(四),服務端發送應答
1,如果是基於可靠傳輸的(比如tcp,sctp,tls),如果在發送應答時連接還在,則基於連接回復應答
如果連接沒有了,則server重新和received“建立連接,然后再send response
2,如果Via header字段中包含“maddr”參數,則需要回給列表中所有的地址,使用的端口號就是"sent-by"中指定的端口號,如果沒有指定,那么就是5060端口號
3,如果基於不可靠的傳輸的單播傳輸,回應答給“received“指定的地址 和 "sent-by"中指定的端口號,如果端口號沒有指定,那么就是5060
4,如果沒有“received“標識,則應答會給 "sent-by"中指定的地址 + 端口號
====================================================================
終極例子
0: atlanta.com是Ailce所屬的sip服務器, 簡稱 A 和A側代理
biloxi是bob所屬的sip服務器,簡稱B和B側代理
--------------begin--->
F1 INVITE Alice -> atlanta.com proxy INVITE sip:bob@biloxi.com SIP/2.0 Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8 Max-Forwards: 70 To: Bob <sip:bob@biloxi.com> From: Alice <sip:alice@atlanta.com>;tag=1928301774 Call-ID: a84b4c76e66710 CSeq: 314159 INVITE Contact: <sip:alice@pc33.atlanta.com> Content-Type: application/sdp Content-Length: 142 (Alice's SDP not shown) 1,Alice要向bob發邀請,首先會把請求發給自己的sip服務供應商atlanta,bob所在的sip服務供應商為biloxi,Alice自己的主機地址(這里面使用的是域名)是pc33.atlanta.com
via:將自己的地址添加到via上,為了讓接下來的proxy知道一回回應答的時候回給哪里(這個哪里是細化到應用進程,即ip:port,當然port要是沒有那就是缺省port)
contact:表示如果bob想要和我聯系,你應該知道我是誰,或者說我的聯系方式是什么。
wxy:bob的主機地址或者說網絡地址Alice是不知道的,所以他只管將請求發給sip服務器,sip服務器是知道bob在哪里的
至於如何知道的,那當然后registry報文的功勞,因為各個uac都會將自己的地址信息注冊到所屬的sip服務供應商,或者叫服務器那里
而Alice自己當然知道自己的主機地址,所以報文中會攜帶具體的地址信息
所以pc33.atlanta.com 和 alice@atlanta.com是兩個概念
A ---> A側代理 invite
----------------------------------
F2 100 Trying atlanta.com proxy -> Alice
SIP/2.0 100 Trying
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Content-Length: 0
2,atlanta實際是作為proxy接收到了請求,於是針對這個請求先回應一個臨時應答,這個應答里的via是在請求的基礎上增加"received"這個參數,表示我是從哪個ip接收到的請求
A <--- A側代理 100
----------------------------------------
F3 INVITE atlanta.com proxy -> biloxi.com proxy
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8 ;received=192.0.2.1
Max-Forwards: 69
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 142 (Alice's SDP not shown)
3,atlanta會繼續將請求轉發給biloxi這個服務器/proxy,在轉發之前會新建一個via,把自己的地址信息添加進來,自己的地址信息這里是一個域名,即proxy的域名:bigbox3.site3.atlanta.com
然后把這個新via insert到報文頭中
A側代理 ---> B側代理 invite
------------------------------------------------------
F4 100 Trying biloxi.com proxy -> atlanta.com proxy
SIP/2.0 100 Trying
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=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Content-Length: 0
4,回復臨時應答給atlanta,同樣在topmost via中添加上"received"參數
A側代理 <--- B側代理 100
----------------------------------------------------------
F5 INVITE biloxi.com proxy -> Bob
INVITE sip:bob@192.0.2.4 SIP/2.0
Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1
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=z9hG4bKnashds8
;received=192.0.2.1
Max-Forwards: 68
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 142
(Alice's SDP not shown)
5,sip 服務供應商(proxy)將invite請求發給最終的uac,但是在發送前同樣需要把自己的位置信息封裝到一個via中insert到headers里
B側代理 ---> B invite
------------------------------------------------ F6 180 Ringing Bob -> biloxi.com proxy SIP/2.0 180 Ringing Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1 ;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=z9hG4bKnashds8 ;received=192.0.2.1 To: Bob <sip:bob@biloxi.com>;tag=a6c85cf From: Alice <sip:alice@atlanta.com>;tag=1928301774 Call-ID: a84b4c76e66710 Contact: <sip:bob@192.0.2.4> CSeq: 314159 INVITE Content-Length: 0
6,bob接收到請求后,自己的手機會響零,同時回發ring報文給自己的proxy,之后由proxy最終告知A:和我之間的通路是通的,並且你要占領該通路並保持着。
如何知道是哪個proxy呢,當然是根據topmost via知道的,一看原來是biloxi這個proxy,器地址為server10.biloxi.com
wxy:這里sip協議的"事務"的概念就出來了,首先sip的上層應用會將invite請求和branch即“事務”編碼對應上,圍繞這個事務創建ring報文,進而知道是針對這個invete的,所以可以從invite中的via提取出來下一步向哪里發送
另外,所有的via在uac那里是不會被remove的,只由在proxy那里被remove
還有就是,ring請求不會改動via,但是會重新生成contact
注:這個響鈴其實就是我們打電話的時候,所謂的通了,但是我還沒接聽。
B側代理 <--- B ring
--------------------------------------------------------------
F7 180 Ringing biloxi.com proxy -> atlanta.com proxy
SIP/2.0 180 Ringing
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=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.0.2.4>
CSeq: 314159 INVITE
Content-Length: 0
7,proxy之間轉發via,接收時檢查topmost via,然后remove之,最后轉發出去
A側代理<--- B側代理 ring
-----------------------------------------------------------
F8 180 Ringing atlanta.com proxy -> Alice
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.0.2.4>
CSeq: 314159 INVITE
Content-Length: 0
8,供應上/server/proxy轉發ring,具體說來是:接收后檢查topmost via,然后remove之,根據next via轉發出去
A<--- A側代理 ring
----------------------------------------------------------
F9 200 OK Bob -> biloxi.com proxy
SIP/2.0 200 OK
Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1
;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=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4> ----這個contact有變化了
Content-Type: application/sdp
Content-Length: 131
(Bob's SDP not shown)
9.bob回應答,即我接聽了,比如按了接聽鍵,首先給自己所屬的sip供應商回,via的原理同ring
wxy:這里仍然用到了“事務”的概念,上層事務模塊在處理過程中用id和最原始的invite請求對應上,進而讓知道transport模塊知道向哪里回應
B側代理 <--- B 200 OK
-----------------------------------------------------------
F10 200 OK biloxi.com proxy -> atlanta.com proxy
SIP/2.0 200 OK
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=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4>
Content-Type: application/sdp
Content-Length: 131
(Bob's SDP not shown)
10,proxy之間的轉發,略
A側代理 <--- B側代理 200 OK
-----------------------------------------------------------
F11 200 OK atlanta.com proxy -> Alice
SIP/2.0 200 OK
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4>
Content-Type: application/sdp
Content-Length: 131
(Bob's SDP not shown)
11,服務供應商/proxy將應答轉發給最終的目的地,也是invite的源頭
A <--- A側代理 200 OK
-----------------------------------------------------------
F12 ACK Alice -> Bob
ACK sip:bob@192.0.2.4 SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds9
Max-Forwards: 70
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 ACK
Content-Length: 0
The media session between Alice and Bob is now established.
Bob hangs up first. Note that Bob's SIP phone maintains its own CSeq
numbering space, which, in this example, begins with 231. Since Bob
is making the request, the To and From URIs and tags have been
swapped.
12,alice回ack給bob,表示我知道你結聽了,現在協議層面的三次握手正式建立
wxy:同樣的,"事務"的概念在這里就起作用了,讓alice知道這個ack是為哪個invite而產生
ACK只對應invite
注:從這個事件開始,事
A ---> B ACK
-----------------------------------------------------------
F13 BYE Bob -> Alice
BYE sip:alice@pc33.atlanta.com SIP/2.0
Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10
Max-Forwards: 70
From: Bob <sip:bob@biloxi.com>;tag=a6c85cf
To: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 231 BYE
Content-Length: 0
13,bob掛電話了,即按了掛斷鍵,於是給alice發bye報文
A <--- B BYE
-----------------------------------------------------------
F14 200 OK Alice -> Bob
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10
From: Bob <sip:bob@biloxi.com>;tag=a6c85cf
To: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 231 BYE
Content-Length: 0
A ---> B 200 OK
---------------------------------------------------------------------------
總結:
1,關於via和contact,以及first line中的Reuest URl
via的作用是告訴下游proxy之后向哪里回送應答,
contact是告訴對方我的聯系方式是啥,
打個比方
alice將自己的聯系方式(contact=名字@住址)和住址(via=住址)寫在明信片上,然后把明信片交給郵局(proxy),
郵局(proxy)會添加一個自己的via(via=郵局的住址)
然后郵局直接進行中轉
最后郵局把明信片郵給bob
contact是身份信息,是uac專有屬性,是給其他uac看的,用來聯系我;
via是位置信息,是給proxy或者uac定位看的,via不是uac專有的,每一個路過的都有
via在請求的創建時,生成第一個via,然后上行沿途轉發的過程中逐漸insert via
基於這個請求的所有via生成應答,然后下行沿途轉發的過程中逐漸remove via
contact同樣是請求創建的時候生成contact,然后一路跟隨直到目的地
基於這個請求查創建應答的時候,生成新的contact,然后再一回跟隨回到發起者
如果對端也向向我發后續的請求,則就向這個地址發
Record-Route/Route:是在經過proxy時由proxy添加進來的,這樣如果有后續的請求,則也從這些proxy走過
via只是指導應答應該走過哪些proxy。route是告訴接收應答或者中間的那些人,如果你想針對此次通話發請求,則同樣也走這條路
比如:a--張三---李四---b,則如果b要發ring請求(針對這個會話的),則也要走李四,張三這條路,盡管通過王二麻子也是能到a那里,但是不行/不推薦
INVITE是純請求,創建via和contact
200 OK是純應答,復制INVITE的所有via,然后創建自己的contact
180 Ringing既是請求也是應答,作為應答他會復制原來INVITE請求的所有via,作為請求他會創建自己的contact
注冊過程:
REGISTER sip:bell-tel.com SIP/2.0 -----注冊服務器的地址,sip uri without user
Via: SIP/2.0/UDP saturn.bell-tel.com ---地址(域名),普通地址
From: sip:watson@bell-tel.com ----注冊報文來自哪里,aor格式地址
To: sip:watson@bell-tel.com ---給誰注冊的,同時也是寫到注冊服務器中的條目的樣子
發起過程
INVITE sip:bob@biloxi.com SIP/2.0 ---向誰發請求,aor類型格式地址是一個對方在注冊服務器中記錄的地址
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8 ---地址(域名),普通地址,表示經過的proxy
Max-Forwards: 70
To: Bob <sip:bob@biloxi.com> ----向誰發請求,aor地址
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com> ----- 表示后續你若想向我再發請求,則發到這里來
=====================================================================================
服務器:開源軟件安裝及使用的命令
/usr/local/opensips/sbin/opensipsctl restart /usr/local/opensips/sbin/opensipsctl stop /usr/local/opensips/sbin/opensipsctl start
客戶端:
安裝的Yate client
1,關於客戶端使用的端口號
缺省是使用的5060,一旦這個端口號被占用,則會自動使用其他端口號,所以為了達到你想要的效果可以手動啟動兩個client進程!
2,