2020-07-19 弱網(wǎng)絡(luò)環(huán)境下的抗丟包技術(shù)分析

所謂的弱網(wǎng)絡(luò)環(huán)境就是網(wǎng)絡(luò)不是很好,比如無線wifi,跨多層網(wǎng)絡(luò)路由、或者網(wǎng)路負載過大等等情況。這樣數(shù)據(jù)在傳輸種會發(fā)生丟失的情況。

再說,網(wǎng)絡(luò)協(xié)議,傳輸層分udp和tcp.本人對tcp做了專門的分析。見下圖。


看圖,我只開了開了2%的丟包,導(dǎo)致發(fā)送速度從>500Mb/s下降到 <160Mb/s。速度下降比率達70-80%,發(fā)生這個現(xiàn)象的原因是TCP的擁塞處理,當發(fā)現(xiàn)網(wǎng)絡(luò)丟包的時候,就啟動擁塞控制機制,降低發(fā)送速度,達到tcp可靠性傳輸?shù)哪康?。大于大?shù)據(jù)和視頻的傳輸,速度在傳輸成降低這么多,很難滿足應(yīng)用層的需求,如果是視頻就會導(dǎo)致視頻的卡頓。因為接收端收不到需要的視頻幀,只有停下來花更多的時間等待。實時上筆者在實際開發(fā)視頻傳輸?shù)臅r候使用過tcp,在我無線網(wǎng)絡(luò)的情況下,tcp確實特別容易卡頓。關(guān)于tcp擁塞控制,大家可以搜索查閱相關(guān)的資料,這個不做細說。

筆者用的丟包工具是clumsy0.2。


我們查明了tcp卡頓的原因后,決定放棄tcp,那只有udp了,但是udp又不可靠,會有丟包和亂序的問題,導(dǎo)致視頻播放的花屏,這個花屏是因為關(guān)鍵幀或者參考幀丟失了,導(dǎo)致后界的視頻無法正常的恢復(fù),從而出現(xiàn)花屏。于是有查閱了相關(guān)的資料,找到了一個udt 協(xié)議。

關(guān)于udt協(xié)議,網(wǎng)上的說明如下:

基于UDP的數(shù)據(jù)傳輸協(xié)議(UDP-based Data Transfer Protocol,簡稱UDT)是一種互聯(lián)網(wǎng)數(shù)據(jù)傳輸協(xié)議。UDT的主要目的是支持高速廣域網(wǎng)上的海量數(shù)據(jù)傳輸,而互聯(lián)網(wǎng)上的標準數(shù)據(jù)傳輸協(xié)議TCP在高帶寬長距離網(wǎng)絡(luò)上性能很差。顧名思義,UDT建于UDP之上,并引入新的擁塞控制和數(shù)據(jù)可靠性控制機制。UDT是面向連接的雙向的應(yīng)用層協(xié)議。它同時支持可靠的數(shù)據(jù)流傳輸和部分可靠的數(shù)據(jù)報傳輸。 由于UDT完全在UDP上實現(xiàn),它也可以應(yīng)用在除了高速數(shù)據(jù)傳輸之外的其它應(yīng)用領(lǐng)域,例如點到點技術(shù)(P2P),防火墻穿透,多媒體數(shù)據(jù)傳輸?shù)鹊取?/p>

??? 按照這個及時他是基于udp的協(xié)議,又是可靠的,那么是否不會有tcp丟包擁塞的問題。

于是對udt協(xié)議做了測試,利用其項目代碼里面的demo測試。

udp是開源的項目,下載地址是:https://sourceforge.net/projects/udt/。目前是udt4.


如下圖是沒有丟包干擾的情況下的發(fā)送截圖:


開了丟包干擾又會如何呢,見下圖。



我們看到udt 協(xié)議也是一樣的開了丟包干擾后速度下降更多。那為什么呢,不是說udt是基于udp的協(xié)議,而udp不會發(fā)生擁塞控制么?為此筆者閱讀了udt的代碼,發(fā)現(xiàn)他就是在udp的基礎(chǔ)上做加了應(yīng)答處理,讓udp變得可靠,當然也有“擁塞控制算法”,只不過是應(yīng)用層的,筆者認為由于這個控制算法是基于應(yīng)用層的,而且發(fā)展的時間,使用的頻率都沒有tcp長,所有在發(fā)生擁塞的時候的表現(xiàn)自然要弱于tcp很多。

