前言
本節內容有點多,不過關於 TCP 的話,除了三四次握手就是可靠傳輸了,高頻重點知識點,大家還是搞清楚比較好。
1.TCP 可靠傳輸概覽首先解釋一下,什麼是可靠傳輸:可靠傳輸就是保證接收方收到的位元組流和傳送方發出的位元組流是完全一樣的。
網路層是沒有可靠傳輸機制的,儘自己最大的努力進行交付。而傳輸層使用 TCP 實現可靠傳輸,TCP 保證可靠傳輸的機制有如下幾種:
1)校驗和 Checksum(稍作了解即可)2)序列號和確認應答機制(重要)3)重傳機制(重要)4)流量控制(滑動視窗協議)(非常重要)5)擁塞控制(重要)以上除了校驗和大家可以只稍作了解之外,其他都是非常重要的,務必爛熟於心。
2. 校驗和所謂 TCP 的校驗和(Checksum)就是說:由傳送端計算待發送 TCP 報文段的校驗和,然後接收端對接收到的 TCP 報文段驗證其校驗和(TCP 的校驗和是一個端到端的校驗和)。其目的是為了發現 TCP 的首部和資料在傳送端到接收端之間是否發生了變動。如果接收方檢測到校驗和有差錯,則該 TCP 報文段會被直接丟棄。
關於校驗和是如何計算和驗證的,並非高頻重點知識,本文就不詳細解釋了,感興趣的童鞋可自行百度
TCP 在計算校驗和時,需要加上一個 12 位元組的偽首部。
其實 UDP 也有校驗和機制,只不過是可選的,而 TCP 的校驗和是必須的,TCP 和 UDP 在計算校驗和時都需要加上一個 12 位元組的偽首部。
解釋下偽首部的概念,偽首部的資料是從 IP 資料報頭獲取的,共有 12 位元組,包含如下資訊:源 IP 地址、目的 IP 地址、保留位元組 (置 0)、傳輸層協議號 (TCP 是 6)、TCP 報文長度 (首部 + 資料):
偽首部是為了增加 TCP 校驗和的檢錯能力:如根據目的 IP 地址檢查這個 TCP 報文是不是傳給我的、根據傳輸層協議號檢查傳輸層協議是否選對了...... 偽首部只在校驗的時候使用。
3. 序列號和確認應答機制TCP 報文段的首部中有一個序號欄位,在之前的文章 關於 TCP 三次握手和四次揮手,滿分回答在此 已經解釋過:指的是該報文段第一個位元組的序號(一個位元組佔一個序號)
確認應答機制就是接收方收到 TCP 報文段後就會返回一個確認應答訊息:
確認應答機制和重傳機制不分家,兩者緊密相連。下面我們詳細講解一下重傳機制
4. 重傳機制在錯綜複雜的網路,並不一定能如上圖那麼順利地傳輸報文,報文存在丟失的可能性。報文丟失的可能因素有很多種,包括應用故障,路由裝置過載,或暫時的服務宕機。報文級別速度是很高的,通常來說報文的丟失是暫時的,因此 TCP 能夠發現和恢復報文丟失顯得尤為重要。
重傳機制是 TCP 最基本的錯誤恢復功能,常見的重傳機制有如下:
超時重傳快速重傳① 超時重傳
大概一說到重傳大家第一個想到的就是超時重傳吧。超時重傳就是 TCP 傳送方在傳送報文的時候,設定一個定時器,如果在規定的時間內沒有收到接收方發來的 ACK 確認報文,傳送方就會重傳這個已傳送的報文段。
對於傳送方沒有正確接收到接收方發來的 ACK 確認報文的情況,有以下兩種(也就是在這兩種情況下會發生超時重傳):
第一種情況:報文段丟失第二種情況:接收方的 ACK 確認報文丟失超時重傳時間我們一般用 RTO(Retransmission Timeout) 來表示,那麼,這個 RTO 設定為多少最合適呢,也就是說經過多長時間進行重傳最好?
在這之前,我們先講解一下 RTT(Round-Trip Time 往返時延) 的概念:RTT 就是資料從網路一端傳送到另一端所需的時間,也就是報文段的往返時間。
顯然,⭐ 超時重傳時間 RTO 的值應該略大於報文往返 RTT 的值:
我們可以假想一下,如果超時重傳時間 RTO 遠大於或小於 RTT,會發生什麼情況:
RTO 遠大於 RTT:網路的空閒時間增大,降低了網路傳輸效率RTO 小於 RTT:不必要的重傳,導致網路負荷增大如果超時重傳的資料又超時了該怎麼辦呢?TCP 的策略是重傳的超時間隔加倍。
也就是說,每進行一次超時重傳,都會將下一次重傳的超時時間間隔設為先前值的兩倍。
超時觸發重傳存在的問題是,超時週期可能相對較長。有沒有一種機制可以減少超時重傳的等待時間呢?於是 「快速重傳」 機制應運而生
快速重傳
快速重傳(Fast Retransmit)機制不以時間為驅動,而是以資料驅動重傳。
快速重傳機制的原理:每當接收方收到比期望序號大的失序報文段到達時,就向傳送方傳送一個冗餘 ACK,指明下一個期待位元組的序號。
舉個例子:傳送方已經發送 1、2、3、4、5報文段
接收方收到報文段 1,返回 1 的 ACK 確認報文(確認號為報文段 2 的第一個位元組)接收方收到報文段 3,仍然返回 1 的 ACK 確認報文(確認號為報文段 2 的第一個位元組)接收方收到報文段 4,仍然返回 1 的 ACK 確認報文(確認號為報文段 2 的第一個位元組)接收方收到報文段 5,仍然返回 1 的 ACK 確認報文(確認號為報文段 2 的第一個位元組)接收方收到 3 個對於報文段 1 的冗餘 ACK,認為報文段 2 丟失,於是重傳報文段 2最後,接收方收到了報文段 2,此時因為報文段 3、4、5 都收到了,所以返回 6 的 ACK 確認報文(確認號為報文段 6 的第一個位元組)一圖勝千言:
4. 滑動視窗協議可以說不知道滑動視窗協議 = 不知道 TCP。該知識點的分量之重,大家一定好好把握。
① 累積確認
上文講快速重傳的時候,不知道大家有沒有注意到這句話 “ 最後,接收方收到了報文段 2,此時因為報文段 3、4、5 都收到了,所以返回 6 的 ACK 確認報文 ”。
為什麼這裡會直接返回報文段 6 的確認應答呢,之前我們不是說每傳送一個 TCP 報文段,就進行一次確認應答嗎(只有收到了上一個報文段的確認應答後才能傳送下一個報文段的)?按照這個模式,我們應該先返回報文段 3 的確認應答啊。
其實只有收到了上一個報文段的確認應答後才能傳送下一個報文段的這種模式效率非常低下。每個報文段的往返時間越長,網路的吞吐量就越低,通訊的效率就越低。
舉個例子:如果你說完一句話,我在處理其他事情,沒有及時回覆你,你就等著我做完其他事情後回覆你,你才能說下一句話,很顯然這不現實。
為此,TCP 引入了 視窗 的概念。視窗大小就是指無需等待確認應答,可以繼續傳送資料的最大值。
⭐ 視窗的實現實際上是作業系統開闢的一個緩衝區,傳送方在等待確認應答報文返回之前,必須在緩衝區中保留已傳送的資料。如果在規定時間間隔內收到確認應答報文,就可以將資料從緩衝區中清除。
假設視窗大小為 3 個 TCP 段,那麼傳送方就可以「連續傳送」 3 個 TCP 段,並且中途即使有 ACK響應報文丟失,也可以透過「下一個確認應答進行確認」。
如下圖:ACK 300 即使丟失了,也不會進行資料重發,可以透過下一個確認應答進行確認。只要傳送方收到了 ACK 400 的確認應答,就意味著 400 之前的所有資料「接收方」都收到了。這個模式就叫累積確認或者累積應答。
② 傳送方的滑動視窗
我們先來看看傳送方的視窗,下圖就是傳送方快取的資料,根據處理的情況分成四個部分:
已傳送並收到 ACK 確認應答的資料已傳送但未收到 ACK 確認應答的資料未傳送但總大小在接收方處理範圍內的資料未傳送但總大小超過接收方處理範圍的資料當傳送方把資料全部發送出去後,可用視窗的大小就為 0 了,表明可用視窗耗盡,在沒收到 ACK 確認之前無法繼續傳送資料:
當收到之前傳送的資料 32~36 位元組的 ACK 確認應答後,如果傳送視窗的大小沒有變化,則滑動視窗往右邊移動 5 個位元組,因為有 5 個位元組的資料被確認應答,接下來 52~56 位元組又變成了可用視窗,那麼後續也就可以傳送 52~56 這 5 個位元組的資料了:
接收方的滑動視窗可分為三個部分:
已成功接收並確認的資料未收到資料但可以接收的資料未收到資料且不可以接收的資料(超出接收方視窗大小)同樣的,接收方的滑動視窗在成功接收並確認的資料後,視窗右移。
5. 流量控制想象一下這個場景:主機 A 一直向主機 B 傳送資料,不考慮主機 B 的接收能力,則可能導致主機 B 的接收緩衝區滿了而無法再接收資料,從而導致大量的資料丟包,引發重傳機制。而在重傳的過程中,若主機 B 的接收緩衝區情況仍未好轉,則會將大量的時間浪費在重傳資料上,降低傳送資料的效率。
所以引入了流量控制機制,主機 B 透過告訴主機 A 自己接收緩衝區的大小,來使主機 A 控制傳送的資料量。總結來說:所謂流量控制就是控制傳送方傳送速率,保證接收方來得及接收。
TCP 實現流量控制主要就是透過 滑動視窗協議。
上文我們提到了滑動視窗大小,但是沒說視窗大小在哪裡設定,其實這個和 TCP 報文首部中的 視窗大小 Window 欄位有關。回顧一下上篇文章 關於 TCP 三次握手和四次揮手,滿分回答在此 中講過的 TCP 報文的首部格式,其中就有一個 16 位的 視窗大小 Window 欄位:
該欄位的含義是指自己接收緩衝區的剩餘大小,於是傳送端就可以根據這個接收端的處理能力來發送資料,而不會導致接收端處理不過來。
所以,通常來說視窗大小是由接收方來決定的。
這段話大家一定要理解哦:接收端會在傳送 ACK 確認應答報文時,將自己的即時視窗大小(接收視窗 rwnd)填入,並跟隨 ACK 報文一起傳送出去。而傳送方根據接收到的 ACK 報文中的視窗大小的值改變自己的傳送速度。如果接收到視窗大小的值為 0,那麼傳送方將停止傳送資料。並定期地向接收端傳送視窗探測資料段,提醒接收端把視窗大小告訴傳送端。
一圖勝前言:
所謂擁塞就是說:在某段時間,對網路中某一資源的需求超過了該資源所能提供的可用部分(即 需大於供),網路的效能變差。
如果網路出現擁塞,TCP 報文可能會大量丟失,此時就會大量觸發重傳機制,從而導致網路擁塞程度更高,嚴重影響傳輸。
其實只要「傳送方」沒有在規定時間內接收到 ACK 應答報文,也就是觸發了重傳機制,就會認為網路出現了擁塞。
因此當出現擁塞時,應當控制傳送方的速率。這一點和流量控制很像,但是出發點不同。
流量控制是為了讓接收方能來得及接收,而擁塞控制是為了降低整個網路的擁塞程度,防止過多的資料注入到網路中。
為了調節傳送方所要傳送資料的量,定義了「擁塞視窗 cwnd」的概念。擁塞視窗是傳送方維護的一個狀態變數,它會根據網路的擁塞程度動態變化:
只要網路中出現了擁塞,cwnd 就會減少若網路中沒有出現擁塞,cwnd 就會增大在引入擁塞視窗概念之前,傳送視窗大小和接收視窗大小基本是相等的關係(取決於接收視窗大小)。引入擁塞視窗後,傳送視窗的大小就等於擁塞視窗和接收視窗的最小值。
TCP 的擁塞控制採用了四種演算法:
慢開始擁塞避免快重傳快恢復下面詳細講解這四種演算法
① 慢開始
慢開始的思路就是:TCP 在剛建立連線完成後,如果立即把大量資料位元組注入到網路,那麼很有可能引起網路阻塞。好的方法是先探測一下,一點一點地提高發送資料包的數量,即由小到大逐漸增大擁塞視窗數值。cwnd 初始值為 1,每經過一個傳播輪次,cwnd 加倍(指數增長)。
當然不能一直執行慢啟動,這裡會設定一個慢啟動輪限 ssthresh 狀態變數:
當 cwnd < ssthresh 時,繼續使用慢啟動演算法當 cwnd >= ssthresh 時,開始使用「擁塞避免演算法」② 擁塞避免
擁塞避免演算法的思路是讓擁塞視窗 cwnd 緩慢增大,即每經過一個往返時間 cwnd 加 1。
注意,無論是慢開始階段還是擁塞避免,只要出現了網路擁塞(觸發超時重傳機制),慢開始輪限 sshresh 和 擁塞視窗大小 cwnd 的值會發生變化(乘法減小):
ssthresh 設為 cwnd/2cwnd 重置為 1由於擁塞視窗大小重置為 1 了,所以就會重新開始執行慢啟動演算法。
快速重傳和快速恢復演算法一般同時使用。
當觸發快速重傳機制,即接收方收到三個重複的 ACK 確認的時候,就會執行快重傳演算法(觸發快速重傳機制和超時重傳機制的情況不同,TCP 認為觸發快速重傳的情況並不嚴重,因為大部分沒丟,只丟了一小部分),快速重傳做的事情有:
cwnd = cwnd/2ssthresh = cwnd重新進入擁塞避免階段後來的 “快速恢復” 演算法是在上述的“快速重傳”演算法後新增的,當收到 3 個重複ACK時,TCP 最後進入的不是擁塞避免階段,而是快速恢復階段。
快速恢復的思想是“資料包守恆”原則,即同一個時刻在網路中的資料包數量是恆定的,只有當“老”資料包離開了網路後,才能向網路中傳送一 個“新”的資料包,如果傳送方收到一個重複的 ACK,那麼根據 TCP 的 ACK 機制就表明有一個數據包離開了網路,於是 cwnd 加 1。如果能夠嚴格按照該原則那麼網路中很少會發生擁塞,事實上擁塞控制的目的也就在修正違反該原則的地方。
具體來說快速恢復的主要步驟是:
把 cwnd 設定為 ssthresh 的值加 3,然後重傳丟失的報文段,加 3 的原因是因為收到 3 個重複的 ACK,表明有 3 個“老”的資料包離開了網路。再收到重複的 ACK 時,擁塞視窗 cwnd 增加 1當收到新的資料包的 ACK 時,把 cwnd 設定為第一步中的 ssthresh 的值。原因是因為該 ACK 確認了新的資料,說明從重複 ACK 時的資料都已收到,該恢復過程已經結束,可以回到恢復之前的狀態了,也即再次進入擁塞避免狀態。