SipUA Invite UDP 重發(fā)

現(xiàn)象描述

在使用 pjsua2 makeCall 的時(shí)候發(fā)現(xiàn)我的 SipUA 可以完成注冊和返回 407 授權(quán)失敗的 INVITE 事務(wù),但 攜帶授權(quán)信息 的 INVITE 消息達(dá)不到對端(對端抓不到包),經(jīng)查是本地一直在重發(fā)。

經(jīng)查,兩次的 INVITE 包的差別只有符合預(yù)期的三個(gè)地方:branch 標(biāo)簽變化、CSeq 加一和攜帶了 Proxy-Authorization 頭。

image.png

反思:這個(gè)現(xiàn)象經(jīng)過很長事件的調(diào)查才調(diào)查到,這是第一個(gè) 不該:一開始只想到是程序本身某配置問題,而未想到用抓包確認(rèn)。

問題猜想

錯(cuò)誤的猜想:昨日猜想是網(wǎng)絡(luò)問題,我本地是在 Windows 內(nèi)的 WSL 運(yùn)行的,公司還有一個(gè)網(wǎng)關(guān),然后才到測試環(huán)境的一個(gè) FS。但這無法解釋 Register 消息和沒帶授權(quán)信息的 INVITE 是可以正常收發(fā)的。

今日忽然想到:可能是 UDP 消息太大了,導(dǎo)致到某個(gè)位置被扔掉了。

問題驗(yàn)證(含知識點(diǎn))

知識點(diǎn)一:學(xué)會看 WireShark 工具抓包信息

證明 IP 分包:通過將 sngrep 保存下來,看到 Reassembled in #N 應(yīng)該就是 IP 重組的包。(用 WireShark 抓包結(jié)果和 sngrep 一樣。)

image.png

在 WireShark 中可通過配置關(guān)閉 IPv4 數(shù)據(jù)報(bào)的重組(一般打開便于閱讀)。


image.png

信息二:確定是因?yàn)?SIP 包過大導(dǎo)致丟包的!

首先 SIP 包過大并不是大問題,從網(wǎng)上查來看,UDP 包最大能支持 65507 字節(jié),但由于包太大分組再重組后發(fā)生校驗(yàn)錯(cuò)誤的概率增大,所以丟包的可能性會大很多。

我找到了 pjsua2 中設(shè)置 codec 的代碼,將不需要的 codec 都禁用了,然后再次禁用 TCP 傳輸,呼叫就可以正常進(jìn)行了。因此,可以確定是因?yàn)楸镜?SIP 包過大導(dǎo)致丟包的問題。

信息三:誰丟了 UDP 包

如果是網(wǎng)絡(luò)上的某些網(wǎng)元會將此 UDP 包丟棄的話,那確實(shí)沒辦法查了。但好在我嘗試了下在 Windows 測試,用 MicroSip 設(shè)置開啟了支持所有的媒體編碼類型,結(jié)果抓包發(fā)現(xiàn)整個(gè)帶授權(quán)頭的 INVITE 消息有 2500 多的字符,同樣在 IP 層有分包發(fā)送,但呼叫狀態(tài)正常。(同樣看網(wǎng)上解釋,UDP 最大可支持 65507 字節(jié)的)

所以問題就在我本地 WSL 的網(wǎng)絡(luò)環(huán)境,與其他無關(guān)。

TODO: 自己寫個(gè)程序,在客戶端控制發(fā)送 UDP 報(bào)文的大小,在服務(wù)端驗(yàn)證接收報(bào)文的大小,測試在 WSL 和 Windows 下是否現(xiàn)象不一致。

TODO: WHY?

知識點(diǎn)四:UDP 分片相關(guān)

UDP 分片會增大丟包的風(fēng)險(xiǎn),因?yàn)?IP 重組如果重組后校驗(yàn)不對,就會丟棄包。

參考

問題解決

用 TCP 方式解決

用 TCP,在 pjsua 中設(shè)置 UDP 和 TCP 都開啟的狀態(tài)下呼叫電話,底層會自動切換到用 TCP 傳輸 帶授權(quán)狀態(tài)的 INVITE 消息(TODO:底層實(shí)現(xiàn),是否是一種普遍方法)。

減小包體

禁用掉許多 codec 減小 SDP 信息大小,同時(shí)關(guān)閉 TCP 傳輸?shù)氖褂茫烧:艚辛恕?/p>

        # ep 是 pj.Endpoint 的實(shí)例
        ep.codecSetPriority("speex/16000", 0)
        ep.codecSetPriority("speex/8000", 0)
        ep.codecSetPriority("speex/32000", 0)
        ep.codecSetPriority("iLBC/8000", 0)
        ep.codecSetPriority("GSM/8000", 0)
        ep.codecSetPriority("G722/16000", 0)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容