[面試∙網路] TCP/IP(五):TCP 協議詳解

bestswifter發表於2017-12-12

上一節 中講過,TCP 協議是面向有連線的協議,它具有丟包重發和流量控制的功能,這是它區別於 UDP 協議最大的特點。本文就主要討論這兩個功能。

資料包重發

資料傳送

丟包重發的前提是傳送方能夠知道接收方是否成功的接收了訊息。所以,在 TCP 協議中,接收端會給傳送端返回一個通知,也叫作確認應答(ACK),這表示接收方已經收到了資料包。

根據上一節對 TCP 首部的分析得知,ACK 的值和下次傳送資料包的序列號相等。因此 ACK 也可以理解為:“傳送方,下次你從這個位置開始傳送!”。下圖表示了資料傳送與確認應答的過程:

ACK 確認

資料包和 ACK 應答都有可能丟失,在這種情況下,傳送方如果在一段時間內沒有收到 ACK,就會重發資料:

未收到 ACK 時重發資料

即使網路連線正常,由於延遲的存在,接收方也有可能收到重複的資料包,因此接收方通過 TCP 首部中的 SYN 判斷這個資料包是否曾經接收過。如果已經接收過,就會丟棄這個包。

重傳超時時間(RTO)

如果傳送方等待一段時間後,還是沒有收到 ACK 確認,就會啟動超時重傳。這個等待的時間被稱為重傳超時時間(RTO,Retransmission TimeOut)。RTO 的值具體是多久呢?

首先,RTO 的值不是固定的,它是一個動態變化的時間。這個時間總是略大於連線往返時間(RTT,Round Trip Time)。這個設定可以這樣理解:“資料傳送給對方,再返回到我這裡,假設需要 10 秒,那我就等待 12秒,如果超過 12 秒,那估計就是回不來了。”

RTT 是動態變化的,因為誰也不知道網路下一時刻是否擁堵。而當前的 RTO 需要根據未來的 RTT 估算得出。RTO 不能估算太大,否則會多等待太多時間;也不能太小,否則會因為網路突然變慢而將不該重傳的資料進行重傳。

RTO 有自己的估算公式,可以保證即使 RTT 波動較大,它的變化也不會太劇烈。感興趣的讀者可以自行查閱相關資料。

TCP 視窗

按照之前的理論,在資料包發出後,直至 ACK 確認返回以前,傳送端都無法傳送資料,而且包的往返時間越長,網路利用效率和通訊效能就越低。前兩張圖片形象的解釋了這一點。

為了解決這個問題,TCP 使用了“視窗”這個概念。視窗具有大小,它表示無需等待確認應答就可以繼續傳送資料包的最大數量。比如視窗大小為 4 時,資料傳送的示意圖如下:

視窗大小為 4

不等確認就連續傳送若干個資料包會不會有問題呢?我們首先來看資料包丟失問題。

我們知道 TCP 首部中的 ACK 欄位表示接收方已經收到資料的最後位置。因此,接收方成功接收到了 1-1000 位元組的資料後,它會傳送一個 ACK = 1001 的確認包。假設 1001-2000 位元組的資料包丟失了,由於視窗長度比較大,傳送方會繼續傳送 2001-3000 位元組的資料包。接收端並不會返回這個資料包的確認,因為它最後收到的資料還是 1-1000 位元組的資料包。

因此,接收端返回的資料包的 ACK 依然是 1001。這表示:“喂,發資料的,別往後發了,你第 1001 位元組開始的資料還沒來呢”。可以想見,傳送端以後每次傳送資料包得到的確認中,ACK 的值都是 1001。當連續收到三次確認之後,傳送方會意識到:“對方還沒有接收到資料,這個包需要重傳”。

因此,引入視窗的概念後,被髮送的資料不能立刻丟棄,需要快取起來以備將來需要重發。

利用視窗傳送資料的過程可以用下圖表示:

快速重傳

如果是資料包沒有丟失,但是確認包丟失了呢?這就是視窗最擅長處理的問題了。假設傳送發收到的確認包中的 ACK 第一次是 1001,第二次是 4001。那麼我們完全可以相信中間的兩個包是成功被接收的。因為如果有沒接收到的包,接收方是不會增加 ACK 的。

在這種情況下,如果不使用視窗,傳送方就需要重傳第二、三個資料包,但是有了視窗的概念後,傳送方就省略了兩次重傳。因此使用視窗實際上可以理解為“空間換時間”。

某些確認包丟失時不用重發

流量控制

視窗大小

如果視窗過大,會導致接收方的快取區資料溢位。這時候本該被接收的資料反而丟棄了,就會導致無意義的重傳。因此,視窗大小是一個可以改變的值,它由接收端主機控制,附加在 TCP 首部的“視窗大小”欄位中。

慢啟動

在連線建立的初期,如果視窗比較大,傳送方可能會突然傳送大量資料,導致網路癱瘓。因此,在通訊一開始時,TCP 會通過慢啟動演算法得出視窗的大小,對傳送資料量進行控制。

流量控制是由傳送方和接收方共同控制的。剛剛我們介紹了接收方會把自己能夠承受的最大視窗長度寫在 TCP 首部中,實際上在傳送方這裡,也存在流量控制,它叫擁塞視窗。TCP 協議中的視窗是指傳送方視窗和接收方視窗的較小值。

慢啟動過程如下:

  1. 通訊開始時,傳送方的擁塞視窗大小為 1。每收到一個 ACK 確認後,擁塞視窗翻倍。
  2. 由於指數級增長非常快,很快地,就會出現確認包超時。
  3. 此時設定一個“慢啟動閾值”,它的值是當前擁塞視窗大小的一半。
  4. 同時將擁塞視窗大小設定為 1,重新進入慢啟動過程。
  5. 由於現在“慢啟動閾值”已經存在,當擁塞視窗大小達到閾值時,不再翻倍,而是線性增加。
  6. 隨著視窗大小不斷增加,可能收到三次重複確認應答,進入“快速重發”階段。
  7. 這時候,TCP 將“慢啟動閾值”設定為當前擁塞視窗大小的一半,再將擁塞視窗大小設定成閾值大小(也有說加 3)。
  8. 擁塞視窗又會線性增加,直至下一次出現三次重複確認應答或超時。

以上過程可以用下圖概括:

視窗大小變化示意圖

強烈建議讀者對照上述八個步驟理解這幅圖!

相關文章