1. 程式人生 > >2008/1/31 OSIP協議棧使用入門(續一:純協議棧邏輯分析)

2008/1/31 OSIP協議棧使用入門(續一:純協議棧邏輯分析)

http://mbstudio.spaces.live.com/blog/cns!C898C3C40396DC11!2860.entry

2008/1/31
OSIP協議棧使用入門(續一:純協議棧邏輯分析)

  (CopyLeft by Meineson | www.mbstudio.cn,原創文章,歡迎轉載,但請保留出處說明!)


  很長時間之前,簡單粗略地看了下Osip,eXosip,ortp等並快速“封裝”了一個Windows下的基於VC6的MFC的SIP軟電話(全部原始碼VC6工程檔案及Lib庫可在本Blog共享資料夾找到),由於時間限制,只能是一知半解地純“應用”式地分析了一下osip,eXosip等開發庫的程式碼,作為興趣愛好者參考瞭解下SIP電話工作原理還可以,但作為商用產品開發參考則還是太淺顯了些:)
  最近擴充套件嵌入式Linux平臺上的SIP功能模組(基於OSIP),由於使用的Osip不包括Call Transfer相關欄位(Refer,Notify等)的解析和狀態機控制(最近的Osip版本是否有擴充套件未檢視)不能支援呼叫轉接,需要手工擴充套件,有機會對Osip的主要事務狀態機、解析庫等部分稍有了些較深入的瞭解,結合SIP RFC總結分享如下。
  (注:下文假設閱讀者已經大概瞭解SIP協議的簡單呼叫流程,會使用Ethereal等抓包工具分析SIP訊息結構,對C語言的指標、連結串列、記憶體控制及狀態機等概念有足夠的認識。)

  要應用Osip到我們的程式中去,首先要看官方文件,文件中對Osip協議棧提供的各個功能部件如何使用都有比較詳細的描述,但未進行整體性的分析,某些中文的指導文件也都停留在對其簡單的翻譯,不能為不熟悉該協議棧使用的使用者快速參考使用,本文件不按照Osip的程式碼進行按功能分塊說明,而是根據實際使用時的程式碼使用順序來對主要邏輯流程進行分析,並適當對流程中使用到的功能部件進行說明,具體更詳細的功能說明或疑問可直接檢視官方文件對應部分的解釋或直接檢視功能函式原始碼即可解決。
準備工作
先認識幾個結構體:osip_t,osip_message_t,osip_dialog_t,osip_transaction_t;
  osip_t是一個全域性變數,所有要使用Osip協議棧的事務處理能力的程式都要第一步就初始化它(相對應於只使用osipparser庫進行SIP訊息欄位解析的應用來說,如果只使用parser庫到自己的程式中,想必對SIP協議棧已經很熟悉了,不需再往下看了^_^),它內部主要是定義了Osip協議棧的四個主要事務連結串列、訊息實際傳送函式及狀態機各狀態事件下的回撥函式等;
  osip_message_t是SIP訊息的C語言結構體儲存空間,收到SIP訊息解析後存在該結構中方便程式使用接收到的訊息中的指定的欄位,傳送訊息前為方便設定要傳送的欄位值,將要傳送的內容存在該結構中等傳送時轉為字串;
  osip_dialog_t則是SIP RFC中的dialog或叫call leg的定義,它標識了uac和uas的一對關係,並一直保持到會話(session)結束,一個完整的dialog主要包括from,to,callid,fromtag,totag,state等(可檢視原始碼),其中fromtag,totag,callid在一個dialog成功建立後才完整,體現在SIP訊息中,就是From、To的tag,Call-id欄位的值相同時,這些訊息是屬於它們對應的一個Dialog的,例如將要發起invite時,只有fromtag,callid填充有值,在收到to遠端的響應時,收到totag填充到dialog中,建立成功一個dialog,後繼的邏輯均是使用這個dialog進行處理(如transaction事務處理),state表示本dialog的狀態,與transaction的state有很大的關聯,共用由Enum結構state_t定義;
