五分鐘技術趣談 | 淺談網絡傳輸中的錯誤恢復機制
在網絡傳輸中,隨機丟包是一種常見且不可避免的現象,常見的隨機丟包原因有:
1??網絡擁塞:當網絡擁塞時,網絡設備(如路由器、交換機等)會出現緩存溢出、隊列滿等情況,導致數據包無法及時處理,從而出現丟包現象。
2??傳輸錯誤:數據包可能會因為傳輸介質的問題或者傳輸過程中的干擾等原因導致數據包損壞,無法通過網絡數據正確性校驗,數據包被丟棄導致丟包現象。
此外,數據包的亂序也是網絡傳輸中的常見現象,當網絡擁塞時,不同數據包在傳輸過程中可能會經過不同路徑,而不同路徑的帶寬和延遲不同導致數據包亂序到達。
為解決隨機丟包與亂序的問題,各個傳輸控制協議分別引入了各自的錯誤恢復機制,比較典型的是:TCP引入了ACK,UDP引入了NACK,下面我們對ACK和NACK進行分析比較。
Part 01
ACK實現原理
ACK是一種正向反饋,接收方收到數據后回復消息告知發送方數據包已收到。ACK要求TCP包頭中包含一個唯一ID(SeqNum),接收端收到數據包后發送“確認當前SeqNum已收到”的數據包給發送端,發送端收到確認包即認為數據發送成功。
常見的ACK實現如下??
1.1 停等協議
發送方A發送數據, 每發送一個數據包就停止發送,開啟定時器并等待接收方B發送確認, 收到確認后A關閉定時器,發送下一個數據包。若超過定時器設置的超時時間,則數據包發送失敗,重新發送數據包并重啟定時器。
圖1 停等協議數據交互示意圖
停等協議一次只能發送一個數據包,保證了準確性但犧牲了效率,對帶寬的利用率也不高。
1.2 快速重傳&滑窗協議
使用 ACK 機制的傳輸協議,通常在發送端等到某個數據包的 ACK 超時后,才會重傳數據包,不夠及時。快速重傳的實現是如果接收端接收到了序號跳躍的數據包,則立即給發送方發送最后一個連續的數據包的 ACK(重復確認) 。如果發送端收到連續 3 個重復確認,則認為該 ACK 的下一個數據包丟失了,并立即重傳該丟失的數據包。
圖2 快速重傳&滑窗協議數據交互示意圖
觸發快速重傳之后,重傳的方案有以下兩種,具體采取哪種方案依賴于具體實現:
a.僅重傳包M1,在較少丟包的時候該方案比較適用,但是如果是連續丟包場景,會不斷的觸發快速重傳,性能反而較差。
b.從M1開始重傳所有包,適用于連續丟包場景,但是較少丟包或亂序時,M1之后已被接收的包也會被重發,浪費網絡資源。
快速重傳協議保留了超時時間機制,超時后數據包重發,引入快速重傳機制可以更快的發現數據包丟失,在未到達超時時間時便可提前重發數據并重啟定時器。
1.3 連續ARQ協議&滑窗協議
發送方維持著一個一定大小的發送窗口,位于發送窗口內的所有包可以連續發送出去,中途不需要依次等待對方的ACK確認。
接收方通常采用積累確認模式,即不必對每一個包逐個發送ACK,而是在連續收到N個包后,對順序到達的最后一個包序號發送ACK,表示這個包及之前的所有包都已正確收到了,其中N會根據網絡狀況和協議設計而有所不同。
圖3 連續ARQ協議&滑窗協議數據交互示意圖
連續ARQ協議保留了超時時間機制,超時后數據包重發。
連續ARQ協議中,在收到確認包(M4)后,之前的所有包(M1、M2、M3)也被確認。
與快速重傳協議相比,連續ARQ協議減少了確認包的數目,節省了帶寬。但連續ARQ協議在確認到丟包(M7)之后,處于丟包(M7)之后已被接收的包(M8)也會被重發,浪費網絡資源,降低網絡響應速度。
1.4 SACK協議&滑窗協議
SACK協議是在連續ARQ協議上的優化,通過在確認包頭中增加已經接收到并緩存的不連續的報文段,避免丟包之后已被接收的包(圖3中M8)也會被重發,從而節省帶寬,加快網絡響應速度。
圖4 SACK協議&滑窗協議數據交互示意圖
需要注意的是,SACK并不是TCP的默認項,需要通信雙方均開啟SACK功能支持。
對于以上四個方案,整體性能評價為SACK協議>ARQ協議≈快速重傳協議>停等協議。特別是針對亂序場景:
(1)SACK可以避免重發接收端已經接受的包;
(2)快速重傳策略a也可避免重發接收端已經接受的包,但是卻引入了連續丟包場景不斷觸發快速重傳的問題;
(3)快速重傳策略b、ARQ協議無法避免重發接收端已經接受的包。
快速重傳和連續ARQ相比,各有其適用的場景,快速重傳適用于數據傳輸延遲要求較高的場景,如實時視頻傳輸;而連續ARQ適用于數據傳輸可靠性要求較高的場景,如文件傳輸。
Part 02
NACK實現原理
NACK是一種負向反饋,接收方只有在沒有收到數據的時候才通知發送方。NACK要求UDP包頭中包含一個唯一ID(SeqNum),接收端收到數據包后,檢查SeqNum是否連續,記錄缺失的SeqNum,等待定時發送NACK請求,要求發送端重發。
圖5 NACK協議數據交互示意圖
定時發送NACK的時間由用戶自定義,一般為20ms,在一個定時發送周期內到達的亂序包不會請求重發,但不在一個定時發送周期內到達的亂序包會冗余重發。
Part 03
ACK與NACK性能對比
由于SACK性能在ACK中最佳,因此我們只比較NACK與SACK。
- SACK的確認包丟失可能會導致數據包發送超時,重發接收端已接收的數據包;NACK反饋包丟失,下一個反饋包會攜帶上一個反饋包的信息。NACK避免了已接收數據包的重發,但因為缺少超時機制,發送端丟包重發完全依賴于NACK反饋包,重傳靈敏度略低于SACK。
- 受限于TCP滑動窗口的大小(100-200個),SACK必須等待滑動窗口中的數據全部發送才能向后繼續發送新的數據包,這會引入部分時延;NACK歷史數據隊列完全由用戶控制,無此限制(一般為1000個或2s內數據)。
- 受限于TCP頭的大小,SACK一個確認包中只能攜帶3組提前收到確認數據,在強丟包場景下性能退化嚴重,很容易導致冗余重發;NACK在強丟包場景下性能略微退化,會導致部分冗余重發,但優于SACK。
綜上,在網絡數據較好時,NACK與SACK各有優劣;在強丟包環境中,NACK性能強于SACK。但個人認為在強丟包環境中,SACK策略與NACK策略仍是各有優劣,但SACK受限于TCP的框架,導致性能不如NACK。
總的來說,基于TCP的SACK適用于效率要求較低、但準確性要求較高的場景,例如文件傳輸、接收郵件、遠程登錄;基于UDP的NACK適用于效率要求較高,但準確性要求不高的場景,例如實時音視頻、快直播、家庭教育、在線視頻觀看等類直播場景。