那么筆者又想到一個問題,那為什么要有udt協(xié)議呢?直接使用tcp不是挺好么?筆者又查閱了資料,其實在此文關(guān)于udt介紹里面就有答案:由于UDT完全在UDP上實現(xiàn),它也可以應(yīng)用在除了高速數(shù)據(jù)傳輸之外的其它應(yīng)用領(lǐng)域,例如點到點技術(shù)(P2P),防火墻穿透,多媒體數(shù)據(jù)傳輸?shù)鹊?。udt就是為了做p2p翻墻容易 和 傳輸可靠而已。

至此,關(guān)于本文標題的答案探索,似乎走向了死角,所有的探索都失敗了。在筆者不屑的努力下,終于又柳暗花明。


筆者查到了webrtc項目關(guān)于抗丟包處理策略,簡稱為QOS-FEC-NACK技術(shù),F(xiàn)EC 中文意思是向前糾錯。NACK 是選擇性重傳,QOS 是處理亂序問題。基于這樣一整套在應(yīng)用層的丟錯重傳機制,最大程度了保證了視頻的傳輸?shù)目煽啃浴O旅婀P者分別來介紹這個技術(shù)。

首先介紹FEC(Forward Error Correction),F(xiàn)EC算法的原理,參見這個博客https://blog.csdn.net/u010178611/article/details/82656838。這里貼出其博客的全文。

筆者經(jīng)過半年的技術(shù)攻關(guān)開發(fā)出了QOS-NACK-FEC抗丟包傳輸協(xié)議。


閱讀了FEC的糾錯原理后,筆者有了疑問:FEC生成冗余包大小跟原始包是一樣的,那么實際上是增加了傳輸?shù)臄?shù)據(jù)量,發(fā)生丟包的時候,為什么還要增加數(shù)據(jù)量。筆者做了一個計算。加入一個視頻幀有100個包,生成了10個冗余包,傳輸?shù)臅r候會丟到9個包,那么視頻還會恢復(fù)。但是如果沒有這10個冗余包,即使只丟一個包視頻也無法恢復(fù)。這就是FEC的好處,在一定數(shù)量的丟包的情況下,可以快速的恢復(fù)視頻,因為不用請求重傳,所以恢復(fù)速度快。只有當丟失過多,比如丟失了11個包,無法恢復(fù)的時候,需要請求重傳。

NACK是丟包請求重傳,下面介紹一下其實現(xiàn)原理,和系統(tǒng)框架設(shè)計。

NACK可以放置在原來的FEC-QOS傳輸層之外,作為上層應(yīng)用層,這種實現(xiàn)方式NACK將FEC-QOS看做普通的UDP傳輸,二者并無緊密結(jié)合,其優(yōu)勢是可以與成熟的NACK方案無縫銜接。我們知道任何NACK方案都必將引入延時抖動,因為接收端在發(fā)起重傳請求后,需要等待發(fā)送端重新發(fā)出的數(shù)據(jù),在“重傳等待時間”內(nèi)不對外輸出數(shù)據(jù)。而QOS階段里為解決UDP亂序包的問題也引入了一個“丟包等待時間”,當遇到包序號不連續(xù)時,將等待這一時間,若仍未收到所需的包則認定丟包,不再等待。如果將這兩個時間合二為一,可以盡量的降低系統(tǒng)時延和抖動,畢竟我們需要的是一個高實時性的NACK傳輸方案。我們將NACK的發(fā)起和等待放置在QOS之中,入下圖所示:

圖1 在QOS中實現(xiàn)NACK發(fā)起

當QOS檢測到序號不連續(xù)時,可能是發(fā)生丟包或者是亂序,此時QOS將通過FEC解碼模塊分析當前疑似丟包是否將導(dǎo)致FEC無法恢復(fù)。此時將產(chǎn)生三種分析結(jié)果:

A、當前丟包即使丟了也不影響FEC恢復(fù),比如當前丟失的包為一個或者多個冗余包,且該冗余包所在的group內(nèi)的媒體包均已接收,或者借助已接收的冗余包足夠恢復(fù)。

B、當前丟包不能確定是否影響FEC恢復(fù),需要接收更多的包才能確定。比如丟包發(fā)生在group的中段且丟的數(shù)量小于冗余包總數(shù)。

C、當前丟包將導(dǎo)致FEC確定無法恢復(fù),比如同一個group內(nèi)丟失的包數(shù)大于冗余包總數(shù)。

對于情況A,QOS將直接不予等待,將后續(xù)接收的包直接交與FEC。對于情況B,QOS將進入“丟包等待時間”,以期收到亂序的包。對于情況C,QOS將發(fā)起NACK重傳并進入等待,這個等待時間即是“丟包等待時間”又是“重傳等待時間”,在等待期內(nèi)不管是該亂序包到達或者重傳包到達,都能滿足FEC的恢復(fù)條件。

在介紹了NACK的發(fā)起條件后,我們來關(guān)注“重傳等待時間”的取值問題。若設(shè)置固定的重傳等待時間將很難滿足各類網(wǎng)絡(luò)情況。時間過小將導(dǎo)致重傳包尚未到達,QOS已結(jié)束等待并輸出后續(xù)包,后續(xù)即便再收到重傳包也將直接丟棄。重傳包也可能因網(wǎng)絡(luò)原因丟包,若“重傳等待時間”過大,將導(dǎo)致更大的延時和抖動。為了提高自適應(yīng)能力,系統(tǒng)通過實時獲取當前網(wǎng)絡(luò)的UDP通訊RTT時間來作為“重傳等待時間”的參考,計算出合理的值。

三、信令通道與媒體通道分離

我們使用獨立的一個UDP信令通道用來傳輸NACK請求,而不是復(fù)用媒體通道。這樣做的主要考慮是:

A、媒體通道上使用的FEC\QOS將必然引入部分延時和抖動(具體參見FEC\QOS原理說明),NACK請求對于時間特別敏感,希望是越早越快通知對方越好。在信令通道上我們將只進行裸UDP收發(fā),不會加入FEC和QOS。

B、避免出現(xiàn)NACK請求包丟失后也發(fā)起NACK重傳請求的情況。

C、更好的兼容性,對于不支持NACK的節(jié)點,只需要忽略信令通道的內(nèi)容即可與NACK節(jié)點互通。

D、程序?qū)崿F(xiàn)上更加簡潔,無需在媒體通道上新增包類型來區(qū)分哪個包是NACK請求包。媒體包上增加字節(jié)也都會直接轉(zhuǎn)化為帶寬的增長。

四、實現(xiàn)流程

方案的實現(xiàn)流程如下圖所示:


圖2 NACK的整體流程

對于傳輸層模塊,它是全雙工的,為演示方便我們只列出單向的情況,另外一個方向也是完全一致的。在FEC編碼之后,所有的發(fā)出的UDP(RTP)包均會被存入一個環(huán)形緩存區(qū)中,當收到遠端NACK請求時,將在下一個媒體包傳輸時觸發(fā)重傳動作,后者將在環(huán)形緩存區(qū)中檢索需要重傳的包并在媒體通道上發(fā)出。我們沒有新增內(nèi)部線程去執(zhí)行重傳動作,而是借助原本的媒體包發(fā)送行為來觸發(fā),這樣可以簡化設(shè)計提高穩(wěn)定性。

檢索過程中我們進行了數(shù)據(jù)包的合法性校驗和時間戳有效性校驗,當發(fā)現(xiàn)當前時間距離數(shù)據(jù)包初次發(fā)送時間的間隔已經(jīng)較大時,將放棄重傳,因為此時遠端極有可能已經(jīng)退出等待,沒有必要再浪費帶寬。檢索使用的是RTP頭中的序號字段,需要考慮序號達到最大值時的跳變動作

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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