osip_transaction_t則是RFC中的事務的定義,它表示的是一個會話的某個Dialog之間的某一次訊息傳送及其完整的響應,例如invite-100-180-200-ack這是一個完整的事務,bye-200這也是一個完整的事務,體現在SIP訊息中,就是Via中的branch的值相同表示屬於一個事務的訊息(當然,事務是在Dialog中的,所以From、To的tag,Call-id值也是相同的),事務對於UAC,UAS的終端型別不同及訊息的不同,分為四類,前面說的invite的事務,主叫uac中會關聯一個ict事務,被叫uas會關聯一個ist事務,而除了invite之外,都歸類定義主叫nict,被叫nist,在Osip中,它是靠有限狀態機來實現的上述四種事務(osip_fsm_type_t中定義)的,它的主要屬性值有callid,transactionid,分別來標識dialog和transaction,其中還有一個時間戳birth_time標識事務建立時間,可由超時處理函式用來判斷和決定超時情況下的事務的進行和銷燬,而它的state屬性是非常重要的,根據上述的事務型別不同,其值也不同,它是前面提到的狀態機的“狀態”,在實際狀態機的邏輯執行中是一個關鍵值;
Osip初始化
  提到osip的初始化,可能大家都看過官方文件裡第一頁的程式碼,首先就是osip_init(&osip)初始化了全域性的osip_t結構體,然後對它的回撥函式進行設定,很多人估計就是一看到這密密麻麻的一頁多的call_back設定被嚇到了,但結合前面分析的三個結構體的含義,這裡的含義就很清晰了:
  osip_t中有一個cb_send_message函式指標,它是Osip最終與外界網路互動的介面,它的引數有( osip_transaction_t * trn, /*本訊息所屬的事務*/
osip_message_t * sipmsg, /*待發送的訊息結構體*/
char *dest_socket_str, /*目標地址*/
int32_t dest_port, /*目標埠*/
int32_t send_sock)    /*用來發送訊息的socket*/
  其中trn傳入主要是為了方便獲取事務的上下文資料,它有一個void指標your_instance,可以用來傳入更多資料方便傳送訊息時參考,例如將該事務所屬的dialog指標傳入;
  而sipmsg則是我們要傳送的SIP訊息的C結構體,使用osip_message_to_str將其按RFC文件格式轉換為一個字串(osip中的parser模組的主要功能),再通過任意你自己的網路資料傳送函式使用send_sock傳送給dest_socket_str和dest_port指定的目標,當然,要記得使用osip_free釋放剛才傳送出去的字串佔用的記憶體,Osip中很多osipparser提供的訊息解析處理函式都是動態記憶體分配的,使用完畢後需要及時釋放;
  使用osip_set_cb_send_message成功設定回撥函式,我們的SIP訊息就有了出口了,下面繼續分析(當然,瞭解到了上面的流程,也可以手工指定了)。

  下面的回撥函式分為三類,分別是普通事務訊息(osip_message_callback_type_t中定義)的處理回撥函式、事務銷燬事件(osip_kill_callback_type_t中定義)的清理回撥函式以及事務執行過程中的錯誤事件(osip_transport_error_callback_type_t中定義)處理回撥函式:
  先說簡單的,事務銷燬事件,事務正常結束(成功完成狀態機流程)或由超時處理函式強制終結等情況下均呼叫了這些回撥函式,一般就是釋放事務結構體,為ICT,NICT,IST,NIST各設定或共用一個回撥函式均可,只要正確釋放不再使用的記憶體即可;
  錯誤處理函式則是在整個狀態機執行過程中發生的任何錯誤的出口,一般用來安插log函式方便除錯,也可以直接設為空函式;
  而最關鍵的就是正常訊息的處理回撥函數了,其量是非常大的,但仔細分下類,也和上面的回撥函式一樣,也是分為四類,我們可有根據實際程式的需要來進行設定,例如,SIP電話機就不需要處理OSIP_NIST_REGISTER_RECEIVED這個SIP註冊伺服器才需要處理的Register訊息事件了,精簡一下,如果只是要做一個只需要實現主叫功能且不考慮錯誤情況的UAC的Demo軟電話程式,則只需要設定如下幾個事件的回撥函式:
  OSIP_ICT_INVITE_SENT 發出Invite開始呼叫
  OSIP_ICT_STATUS_1XX_RECEIVED 收到180
  OSIP_ICT_STATUS_2XX_RECEIVED 收到200
  OSIP_ICT_ACK_SENT  發出ack確定呼叫
  OSIP_NICT_BYE_SENT  發出bye結束呼叫
  OSIP_NICT_STATUS_2XX_RECEIVED 收到200確認結束呼叫
  OSIP_NIST_BYE_RECEIVED 收到bye結束呼叫
  OSIP_NIST_STATUS_2XX_SENT 發出 200確定結束呼叫
