為什麼收到三個重複的ACK意味著發生擁塞?
三次重複的ACK,可能是丟包引起的,丟包可能是網路擁塞造成的,也可能是信號失真造成的。
三次重複的ACK,也有可能是亂序引起的,而亂序和網路擁塞沒有直接關係。
如果就寫這兩行,感覺什麼都沒寫,接下來的文字詳細解釋這兩行文字。
TCP背景知識
客戶端有1M的文件需要上傳到伺服器上,問題來了,這個大文件能否用一個TCP報文傳輸?
肯定不能啊,因為網路路徑有最大傳輸單元(MTU = 1500)的限制,所以IP報文不能大於1500位元組,那怎麼辦啊?
那就讓TCP將1M砍成N個1460位元組的segment,這裡N=685,問題又來了,這685個TCP報文如果發送方不編號,到達接收方之後,接收方依靠什麼方法知道誰是第1個segment,誰是第2個segment。。。誰又是第685個segment?
為何要編號呢?不編號難道不可以嗎?
比如發送方只要將685個segment按序發送, 1、2、3、4、5。。。681、682、683、684、685,這些segment排著隊伍,到達終點順序也應該是1、2、3。。。683、684、685,然後再將685個segment按序拼接在一起,就會復原成1M 位元組的文件。理想很豐滿,現實很骨感,現實會把這個理想擊打成粉粹。現在作者提出三個問題,讀者分析一下如何解決:
(1) 亂序假如第4、5 segment在傳輸過程中發生了亂序,即5比4先到達,接收方拼接的文件將為「12354 。。。」,拼接文件就不是發送方的文件了,而是一個完全沒有意義的廢文件了,拼了老命吭哧吭哧,結果到對方卻是廢文件,這是無法接受的。(2) 丟包
假如第4個segment在傳輸過程中不幸丟了,接收方無法完整地拼接出原始文件。(3) 重複包
假如第4個segment在傳輸過程被網路設備多發送了一次,接收方拼接出的文件將是「123445。。」如果TCP真是這麼脆弱不堪,壓根不會壟斷可靠傳輸層協議,也不會有今天的地位。那麼TCP是如何解決以上三個問題的?
很簡單,就兩個字:編號!
編號可以解決以上三個常見問題,詳細解釋見下文。
亂序
接收方發現5先到,而4沒有到,TCP先將5用倉庫緩存下來,耐心等待4的到來,4到達之後,再按照順序將4、5重寫排列好,這很好理解吧?丟包
接收方對於接收到的segment,會發送一個確認ACK,告訴發送方已經成功接收的編號數字,對於遲遲沒有確認的segment 4,發送方的鬧鐘(timer)一響(timeout),會自動重傳segment 4,最終接收方也可以收到segment 4。同學們肯定會說,由於segment 5、6、7沒有丟,可能早就到達了接收方,在segment 4重傳到達之前,TCP如何處理它們?
也是用倉庫臨時堆放一下,等4到來之後,就可以按照4567。。。的順序讓應用程序取走數據了,這個和快遞公司的工作模式很相似。
重複包
既然segment 編號了,接收方發現segment 4已經收到過,再次收到segment 4已經沒有任何意義了,直接丟棄就可以了。TCP確認機制
發送方發送segment 1,接收方收到之後需要ACK確認,這裡需要敲黑板提醒還在睡覺同學的注意,ACK確認號是多少?ACK = 2 ,什麼意思呢? 意思是說,segment 1已經成功接收,寡人等待segment 2的到來。每次寫到這裡,都會想起中學語文課本里那句經典名言:讓暴風雨來得更猛烈些吧! 亞當夏娃仍玉米棒子的故事正是受這句名言的啟發。
如果發送方發送的segment是「1、2、3、4、5、6」,那麼接收方應該如何ACK確認呢?
同學們會說,那不是很簡單嗎? 收到6個segment,那就發6次ACK,應該是「2、3、4、5、6、7」,當接收方接到ACK =7,就意味著編號7前面所有segment都成功接收,在這裡就是「1、2、3、4、5、6」都成功接收了。問題是ACK的次數有點多,接收方發送一次ACK耗費CPU,接收方接到一次ACK也耗費CPU,另外ACK報文對網路也是一個小小的負擔。
於是RFC建議優化ACK方法,每收到2個segment,發送一次ACK,這樣就會將ACK數目減半,Good Idea!
那麼接收方可以這樣ACK :「3、5、7。。。」,當發送方接收到ACK=7,表明segment 1、2、3、4、5、6已經到了。
問題來了,如果發送方只有一個segment發送,按照以上規則,接收方接收2個segment才發送一個ACK,既然只有一個segment,那接收方的ACK會不會永遠也發不出?
會的,機器是一根筋認死理,這裡會造成通信的死鎖(deadlock)。但人是活的,只要幫助TCP啟動一個定時器就好,只要鬧鐘響了(timeout)而第二個segment還沒有到,這時就顧不了那麼多,直接發ACK就好。
TCP 慢啟動(slow start)就是先發segment 1,直到接到對方ACK了,才會發segment 2、3。有了這個定時器,就可以避免TCP慢啟動期間的通信死鎖。信息不對稱
假如發送方發送segment 「1234567」,接收方接到的是「1235」,接收方接到5的那一瞬間,知道4有可能丟了,也有可能亂序了,至於是哪種情況,接收方無從知曉。但發送方對於segment 4的狀態是完全空白的,既然接收方知道多一點,為什麼不把這個信息同步給發送方呢?通過什麼方法把消息同步給發送方呢?
ACK =4有同學會迷惑不解,明明收到的是5,應該ACK= 6,為何這裡是4?
ACK=6 是什麼意思?
表示 「12345」都成功接收,問題是4收到了嗎? 沒有啊!所以只能ACK=4。稍後,接收方所有接收到的segment為 「12356」,接收方如何做?
ACK=4再稍後,接收方所有接收到的segment為 「123567」,接收方如何做?
ACK =4發送方一直記錄自己重複收到某個ACK的次數,Duplicated ACK(4) =3,此時發送方意識到segment 4有可能丟了,此時應該立馬主動將segment 4重傳出去,而不要被動等待segment 4的重傳定時器超時再重傳segment 4。
由於這種依賴外界刺激的重傳方法比超時重傳更快、更及時,美其名曰:快速重傳(Fast Re-transmit)。
當重傳的segment 4到達接收方時,終於將不連續的segment流 「123567」 修復(Restore)成連續的segment流 「1234567」,通常稱這種主動修複位元組流的方法為快速修復(Fast Restore)。兩者合在一起統稱Fast Re-transmit / Fast Restore。
上文說的外界刺激,就是發送方連續收到三次duplicated ACK,立馬啟動Fast Re-transmit / Fast Restore機制。
如果上文中的接收方所有接收到的segment 不是「123567」,而是」123564」,此時就是亂序的發生,接收方會重新排序成」123456」 ,接收方發送ACK =7即可,segment是連續的,無需修復。
在這種情況下,發送方只接到了兩次duplicated ACK =4,無需啟動Fast Retransmit / Fast Restore機制,因為接收方已經成功修復。總結一下
沒有Fast Re-transmit / Fast Restore機制,TCP完全可以憑藉超時重傳來完成可靠傳輸,由於是被動地等待,所以傳輸效率低下。
Fast Re-transmit / Fast Restore機制,使得TCP能夠快速地通過三次重複的ACK,推測報文有丟失的可能,進而主動重傳,傳輸效率更高。
完整內容請閱讀公眾號文章:
為什麼收到三個重複的ACK意味著發生擁塞??mp.weixin.qq.com發出ack代表我期待這個seq後面的數據。
所以收到多次ack就代表對端收到的數據包的最新seq值一直沒變
你說的這個是tcp的一種增強特性,叫做Fast Retransmit Recovery (FRR),機制是當接收方發現丟包時,馬上發送重複的ACK給發送方,當發送方收到三個重複的ACK時,就確認丟包了,馬上進行重傳,這樣就不需要等待計時器超時了。
所以其實並不是ack表示發生擁塞,而是發生擁塞時,接收方會主動發送重複的ack.
可以參考:https://searchnetworking.techtarget.com/definition/fast-retransmit-and-recovery首先,收到重複ack不一定代表的是擁塞,可能是網路異常導致重複報文,也可能是報文丟失。擁塞的通常意義是指網路上開始堵車了,這個時候我們要採取降流等措施。而三個重複ack意味著發生了報文丟失。路上擁堵和故障不能完全等同。另外,問題中的為什麼是三個而不是其它數字如2個或一個。我是這樣理解的:首先一個肯定不太合適,如果中間網路設備異常導致該ack多發了次,發送端看到重複立即重發,會導致網路負載加重。然後如果是兩次,需要考慮另外一種情況就是報文序號套圈了,那麼收到的可能不是重複而是正確的ack報文。所以三次比較合適,可以認為對端確實沒有收到,從而發送端可以安全選擇重發。
有誤之處歡迎指出,謝謝。
不一定就是擁塞,有可能是網路波動造成數據包丟包,收到三個重複的ACK,一般不按網路發生擁塞時用的慢開始來應對,用快重傳來對數據包進行重傳。
我昨天才考完計算機網路
今天知乎就推送了這個問題|-|
當我在一片娛樂新聞里刷到這個問題時是有點害怕的,這些軟體對我每天上課學了什麼,和期末考試複習時在百度里搜了些什麼問題完全一清二楚。
推薦閱讀:
※使用tracert命令時,在一個節點後所有的節點都沒有數據,這是為什麼?
※關於 python gevent 架框 作為 TCP伺服器 的 代碼問題 , 每個 socket 的 消息 接收 是否有使用 事件監聽回調的方法呢?
※表示層( presentation layer)和會話層(session layer)為什麼會被棄用?
※如何正確關閉 tcp 連接?
※TCP 應用中,如何避免客戶端發送大數據時導致心跳超時斷開連接?