文章目錄
- 1. 慢啟動
- 2. 擁塞避免
- 3. 快速重傳和快速恢復
初識tcp報文
我們先來簡單認識一下報文的格式,具體理解需要后面詳細介紹
- 源端口和目的端口:顧名思義就是標識傳輸雙方的信息
- 首部長度:指的是TCP報頭的長度,換句話來說,我們需要用一個屬性來描述報頭的長度,就說明TCP的報頭是變長的
- 選項:實際上這里翻譯為"可選項"更加準確,這是TCP為了適應復雜的網絡環境和更好的服務應用層而進行設計的,TCP選項部分最長可以達到40byte ,再加上TCP選項外的固定的20byte字節部分, TCP的最長頭部可達60byte
- 序列號:在一個TCP連接中傳輸的字節流中的每一個字節都按順序編號,該字段表示本報文所發送的數據的第一個字節的序號
- 確認號:表示期待收到對方下一個報文段的第一個數據字節的字段,例如當確認號為N,表示序列號N-1之前的所有數據都已全部接收到
- 窗口:通知發送端,接收端可接受的空間大小
- 校驗和:發送端計算發送的TCP報文段的校驗和,接收端對接收到的TCP報文段,驗證其校驗和,如果不一致,說明數據包在發送端和接受端之間傳輸的時候發生了變動,此時該TCP報文會被直接丟棄
- 保留位: TCP報頭里,提前申請好的一塊空間,這個空間暫時用不上,但是說不定以后就能夠用的上(萬一以后TCP要擴展一些新的功能,就可以用這個保留位來表示了)
- 6位標志位:他們中的一位或者多位可以同時設置為1,用于說明該報文性質,其中下面三個字段需要格外注意:
- ACK,僅當ACK為1的時候,確認號字段才有效,TCP規定,當連接建立之后,所有傳輸的報文都必須將ACK設置為1
- SYN,僅在三次握手建立TCP連接時有效,當SYN=1,ACK=0時,表明這是一個連接請求報文,若對方同意連接,則在響應報文中設置SYN=1 和 ACK = 1
- FIN,用來釋放一個連接,當FIN=1時,表示此報文的發送方的數據已經發送完畢,并要求釋放連接
三次握手建立連接
為了確保通信雙方都能**確認對方的存在和可用性**,并且**數據能夠被正確的傳輸和接收**,TCP要求通信雙方建立一條可靠的通信連接三次握手是TCP協議中用于建立可靠連接的過程:
- A向B發送一個SYN報文,請求建立連接
- B收到A的SYN報文后回復一個SYN + ACK的報文
- A收到B的SYN報文之后,回復一個ACK報文
- B接受到ACK報文之后,表示一個TCP連接就建立好了
如果對上面的建立連接過程不懂,可以看下面的例子:
那么此時就很容易發現,為什么一定要三次握手??兩次行不行??
答案是否定的,因為只有經過最后一次交互,B才能知道對方的接受能力和自己的發送能力是沒問題的
分段機制
TCP經過三次握手之后就能發送數據了,但是一般網絡不能連續的傳輸任意長的數據,因此,發送方A將需要傳輸的消息分割成小快,按照順序分別發送,這些小塊就是數據包B接受到數據報之后,經過校驗和校驗無誤之后,開始將收到的數據包進行重新組裝,還原成原始消息
但是可想而知,數據包組裝需要安裝分割前的順序進行拼接,但是我們確不能保證B接受數據包的順序和我們傳輸的順序是一致的,因為在網絡傳輸過程中會經過很多交換機和路由器的傳輸,而不同的設備傳輸速度,負載情況,網絡擁堵等因素都會影響數據包的傳輸順序
為了解決這個問題,TCP在數據包的頭部定義了一個序列號的字段,用于對在一個TCP連接中傳輸的字節流中的每一個字節按順序進行編號,就可以解決亂序的問題了
確認應答+超時重傳機制
為了保證所有的數據包都能夠正確接收,TCP引入了確認應答機制B在接收到數據之后,會回復一個攜帶確認號的確認包,也就是ACK包,A接收到ACK報之后,再發送下一個數據包
如果結果一段等待時間之后,發送包無法知道數據包的情況,則可能出現以下的情況:
解決這個問題的一般方法是超時重傳,A在發送數據包之后,不會一直等待,而是啟動一個重傳定時器,定時到期之后未收到數據包,就會認為數據包丟失了,從而重傳數據包
滑動窗口機制
在上面確認應答機制的情況下,A只有當接收到B回復的ACK包之后,才能繼續發送下一個數據包在這種場景下,發送方大多數時間都是在等待接收方的ACK中,極大的影響了通信的效率
一種解決問題的方法就是滑動窗口,這種機制允許發送方連續發送多個數據包而無需等待
TCP使用一個稱為發送窗口的變量,表示無需等待確認即可發送的數據包的數量
那么發送窗口的大小設置為多少才是合理的呢?
如果太小,那就和我們上面簡單的確認應答機制沒什么區別了
如果太大,接收方可能會來不及處理而丟失數據包,得不償失
TCP使用滑動窗口的機制,要求接收方維護一個可處理的接收窗口,發送方根據接收窗口來調整發送窗口的大小,從而根據接收方的接受能力來動態控制流量
通信雙方每次在發送數據時,都會在TCP報頭中攜帶窗口的大小.表示自己當前的接收能力
前面我們說過為了保證所有數據包都被收到,要求每個數據包接收到之后都要返回一個ACK包
延時應答 +累計確認
在滑動窗口的場景下,如果每個數據包都要回復一個ACK包的話,過多的確認包會消耗網絡資源,降低網絡利用率對于累計確認來說,就是如果接收方返回確認號為N的ACK包,則表明N之前但不包括N的數據包已經全部接收到了
而延遲確認指的是接收方在接收到數據包之后不立即回復,而是延遲等待一段時間,這樣在延遲的時間內收到多個連續的包可以累計成一個ACK確認
通過這兩種方式,減少了ACK包的數量,提高了網絡利用率
快速重傳機制
前面說過,接收方等待一定時間之后,如果未接收到ACK包,就會觸發超時重傳但是還是需要等待一定時間,降低了傳輸效率
TCP引入了快速重傳機制:當接收方連續收到三個相同的確認號的時候,就會觸發快速重傳,立即重傳數據包,而不必等待超時時間
擁塞控制
滑動窗口只考了接收方的處理能力,發送端根據接收窗口的大小發送數據,而忽略了網絡的負載情況隨著網絡連接數量的增加,必然會造成網絡擁堵,就可能引起大量的超時重傳,形成惡性循環,造成整個網絡不可用
因此要進行擁塞控制來避免這種情況的發生
擁塞控制的主要思路是防止過多數據注入網絡,以避免網絡出現負載過大,也就是要根據網絡擁塞情況,對發送數據包的數據進行控制
TCP定義了一個擁塞窗口的變量,如果網絡擁堵就減少擁塞窗口,暢通就增加擁塞窗口
我們前面說過,發送窗口的大小等于接收窗口的大小
現在引入擁塞窗口之后,發送窗口的大小就等于擁塞窗口和接收窗口中的最小值,這樣就能有效控制網絡的發送效率
其中Reno算法是當前應用最廣泛的擁塞控制算法
主要包含慢啟動、擁塞避免、快速重傳、快速回復
1. 慢啟動
剛接入網絡的時候先發送少量數據包,探測網絡情況,然后再一點點逐步提高發送包的數量
2. 擁塞避免
慢啟動的指數級別增長不可能一直持續下去,當發生網絡擁堵的時候,慢啟動增長就會結束
同時TCP定義了一個慢啟動閾值的變量,當擁塞窗口超過這個閾值,就會進入擁塞避免模式,結束指數級增長
假設我們這個閾值是8,當超過8之后,就會以1的數量級增加,以線性增長,這種線性增長的方式相對于指數增長的方式更加穩定,可以避免網絡擁塞的發生
但是也不能一直增長,和慢啟動一樣,當發生網絡擁堵的時候,擁塞窗口的增長就會結束
那么網絡擁堵是怎么感知到的?? 同時擁塞窗口又怎么調整呢?
TCP是通過丟包來感知網絡擁堵狀態的
丟包可以分為兩種
- 超時丟包
也就是經過一段等待時間之后,還沒收到ACK包,這種情況說明網絡擁堵比較嚴重,TCP會進入慢啟動模式,并將慢啟動閾值設置為出現丟包時候的擁塞窗口的一般,重新開始慢啟動,后擁塞避免…
- 第二種是收到三次重復的ACK
此時就會認為之前發送的數據包丟失,這種情況下認為當前網絡發生了輕微擁堵,此時擁塞窗口減小的幅度不會像超時丟包那么大,會執行快速重傳和快速恢復算法
3. 快速重傳和快速恢復
- 將慢啟動閾值設置為當前擁塞窗口的一半,并重傳丟失的數據包
- 將擁塞窗口設置為當前慢啟動閾值加上3,這是因為,發送方收到了3個相同的ACK說明當前已經有三個數據包到達了接收端,網絡的"在途數據量"減少了三個,此時將擁塞窗口加上3,就是適當將擁塞窗口盡可能的變大些
- 再次收到重復的ACK,將擁塞窗口增加1
- 當收到新的數據包的ACK的時候,將擁塞窗口設置為第一步設置的閾值的值,此時快速恢復結束,再次進入擁塞避免階段
實際上第一次學我也是云里霧里的,這里有必要通過一個例子來解釋一下我的理解:
cwnd(擁塞窗口)=9
,此時發送9個數據包(序號1-9)- 假設5數據包丟失,但是報文6~9正常到達接收端
- 接收端每次接受到6~9的數據包,都會回復一個ACK=5,發送端連續收到了3個ACK
- 觸發快速重傳,立即重傳丟失的報文5,同時更新閾值
(ssthresh= cwnd / 2 = 4.5 = 4)
- 進入快速恢復階段,設置
cwnd = ssthresh+3 = 7
,加3是因為,由于已經收到了3個重復的ACK,說明(6,7,8)報文已經成功到達了接收端,可以將窗口向上提3個大小,不會導致下降太多 - 在等待ACK=5的報文階段,允許發送新的報文(10~13),此時又會收到連續的重復的ACK=5,此時每收到一個就將cwnd+1,原因和5一樣
- 當收到新的ACK=6,說明網絡恢復,此時重置窗口cwnd=4,退出快速回復,進入擁塞避免階段,為什么要重新降低?? 我覺得有以下原因
- 快速恢復階段通過臨時增大窗口(
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">cwnd = ssthresh + 3</font>**
)來維持數據傳輸,但此時網絡可能仍處于敏感狀態,若繼續保持這個值,可能會導致擁塞循環 - 重置窗口后,TCP進入擁塞避免階段,確保窗口增長更平滑,避免突然的流量激增。
- 快速恢復階段通過臨時增大窗口(
四次揮手結束通信
TCP是全雙工的,每個方向可以同時傳遞數據,因此每個方向需要單獨的關閉- A方發送
FIN
來終止這個方向上的傳輸,待對方確認(返回ACK)后進入半關閉狀態(也就是說A此時還能接受B發送來的數據 ,因為只是A認為自己結束了,B可能還在發送) - B發現自己數據發送完畢之后,發送給A
FIN
來終止傳輸,待A確定后(返回ACK)終斷連接