而要增加接受呼叫的被叫UAS功能,則只需要增加如下事件:
  OSIP_IST_INVITE_RECEIVED 收到invite開始呼叫
  OSIP_IST_STATUS_1XX_SENT 發出180
  OSIP_IST_STATUS_2XX_SENT 發出200
  OSIP_IST_ACK_RECEIVED  收到ack確認呼叫
具體的函式定義,則直接參考osip_message_cb_t,osip_kill_transaction_cb_t,osip_transport_error_cb_t即可,回撥函式的設定同上可以手工設定,也可以使用Osip提供的對應的osip_set_xxx_callback函式;
發出SIP訊息
  要傳送SIP訊息,從上面的分析可知有幾個必要的條件,osip_messag_t結構的待發送訊息,osip_dialog_t結構體的dialog以及osip_transaction_t的事務;
  首先osip_malloc新分配一個dialog,使用osip_to_init,osip_to_parse,osip_to_free這類parser函式功能函式按RFC設定call-id,from,to,local_cseq等必要欄位(原則是:後面生成實際SIP訊息結構體要用到的欄位就需要設定),使用osip_message_init初始化一個sipmsg,根據dialog來填充該結構體(不同的訊息填充的資料是不同的,沒有捷徑可走,只能看RFC根據需要填充欄位),如果要給SIP訊息新增Body例如SDP段,需要使用osip_message_set_body,osip_message_set_content_type函式,設定的值是純文字,如果是SDP,Osip有提供簡單的解析和生成便捷函式例如sdp_message_to_str,sdp_message_a_attribute_add,但只是簡單的字元操作,要填充合法的欄位需要自己參考SDP的RFC文件,同樣沒捷徑可走。
  現在我們有了兩個必要條件了,還有最後一個也是最關鍵的部件,就是事務的建立和觸發,
