TCP協議可靠性是如何保證之滑動窗口,超時重發,序列號確認應答信號

原創文章首發於公衆號:「碼農富哥」,歡迎收藏和關注,如轉載請註明出處!

TCP 是一種提供可靠性交付的協議。
也就是說,通過 TCP 連接傳輸的數據,無差錯、不丟失、不重複、並且按序到達。
但是在網絡中相連兩端之間的介質,是複雜的,並不確保數據的可靠性交付,那麼 TCP 是怎麼樣解決問題的?
TCP 是通過下面幾個特性保證數據傳輸的可靠性:

  • 序列號和確認應答信號
  • 超時重發控制
  • 連接管理
  • 滑動窗口控制
  • 流量控制
  • 擁塞控制

通過序列號和確認應答信號提高可靠性

如下圖,在 TCP 中,當發送端的數據到達接收主機時,接收端主機會返回一個已收到消息的通知,這個消息叫做確認應答(ACK)。當發送端將數據發出之後會等待對端的確認應答。如果有確認應答,說明數據已經成功到達對端。反之,則數據丟失的可能性很大。
image

但是,如果在一定時間內發送端都沒有得到確認應答ACK,發送端就會認爲數據丟失,並進行數據重發。所以,即使產生了丟包,TCP仍然能保證數據能夠到達對端,實現可靠的傳輸。
發送端確認應答ACK,主要分兩種情況:

  • 1. 發送端發送的數據丟包
    image
    上圖中的主機A發出數據後因爲網絡擁堵等原因導致了丟包,數據無法達到主機B,此時,主機A如果在一個特定的時間間隔內都沒收到主機B的ACK,則會將數據進行重發
  • 2. 接收端發送的確認應答ACK丟包或延遲
    image
    這個圖中主機A的數據正常發送到主機B,但由於網絡堵塞等原因,主機B的ACK沒有達到主機A。主機A在一定時間間隔內始終沒收到ACK,則會重發這個數據。
    此時,主機B收到數據後就會再次發送ACK,但是由於主機B已經接收過1-1000的數據,所以當再有相同數據達到它時就會放棄這個數據。

此外,也有可能因爲一些其他原因導致ACK延遲到達,在源主機重發數據以後纔到達的情況也屢見不鮮。此時,源主機只要按照機制重發數據即可。

雖然目標主機通過重發數據可以提供可靠的傳輸,但是對於目標主機來說,反覆收到相同的數據可能會是一個”災難“,既浪費網絡資源,還要耗資源對它處理。
所以,我們需要一種機制來識別是否已經接收到了這個數據包、又能夠判斷數據包是否需要接收。

目標主機反覆收到相同數據是不可取的,爲了保持數據的一致性,目標主機必須扔掉重複的數據包,那麼怎麼判斷該數據包是已經重複收取過呢? 爲此我們引入了序列號

序列號是按照順序給發送數據的每一個字節(8位字節)都標上號碼的編號。接收端查詢接收數據 TCP 首部中的序列號和數據的長度,將自己下一步應該接收的序列號作爲確認應答返送回去。通過序列號和確認應答號,TCP 能夠識別是否已經接收數據,又能夠判斷是否需要接收,從而實現可靠傳輸。

所以,通過序列號,上面說的**“確認應答ACK處理”, “重發控制”,“重複控制”**都能實現了。

超時重發如何確定呢?

  • 重發超時是指在重發數據之前,等待確認應答到來的那個特定時間間隔。如果超過這個時間仍未收到確認應答,發送端將進行數據重發。
    最理想的是,找到一個最小時間,它能保證“確認應答一定能在這個時間內返回”。

  • TCP 要求不論處在何種網絡環境下都要提供高性能通信,並且無論網絡擁堵情況發生何種變化,都必須保持這一特性。爲此,它在每次發包時都會計算往返時間(RTT Round Trip Time)及其偏差(RTT波動的時間,也叫抖動)。將這個往返時間和偏差時間相加,重發超時的時間就是比這個總和要稍大一點的值。

  • 重發超時既要考慮RTT往返時間,又要考慮網絡抖動的偏差,如下圖所示,網絡網絡環境不同可能會造成RTT大幅度擺動,TCP/IP的目的就是即使在這種環境下也能進行控制,不浪費網絡流量。
    image

  • 在 BSD 的 Unix 以及 Windows 系統中,超時都以0.5秒爲單位進行控制,因此重發超時都是0.5秒的整數倍。不過,最初其重發超時的默認值一般設置爲6秒左右。

  • 數據被重發之後若還是收不到確認應答,則進行再次發送。此時,等待確認應答的時間將會以2倍、4倍的指數函數延長。

  • 此外,數據也不會被無限、反覆地重發。達到一定重發次數之後,如果仍沒有任何確認應答返回,就會判斷爲網絡或對端主機發生了異常,強制關閉連接。並且通知應用通信異常強行終止。

連接管理

TCP是面向連接的通信協議,面向連接是指在數據通信之前先做好通信兩端之間的準備工作。
因此,在數據通信之前,會通過TCP首部發送一個SYN包作爲建立連接和等待確認應答,如果對端發來確認應答ACK,則認爲可以進行通信,否則如果對端沒有發送正確的ACK應答,那麼就不會通信。
另外通信完畢需要發送FIN包來關閉連接

這就是我們常常說的 三次握手建立連接 和四次揮手關閉連接
我之前也寫了一篇 一文徹底搞懂 TCP三次握手、四次揮手過程及原理,大家有興趣可以去看看,瞭解TCP連接時如何建立和關閉的

TCP是以段爲單位進行數據包的發送的

