PJSIP開發指南

2022-08-03 13:15:12 字數 3134 閱讀 9161

一、通用設計

1.1   架構

1.1.1        通訊圖

下面的圖展示了sip訊息在pjsip元件間從後端到前端如何傳遞的。

1.1.2        類圖

下面的圖顯示類檢視

sip 協議棧的核心是sip endpoint,它由透明的pjsip_endpoint的表示,endpoint具有下面的屬性和職責

l  內儲存工廠,為所有的sip元件分配記憶體

l  具備定時器堆實列,為所有的sip元件排程定時器

l  傳輸管理起例項,傳輸管理器負責傳輸sip訊息並且控制訊息的解析和列印。

l  擁有單例項pjlib io 佇列,io佇列採用proactor模式分發事件。

l  提供了執行緒安全的輪詢功能,應用程式的執行緒能夠查詢定時器和socket事件(pjsip自身不常見任何執行緒)

l  管理pjsip模組,pjsip模組主要以為著擴充套件sip stack,而不僅僅限於訊息的解析和列印。

l  從傳輸模組接收sip訊息,並且分發到各個模組中。

1.2.1        記憶體池分配和釋放

為了保證執行緒的安全性以及在整個應用中策略強一致性,所有的sip記憶體都需要在endpoint中完成分配。比如:記憶體池快取,未使用的記憶體被保留在以後使用而不是銷毀。

endpoint提供下面的函式分配和釋放記憶體池

pjsip_endpt_create_pool()

pjsip_endpt_release_pool()

當endpoint被pjsip_endpt_create()建立時,應用一定要指明由endpoint使用的記憶體池工廠。在整個生命週期內endpoint持有記憶體池工廠的指標,將來備用建立和釋放記憶體。

1.2.2        定時器管理

endpoint 擁有乙個獨立定時器堆例項,所有sip元件的定時器建立和排程都需要通過endpoint 完成。endpoint提供下面的函式管理定時器

pjsip_endpt_schedule_timer()

pjsip_endpt_cancel_timer()

endpoint的poll函式被呼叫時,檢查定時器是否過期。

1.2.3         輪詢棧

endpoint 提供獨立的函式(pjsip_endpt_handle_events())為了檢查定時器和網路事件的出現,應用可以指定準備等待多久去檢查事件的出現。

pjsip 棧從不建立執行緒,整個棧的執行過程都依賴於應用所建立的執行緒,不管是api被呼叫或者是輪詢函式被呼叫。

輪詢功能優化等待時間依賴於定時器堆的內容,比如:當它找到定時器將在下個5s過期,他等待socket事件的時間,將不會比5s長,在無網路事件出現時,應用程式等待超過5s是沒有必要的,當然每個平台的定時器的精度是不一樣的。

1.3   執行緒安全和執行緒的相容性

1.3.1        執行緒安全

執行緒安全的討論是比較麻煩的事情,但是,幸運的是,下面的設計原則,在整個協議中的應用都保持了一致性。

物件一定是執行緒安全的,但是資料結構一定不是執行緒安全的。

看到這個主題,很自然的想到,物件和簡單資料結構的區別不是那麼清晰,但是有一些例子可以提供,可能更明白寫。

資料結構的例子:

pjlib的資料結構,比如:

l  鍊錶、陣列、雜湊表、字串、記憶體池。

l  sip訊息的元素,urls、header fields和sip訊息

這些資料結構並不是執行緒安全的,資料結構的執行緒安全由包含他們的物件保證。如果資料結構是執行緒安全的,將會嚴重的影響協議棧的效能並且消耗作業系統的資源。

相比之下,pjsip的物件一定是執行緒安全的,比如:

l  pjlib 物件,比如ioqueue

l  pjsip 物件,比如: endpoint、transactions、dialogs等。

1.3.2        複雜性

更糟糕的是,一些物件在標頭檔案中暴露了他們的宣告(pjsip_transaction 和 pjsip_dialog)。

雖然api暴露可以保證執行緒的安全性,應用在訪問資料結構前,必須通過pj_mutex_lock獲取到物件的互斥鎖。

更糟糕的是,dialog暴露的不同的api鎖定dialog,應用程式應該呼叫pjsip_dlg_inc_lock和pjsip_dlg_dec_lock() 替代pj­_mutex_lock和pj_mutex_unlock。兩種處理方式區別是:

dialog的inc/dec保證了dialog將不會被銷毀在函式呼叫時。而pj_mutex_unlock會因為dialog銷毀了而導致層序crash。

考慮下面的例子:

pj_mutext_lock(dlg->mutex)

pjsip_dlg_end_session(dlg,…)

pj_mutex_unlock(dlg->mutex)

在上面的例子中,應用可能會crash,因為pjsip_dlg_end_session可能會銷毀dialog在某些情況下。例如:invite事務沒有被應答,事務會被馬上銷毀,導致dialog被立刻銷毀。dialog

inc/dec可以通過增加dialog的引用計數,防止這類情況的發生。再end_session時,dialog 不會被銷毀,dialog 可能會在dec_lock函式中被銷毀。所以正確的呼叫順序應該如下:

pjsip_dlg_inc_lock(dlg)

pjsip_dlg_end_session(dlg)

pjsip_dlg_dec_lock

最最糟糕的是,鎖的呼叫順序一定是正確的,否則引起死鎖。比如:應用想去鎖dialog和dialog 的 transaction,應用必須獲得dialog mutex在獲取transaction mutex之前,否則當其他的執行緒以相反的順序拿鎖時,將會引起死鎖。

1.3.3         解決辦法

幸運的是,應用程式很少會直接獲取物件的鎖,所以上面的問題很少會發生。應用應該使用物件的api訪問物件,api會物件檢查,保證了lock的正確性,避免了死鎖和crash的產生。

應用程式的**被物件呼叫,這些**被呼叫時,物件的鎖已經獲取到了。所以應用能夠安全的獲取物件的資料結構,不必要獲取物件的鎖。

Django Web開發指南

國內第一本django圖書 django web開發指南 歡迎使用django 歡迎來到django的世界,很高興能和你一起進行這趟旅程。你會發現有了這個強大的web框架,做每件事情都變得便捷起來 從設計開發新應用到不用大刀闊斧地修改 就能為現有 提供新的特性和功能。關於本書 市面上已經有了一些講解...

SQL Server 開發指南

sql server 資料庫設計 一 資料庫設計的必要性 二 什麼是資料庫設計 三 資料庫設計的重要 四 資料模型 實體 關係 e r 資料模型 實體 entity 屬性 attribute 關係 relationship 五 資料庫設計步驟 1 需求分析階段 2 概要設計階段 3 詳細設計階段 六...

shell 開發指南

語法 釋義使用示例 bash source 0 取得當前執行的shell檔案所在的路徑 context dirname 上一條指令執行完的退出狀態.0為成功,非0為失敗.if eq 0 then 基本結構 if 條件語句 then 執行語句1 else 執行語句2 fi注意條件語句外有 兩種形式 常...