int osip_transaction_init(
osip_transaction_t ** transaction, /*返回的事務結構體指標*/
osip_fsm_type_t ctx_type, /*事務型別ICT/NICT/IST/NIST*/
osip_t * osip,  /*前文說的全域性變數*/
osip_message_t * request) /*前面生成的sipmsg*/
  建立了一個新的事務,並自動根據事務型別、dialog和sipmsg進行了初始化,最重要的是它使用了__osip_add_ict等函式,將本事務插入到全域性的osip_t結構體的全域性FIFO連結串列中去了,不同的事務型別對應不同的FIFO,由前文可知,本類函式有四個,FIFO也有四個,對應ICT,NICT,IST,NIST,注意這個這裡使用osip_transaction_set_out_socket把傳送sip訊息的socket介面配給該事務,方便自動呼叫前面設定的傳送訊息回撥函式使用它自動傳送訊息;
  前文提到了transaction裡的state作為狀態機的“狀態”,要執行狀態機,就需要有“事件”來觸發,事件結構體osip_event_t需要使用osip_new_outgoing_sipmessage來對sipmsg進行探測生成,設定正確的事件值,省卻了我們手工設定的工作,它呼叫evt_set_type_outgoing_sipmessage來設定“事件”type_t,並將sipmsg掛到事件結構體的sip屬性值上,有了根據訊息分析出的事件後,使用osip_fifo_add(trn->transactionff, ev)將事件插入到事務的事件FIFO中,即transactionff屬性;
  
  有了上面的傳送訊息的必要條件了,訊息是如何實際出發的呢?上面提到了,SIP訊息的傳送和響應是一個事務,不能隔離開來,即訊息的傳送需要事務狀態機來控制,我們上面設定了狀態機的狀態和事件,要觸發它,就是要執行狀態機了:
  osip_ict_execute
  osip_nict_execute
  osip_ist_execute
  osip_nist_execute
  分別用來遍歷前面提到的四個事務FIFO,取出事務,再依次取出事務內的事件FIFO上的事件,使用osip_transaction_execute依次執行(有興趣的可以更深一步去檢視,可以看到它最終就是呼叫了我們前面設定的訊息回撥函式,至於具體呼叫哪個,這就是OSIP協議棧內部幫我們做的大量的工作了^_^);
  如果某個事務不能正常終結怎麼辦呢?例如發出了Invite沒有收到任何響應,按RFC定義,不同的事務有不同的超時時間,osip_timers_ict[nict|ist|nist]_execute這些函式就是來根據取出的事務的時間戳與當前時間取差後與規定的超時時間比對,如果超時,就自動設定了超時“事件”並將事務“狀態”設為終結,使用前面設定的訊息超時事件回撥函式處理即可(如果設定了);
  如果網路質量不穩定,經常丟失訊息,需要使用osip_retransmissions_execute函式來自動重發訊息而不是等待超時;
  為了即時響應SIP訊息的處理推動狀態機,上述的九個函式需要不停執行,可以將它放入單獨執行緒中。
收到SIP訊息
  有了前面的傳送SIP訊息的理解,接收訊息的處理就方便理解了,收到SIP訊息,使用osip_parse進行解析,得到一個osip_message_t的sipmsg,使用evt_set_type_incoming_sipmessage得到事務的“事件”,並同上將sipmsg掛到事件結構體的sip欄位,隨後立即使用osip_find_transaction_and_add_event來根據“事件”查詢事務(有興趣可以深入看一下,事務的查詢是通過SIP訊息Via中的branch來匹配的),否則新建事務,然後推動狀態機執行。
狀態機內部邏輯
  弄清了上面的狀態機的大概邏輯,設定正確完備的回撥函式,就可以正確使用Osip來進行工作了,如果要進一步深入Osip,比如要擴充套件Osip的狀態機處理自定義的訊息欄位和實現新的事務邏輯來生成新業務時,就需要對狀態機的內部邏輯有一定的瞭解;
  前面一再強調,Osip內部的幾個重要的資料結構osip_message_t,osip_dialog_t,osip_transaction_t,其中面向使用者的主要是前後兩個,而中間的dialog則很多時候是在狀態機內部使用的,例如:收到訊息,解析到sipmsg中,查詢transaction並進行驅動,隨後找到它關聯的dialog(或者新生成)解析填充要傳送的訊息結構體sipmsg,再次根據dialog和sipmsg查詢或生成transaction。
  如果要擴充套件Osip,要做工作主要有:
  擴充套件osip_message_t,增加要解析的欄位或訊息頭,並參考原Osip函式生成對應的SIP字串生成和解析函式;
  擴充套件osip_dialog_t,增加新的屬性,對應osip_message_t的新增內容;
  擴充套件狀態機的事件和狀態型別,設定對應的回撥函式,並關聯新增事件和狀態型別到osip_message_t的解析函式或osip_dialog_t的初始化函式中,而osip_transaction_t大多數時候不需要擴充套件,只要在對應的事務型別(大多數時候是NICT、NIST)處理邏輯中,增加對新增事件和狀態型別的判斷和呼叫回撥函式的邏輯即可。

(不定期更新補充中……)

  (CopyLeft by Meineson | www.mbstudio.cn,原創文章,歡迎轉載,但請保留出處說明!)
15:59:04 | 寫入日誌
評論 (6)

Windows Live 使用者 - 2008年 7 月 10 日
wow gold wow gold wow gold wow gold wow gold wow gold wow gold wow gold -39238267250129