在建立 TCP 連接的同時,也可以確定發送數據包的單位,我們也可以稱其爲**“最大消息長度”(MSS,Max Segment Size)**,也就是一個段。最理想的情況是,最大消息長度正好是 IP 中不會被分片處理的最大數據長度。

TCP 在傳送大量數據時,是以 MSS 的大小將數據進行分割發送。進行重發時也是以 MSS 爲單位。

MSS 在三次握手的時候,在兩端主機之間被計算得出。兩端的主機在發出建立連接的請求時,會在 TCP 首部中寫入 MSS 選項,告訴對方自己的接口能夠適應的 MSS 的大小。然後會在兩者之間選擇一個較小的值投入使用。
image
上圖的是Tcpdump抓包的信息,在三次握手建立連接時,大家都交換了對方的MSS,目的是告訴對方,我能適應每次TCP數據傳輸單位最大是多少,後面通信雙方就會按照這個MSS大小作爲發送單位發送數據,以上圖爲例,TCP每次傳輸最多不會超過65495字節

利用滑動窗口控制提高速度

上面說了,TCP 以1個段爲單位,如果每發送一個段進行一次確認應答,才能進行下一次通信,那這樣的傳輸方式有一個缺點,就是包的往返時間(RTT)越長通信性能就越低。如下圖:
image
這種方式有點類似於數據庫不能併發請求,只能一個挨一個的處理,自然這樣的效率肯定是比並發低的

爲解決這個問題,TCP 引入了窗口這個概念。確認應答不是以每個分段來確認,而是以更大的單位進行確認,轉發時間將會被大幅地縮短。也就是說,發送端主機,在發送了一個段以後不必要一直等待確認應答,而是繼續發送。如下圖所示:
image
如上圖,我們假設窗口大小是4000字節,主機A可以一口氣發送把4000字節的序列號發送完畢。這個跟前面每個段接收ACK後才能繼續發送新一個段的情況相比,即使RTT變長也不會影響網絡的吞吐量。
窗口大小就是指無需等待確認應答ACK而繼續發送數據的最大值
這種窗口機制實現了使用了大量的緩衝區(Buffer,指的是計算機存儲收發數據的的內存空間),通過對多個段同時進行確認應答的功能。

滑動窗口示意圖如下:
image

上面這個圖一個段爲1000字節,滑動窗口是4個段,在①的狀態下,如果收到一個序列號爲2000的ACK,那麼2001 之前的數據就沒必要重發了,這部分的數據可以被過濾掉,滑動窗口成爲③的樣子。
對於滑動窗口有以下幾點特點:

  • 上圖中的窗口內的數據即便沒有收到確認應答也可以被髮送出去。不過,在整個窗口的確認應答沒有到達之前,如果其中部分數據出現丟包,那麼發送端仍然要負責重傳。爲此,發送端主機需要設置緩存保留這些待被重傳的數據,直到收到他們的確認應答
  • 在滑動窗口以外的部分包括未發送的數據以及已經確認對端已收到的數據。當數據發出後若如期收到確認應答就可以不用再進行重發,此時數據就可以從緩存區清除。
  • 收到確認應答的情況下,將窗口滑動到確認應答中的序列號的位置。這樣可以順序地將多個段同時發送提高通信性能。這種機制也別稱爲滑動窗口控制

滑動窗口控制與重發控制

在使用了窗口控制中,如果出現了丟包怎麼辦呢?這裏我們還是分兩種情況分析:

  • 1.確認應答ACK未能正確返回的情況
    在這種情況下,數據是已經被對端主機成功接收了的,是不需要進行重新發送的。
    然而,如果在沒有使用窗口控制的前提下,沒有收到確認應答包的數據包都會被重發。
    但是,在使用了窗口控制以後,就如下圖所示,某些應答包即使丟失了也無需重發,這也提高了傳輸效率。
    image
    如上圖所示,一個段大小爲1000字節,一個窗口大小爲6000個字節的情況,主機A連續發送了6000序列號的數據,中間的主機B對1001的ACK丟失了,但是後面的2001的ACK正常返回了,說明前2000的序列號的數據都正常讀取了,那麼即使1001的ACK丟失也不需要進行數據重發!
    所以在窗口控制的機制下,前面的ACK丟失,也能通過下一個ACK進行確認,提高了不少效率。

  • 2.某個報文丟失的情況
    如果當接收端主機接收到一個自己應該接收的序列號之外的數據包時,它會一直對當前接收到的數據包返回確認應答包。

image
如上圖所示,主機A的1001-2000序列號的報文丟失了,它會一直收到來自主機B的一個1001的ACK,這個ACK就像在跟主機A提醒 “我想接收從1001開始的數據”。當主機A連續收到這個1001的確認應答ACK 3次後,就會認爲數據丟失了,需要重發。

在滑動窗口比較大的情況下,同一個序列號的確認應答將會被重複不斷地返回。而發送端主機如果 連續 3 次 接收到同一個確認應答包,就會將其對應的數據重發,這種機制比之前提到的“超時重發”更加高效,所以被稱之爲“高速重發控制”

總結

TCP協議在實現傳輸可靠性上面做了很多:

  • 通過序列號和確認應答信號確保了數據不會重複發送和重複接收。
  • 同時通過超時重發控制保證即使數據包在傳輸過程中丟失,也能重發保持數據完整。
  • 通過三次握手,四次揮手建立和關閉連接的連接管理保證了端對端的通信可靠性。
  • TCP還使用了滑動窗口控制提高了數據傳輸效率

最後

文章如果對你有收穫,可以收藏轉發,這也是對我寫作的肯定!另外可以關注我公衆號**「碼農富哥」** (搜索id:coder2025),我會持續輸出Python,服務端架構,計算機基礎(MySQL, Linux,TCP/IP)的 原創 文章

掃碼關注我:碼農富哥

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章