所謂的弱網絡環境就是網絡不是很好,比如無線wifi,跨多層網絡路由、或者網路負載過大等等情況。這樣數據在傳輸種會發生丟失的情況。
再說,網絡協議,傳輸層分udp和tcp.本人對tcp做了專門的分析。見下圖。
看圖,我只開了開了2%的丟包,導致發送速度從>500Mb/s下降到 <160Mb/s。速度下降比率達70-80%,發生這個現象的原因是TCP的擁塞處理,當發現網絡丟包的時候,就啟動擁塞控制機制,降低發送速度,達到tcp可靠性傳輸的目的。大于大數據和視頻的傳輸,速度在傳輸成降低這么多,很難滿足應用層的需求,如果是視頻就會導致視頻的卡頓。因為接收端收不到需要的視頻幀,只有停下來花更多的時間等待。實時上筆者在實際開發視頻傳輸的時候使用過tcp,在我無線網絡的情況下,tcp確實特別容易卡頓。關于tcp擁塞控制,大家可以搜索查閱相關的資料,這個不做細說。
筆者用的丟包工具是clumsy0.2。
我們查明了tcp卡頓的原因后,決定放棄tcp,那只有udp了,但是udp又不可靠,會有丟包和亂序的問題,導致視頻播放的花屏,這個花屏是因為關鍵幀或者參考幀丟失了,導致后界的視頻無法正常的恢復,從而出現花屏。于是有查閱了相關的資料,找到了一個udt 協議。
關于udt協議,網上的說明如下:
基于UDP的數據傳輸協議(UDP-based Data Transfer Protocol,簡稱UDT)是一種互聯網數據傳輸協議。UDT的主要目的是支持高速廣域網上的海量數據傳輸,而互聯網上的標準數據傳輸協議TCP在高帶寬長距離網絡上性能很差。顧名思義,UDT建于UDP之上,并引入新的擁塞控制和數據可靠性控制機制。UDT是面向連接的雙向的應用層協議。它同時支持可靠的數據流傳輸和部分可靠的數據報傳輸。 由于UDT完全在UDP上實現,它也可以應用在除了高速數據傳輸之外的其它應用領域,例如點到點技術(P2P),防火墻穿透,多媒體數據傳輸等等。
??? 按照這個及時他是基于udp的協議,又是可靠的,那么是否不會有tcp丟包擁塞的問題。
于是對udt協議做了測試,利用其項目代碼里面的demo測試。
udp是開源的項目,下載地址是:https://sourceforge.net/projects/udt/。目前是udt4.
如下圖是沒有丟包干擾的情況下的發送截圖:
開了丟包干擾又會如何呢,見下圖。
我們看到udt 協議也是一樣的開了丟包干擾后速度下降更多。那為什么呢,不是說udt是基于udp的協議,而udp不會發生擁塞控制么?為此筆者閱讀了udt的代碼,發現他就是在udp的基礎上做加了應答處理,讓udp變得可靠,當然也有“擁塞控制算法”,只不過是應用層的,筆者認為由于這個控制算法是基于應用層的,而且發展的時間,使用的頻率都沒有tcp長,所有在發生擁塞的時候的表現自然要弱于tcp很多。
那么筆者又想到一個問題,那為什么要有udt協議呢?直接使用tcp不是挺好么?筆者又查閱了資料,其實在此文關于udt介紹里面就有答案:由于UDT完全在UDP上實現,它也可以應用在除了高速數據傳輸之外的其它應用領域,例如點到點技術(P2P),防火墻穿透,多媒體數據傳輸等等。udt就是為了做p2p翻墻容易 和 傳輸可靠而已。
至此,關于本文標題的答案探索,似乎走向了死角,所有的探索都失敗了。在筆者不屑的努力下,終于又柳暗花明。
筆者查到了webrtc項目關于抗丟包處理策略,簡稱為QOS-FEC-NACK技術,FEC 中文意思是向前糾錯。NACK 是選擇性重傳,QOS 是處理亂序問題?;谶@樣一整套在應用層的丟錯重傳機制,最大程度了保證了視頻的傳輸的可靠性。下面筆者分別來介紹這個技術。
首先介紹FEC(Forward Error Correction),FEC算法的原理,參見這個博客https://blog.csdn.net/u010178611/article/details/82656838。這里貼出其博客的全文。
筆者經過半年的技術攻關開發出了QOS-NACK-FEC抗丟包傳輸協議,可以加群了解。
閱讀了FEC的糾錯原理后,筆者有了疑問:FEC生成冗余包大小跟原始包是一樣的,那么實際上是增加了傳輸的數據量,發生丟包的時候,為什么還要增加數據量。筆者做了一個計算。加入一個視頻幀有100個包,生成了10個冗余包,傳輸的時候會丟到9個包,那么視頻還會恢復。但是如果沒有這10個冗余包,即使只丟一個包視頻也無法恢復。這就是FEC的好處,在一定數量的丟包的情況下,可以快速的恢復視頻,因為不用請求重傳,所以恢復速度快。只有當丟失過多,比如丟失了11個包,無法恢復的時候,需要請求重傳。
NACK是丟包請求重傳,下面介紹一下其實現原理,和系統框架設計。
NACK可以放置在原來的FEC-QOS傳輸層之外,作為上層應用層,這種實現方式NACK將FEC-QOS看做普通的UDP傳輸,二者并無緊密結合,其優勢是可以與成熟的NACK方案無縫銜接。我們知道任何NACK方案都必將引入延時抖動,因為接收端在發起重傳請求后,需要等待發送端重新發出的數據,在“重傳等待時間”內不對外輸出數據。而QOS階段里為解決UDP亂序包的問題也引入了一個“丟包等待時間”,當遇到包序號不連續時,將等待這一時間,若仍未收到所需的包則認定丟包,不再等待。如果將這兩個時間合二為一,可以盡量的降低系統時延和抖動,畢竟我們需要的是一個高實時性的NACK傳輸方案。我們將NACK的發起和等待放置在QOS之中,入下圖所示:
圖1 在QOS中實現NACK發起
當QOS檢測到序號不連續時,可能是發生丟包或者是亂序,此時QOS將通過FEC解碼模塊分析當前疑似丟包是否將導致FEC無法恢復。此時將產生三種分析結果:
A、當前丟包即使丟了也不影響FEC恢復,比如當前丟失的包為一個或者多個冗余包,且該冗余包所在的group內的媒體包均已接收,或者借助已接收的冗余包足夠恢復。
B、當前丟包不能確定是否影響FEC恢復,需要接收更多的包才能確定。比如丟包發生在group的中段且丟的數量小于冗余包總數。
C、當前丟包將導致FEC確定無法恢復,比如同一個group內丟失的包數大于冗余包總數。
對于情況A,QOS將直接不予等待,將后續接收的包直接交與FEC。對于情況B,QOS將進入“丟包等待時間”,以期收到亂序的包。對于情況C,QOS將發起NACK重傳并進入等待,這個等待時間即是“丟包等待時間”又是“重傳等待時間”,在等待期內不管是該亂序包到達或者重傳包到達,都能滿足FEC的恢復條件。
在介紹了NACK的發起條件后,我們來關注“重傳等待時間”的取值問題。若設置固定的重傳等待時間將很難滿足各類網絡情況。時間過小將導致重傳包尚未到達,QOS已結束等待并輸出后續包,后續即便再收到重傳包也將直接丟棄。重傳包也可能因網絡原因丟包,若“重傳等待時間”過大,將導致更大的延時和抖動。為了提高自適應能力,系統通過實時獲取當前網絡的UDP通訊RTT時間來作為“重傳等待時間”的參考,計算出合理的值。
三、信令通道與媒體通道分離
我們使用獨立的一個UDP信令通道用來傳輸NACK請求,而不是復用媒體通道。這樣做的主要考慮是:
A、媒體通道上使用的FEC\QOS將必然引入部分延時和抖動(具體參見FEC\QOS原理說明),NACK請求對于時間特別敏感,希望是越早越快通知對方越好。在信令通道上我們將只進行裸UDP收發,不會加入FEC和QOS。
B、避免出現NACK請求包丟失后也發起NACK重傳請求的情況。
C、更好的兼容性,對于不支持NACK的節點,只需要忽略信令通道的內容即可與NACK節點互通。
D、程序實現上更加簡潔,無需在媒體通道上新增包類型來區分哪個包是NACK請求包。媒體包上增加字節也都會直接轉化為帶寬的增長。
四、實現流程
方案的實現流程如下圖所示:
圖2 NACK的整體流程
對于傳輸層模塊,它是全雙工的,為演示方便我們只列出單向的情況,另外一個方向也是完全一致的。在FEC編碼之后,所有的發出的UDP(RTP)包均會被存入一個環形緩存區中,當收到遠端NACK請求時,將在下一個媒體包傳輸時觸發重傳動作,后者將在環形緩存區中檢索需要重傳的包并在媒體通道上發出。我們沒有新增內部線程去執行重傳動作,而是借助原本的媒體包發送行為來觸發,這樣可以簡化設計提高穩定性。
檢索過程中我們進行了數據包的合法性校驗和時間戳有效性校驗,當發現當前時間距離數據包初次發送時間的間隔已經較大時,將放棄重傳,因為此時遠端極有可能已經退出等待,沒有必要再浪費帶寬。檢索使用的是RTP頭中的序號字段,需要考慮序號達到最大值時的跳變動作。
有意交流的可以私信留言 微信:Lovexx201709