Windows Live 使用者 - 2008年 9 月 8 日

Hi,Do you need digital signage, digital sign, ad players and ad displays? Please go Here:www.amberdigital.com.hk(Amberdigital).we have explored and developed the international market with professionalism. We have built a widespread marketing network, and set up a capable management team dedicated to provide beyond-expectation services to our customers.

amberdigital Contact Us

E-mail:

[email protected]
website:www.amberdigital.com.hk
alibaba:amberdigital.en.alibaba.com[afd

趙 晉 - 2008年 10 月 24 日
真的很強大,太崇拜你了,對您blog裡的文章有一種恨晚的感覺,受益匪淺!!謝謝

劉 iekliu - 2009年 4 月 5 日
寫得真好,我是一名初學者,有些地方還是不太明白,博主的sip軟電話原始碼能發給我看看嗎
[email protected]

小 林 - 2009年 12 月 16 日
請email一份 例子 學習下,謝謝。 自己給予 EXosip寫的程式 發不出去訊息。

小 林 - 2009年 12 月 16 日
[email protected]

若要新增評論,請使用您的 Windows Live ID 登入。
引用通告
此日誌的引用通告 URL 是:
http://mbstudio.spaces.live.com/blog/cns!C898C3C40396DC11!2860.trak

相關推薦

2008/1/31 OSIP協議使用入門協議邏輯分析

http://mbstudio.spaces.live.com/blog/cns!C898C3C40396DC11!2860.entry 2008/1/31OSIP協議棧使用入門(續一:純協議棧邏輯分析)  (CopyLeft by Meineson | www.mbstu

1 小時 SQL 極速入門

前幾天,我在論壇溜達。看到一個人發帖說 做了6年的企業級開發,總是被網際網路行業的人認為沒技術含量,不就是CRUD麼 先解釋下 CRUD 是什麼。CRUD 就是我們常說的增刪改查(Create,Retrieve,Update,Delete) 其實,對這個問題,我也思考過。我們所有的業務流程,最終

1 小時 SQL 極速入門

上篇我們說了 SQL 的基本語法,掌握了這些基本語法後,我們可以對單表進行查詢及計算分析。但是一個大的系統,往往會有數十上百張表,而業務關係又錯綜複雜。我們要查的資料往往在好幾張表中,而要從多張表中來獲取資訊就需要用到表聯結了。 先說說什麼是聯結,聯結就是用一條 SELECT 語句從多個表中查詢資料。通

1 小時 SQL 極速入門——分析函式

1 小時 SQL 極速入門 前面兩篇我們從 SQL 的最基礎語法講起,到表聯結多表查詢。 大家可以點選連結檢視 1 小時 SQL 極速入門(一) 1 小時 SQL 極速入門(二) 今天我們講一些在做報表和複雜計算時非常實用的分析函式。由於各個資料庫函式的實現不太一樣,本文基於 Oracle 12c 。 R

1 小時 SQL 極速入門——分析函數

logs 都是 spa 平均值 code word har 種類 auto 轉自:https://www.cnblogs.com/injet/p/10122832.html 前面兩篇我們從 SQL 的最基礎語法講起,到表聯結多表查詢。大家可以點擊鏈接查看1 小時 SQL 極

Python爬蟲入門+進階學習筆記 1-6 瀏覽器抓包及headers設定案例爬取知乎

爬蟲的一般思路:抓取網頁、分析請求解析網頁、尋找資料儲存資料、多頁處理 本節課主要講授如何通過谷歌瀏覽器開發者工具分析真實請求的方法。尋找真實請求的三個步驟分析:使用谷歌瀏覽器開發者工具分析網頁的請求測試:測試URL請求中每個引數的作用,找出控制翻頁等功能的引數重複:多次重複

幾種開源SIP協議對比文章很早了,僅供參考

幾種開源SIP協議棧對比 2006-12-05 15:49   基於SIP的開發的話,基本還是使用開源的協議棧的,當然也有牛的公司和單位是自己開發協議棧的,不過並不一定比開源的好用,不過可能使用在特定場合時,會有一定優勢吧。   在以前開發UA時,我用的

11.python並發入門part15 關於I/O多路復用

python select poll epoll io多路復用 一、為什麽要產生I/O多路復用?兩個主機之間通信,主機A和主機B都需要開啟socket,主機A首先要等待客戶端來進行連接,這是會發起一個recvfrom的系統調用,如果主機B一直沒有去連接主機A,沒有給主機A發送任何數據,進程

Storm入門Twitter Storm源代碼分析之CoordinatedBolt

業務 什麽 協議 ack 第一個 ng- rec 功能 這一 作者: xumingming | 可以轉載, 但必須以超鏈接形式標明文章原始出處和作者信息及版權聲明網址: http://xumingming.sinaapp.com/811/twitter-storm-code

WEB微信協議詳註

心跳檢測 心跳同步 先放一張效果圖以上的基礎都是建立在正確同步心跳之上;呵呵,界面做的很醜哈,不過關鍵是功能實現了。再次強調一次: 同步中所用的synckey 第一次所需的synckey是在微信初始化時返回的字串中,在開啟同步心跳的時候第一次提交的synckey就是來源於此;

WPF自學入門WPF MVVM模式Command命令

tps xaml 使用 company change 用戶 通過命令 AC 聯系人 在WPF自學入門(十)WPF MVVM簡單介紹中的示例似乎運行起來沒有什麽問題,也可以進行更新。但是這並不是我們使用MVVM的正確方式。正如上一篇文章中在開始說的,MVVM的

Java基礎入門之基本數據包裝類以及簡單轉換

數據包 intvalue nbsp 1.5 lse false 永遠 ring jdk 一、 基本數據類型包裝類 引用數據類型一般為基本數據類型首字母大寫,除了int 、char,其中int的引用數據類型類Integer,char的引用數據類型為Character 關

《SQL入門經典》筆記第二章建立資料庫之資料型別

“建立資料庫”包括五個內容:定義資料結構、管理資料庫物件、規格化過程、操作資料以及管理資料庫事務   1. 什麼是資料型別? 資料型別用於指定特定列所包含資料的規則,它決定了資料儲存在列裡的方式。SQL最基本的資料型別有字串、數值、日期和時間(其實每個實現都有自己的資料型別

C語言入門廿之static、extern關鍵字

區域性變數  概念: 區域性變數就是定義在函式, 程式碼塊和函式形參列表中的變數, 我們就稱之為區域性變數  作用範圍: 從定義的那一行開始一直直到遇到大括號結束或者遇到return為止  特點:     相同作用域範圍內不

python入門

except all 打印 化工 print trac pen txt not 類和方法 創建類 class A(object): def add(self, a,b ): return a+b count = A() print(count.a

java多線程快速入門

lee read .get java多線 als pub syn this ble 在方法上面加synchonizd用的是this鎖 package com.cppdy; class MyThread7 implements Runnable { privat

java多執行緒快速入門

在方法上面加synchonizd用的是this鎖 package com.cppdy; class MyThread7 implements Runnable { private Integer ticketCount = 100; public boolean falg = tr

Python入門File檔案方法

Python File(檔案) 方法 file 物件使用 open 函式來建立,下表列出了 file 物件常用的函式: 序號 方法 描述 1 file.close() 關

8筆記--OSPF路由協議-單域

OSPF已經瞭解了一定的基礎。那麼,為什麼要用OSPF,它適用於怎樣的環境。1.使用OSPF的原因:(1)網路的規模:當網路中的路由器在十臺以上或大中型規模的網路環境(2)網路的拓撲結構:網路的拓撲結構為網狀,並且任意兩臺路由器之間都有互通的需求(3)其他的網路需求:要求路由變化時能夠快速收斂,路由協議自身的

python ZeroMQ實現1:N,非同步收發訊息也可向指定客戶端傳送訊息

ZeroMQ的python版本和C/C++版本的介面差不多,要實現一個server對N個client,非同步方式,而且可以對指定的client傳送訊息,可以這樣: server採用ROUTER方式,cl