面試突擊:TCP 可靠嗎?為什么?
作者 | 磊哥
來源 | Java面試真題解析(ID:aimianshi666)
轉載請聯(lián)系授權(微信ID:GG_Stone)
相比于 UDP 來說,TCP 的主要特性是三個:有連接、可靠、面向數(shù)據(jù)流。所謂的“有連接”指的是 TCP 中的連接管理機制,也就是著名的三次握手和四次揮手,就像打電話一樣,想要正常的交流,必須先和對方建立起連接,這就是所謂的“有連接”,而面向數(shù)據(jù)流的機制咱們以后再講,我們今天要討論的主題是:TCP 是如何保證可靠性的?TCP 之所以能保證可靠性,主要是通過以下 6 個手段:
- 校驗和
- 確認應答
- 超時重傳
- 流量控制
- 擁塞控制
- 丟棄重復數(shù)據(jù)
接下來,我們詳細來看這幾種手段的具體實現(xiàn)。
1、校驗和
TCP 協(xié)議的數(shù)據(jù)格式如下圖所示:
(圖片來源:許許如生xxrs) 從上圖可以看出“校驗和”是保存在 TCP 首部中的一個數(shù)據(jù),TCP 的發(fā)送端和接收端會采用相同的算法,根據(jù)發(fā)送的數(shù)據(jù)計算出一個 16 位的校驗和,并且校驗和會連同數(shù)據(jù)一起發(fā)送給接收端。接收端在得到數(shù)據(jù)之后,會根據(jù)接收的數(shù)據(jù)生成一個新的校驗和,然后用新的校驗和與傳遞過來的校驗和做對比,如果校驗和相同,那么說明數(shù)據(jù)在傳遞過程中沒有發(fā)生任何改變,是一個有效的數(shù)據(jù),反之則為無效數(shù)據(jù),舍棄即可。
校驗和基本算法
TCP/UDP/IP 等協(xié)議的校驗和算法都是相同的,采用的都是將數(shù)據(jù)流視為 16 位整數(shù)流進行重復疊加計算。為了計算檢驗和,首先把檢驗和字段置為 0,然后,對有效數(shù)據(jù)范圍內中每個 16 位進行二進制反碼求和,結果存在檢驗和字段中,如果數(shù)據(jù)長度為奇數(shù)則補一字節(jié) 0。當收到數(shù)據(jù)后,同樣對有效數(shù)據(jù)范圍中每個 16 位數(shù)進行二進制反碼的求和。由于接收方在計算過程中包含了發(fā)送方存在首部中的檢驗和,因此,如果首部在傳輸過程中沒有發(fā)生任何差錯,那么接收方計算的結果應該為全 0 或全 1(具體看實現(xiàn)了,本質一樣) 。如果結果不是全 0 或全 1,那么表示數(shù)據(jù)錯誤。
2、確認應答
確認應答機制是保證消息傳遞可靠性的關鍵手段,也是幾乎所有消息中間件(MQ)中,最常用的技術之一,比如主流的消息中間件 RabbitMQ、Kafka、RocketMQ 中都有確認應答機制,也就是我們常說的 ACK(ACKnowledge Character,確認字符)。確認應答機制是 TCP 中,保證消息可靠性的核心機制。怎么才能確認你發(fā)的消息對方一定收到了呢?最有效的手段無疑是對方告訴你,它已經(jīng)收到了,這就是確認應答。確認應答的流程如下圖所示:
3、超時重傳
消息在確認應答的過程中可能會出現(xiàn)兩個問題:第一,消息在發(fā)送的時候丟失了,第二,消息在確認應答時丟失了,如下圖所示:
顯然,即使有了確認應答機制也保證不了消息不丟失,那怎么辦呢?消息丟了沒關系,發(fā)送端在確認了消息丟失之后,再補償一個同樣的消息給接收端不就解決了?這就是超時重傳機制。
巧妙的超時重傳機制
TCP 的超時重傳機制在設計上也非常巧妙,它為了保證消息在任何環(huán)境中,都能高效的通訊,所以 TCP 采用的是“動態(tài)時間”的超時重傳機制。比如第一次如果消息丟了,那么發(fā)送端會在 500ms 之后再發(fā)送一個消息,如果發(fā)送的第二個消息也丟了,那么發(fā)送端會在 1000ms 之后再發(fā)送一個消息,如果第三個消息也丟了,那么它會在 2000ms 之后再發(fā)送一個消息,如果累計了一定的次數(shù),消息還沒有成功的發(fā)送,那么 TCP 會認為對方主機存在異常,會強制關閉連接,這就是 TCP 超時重傳的主要執(zhí)行流程。
4、流量控制
接收端處理數(shù)據(jù)的速度是有限的,如果發(fā)送端發(fā)的太快,那么就會導致接收端的緩沖區(qū)被打滿,這個時候如果發(fā)送端繼續(xù)發(fā)送,就會造成丟包,繼而引起丟包重傳等等一系列連鎖反應。因此 TCP 會根據(jù)接收端的處理情況,動態(tài)調整發(fā)送數(shù)據(jù)的大小,這個機制就叫流量控制(Flow Control)。
5、擁塞控制
擁塞控制指的是 TCP 會根據(jù)當前網(wǎng)絡的情況,動態(tài)的控制發(fā)送數(shù)據(jù)的多少,以適合的速度來傳遞數(shù)據(jù)。想象一下,如果 TCP 在不清楚網(wǎng)絡情況的環(huán)境下,貿然的發(fā)送大量的數(shù)據(jù)給接收端,這樣就會導致更多的丟包及超時重傳,從而引起一系列的連鎖反應,導致數(shù)據(jù)傳遞變慢。而 TCP 采取的是“慢啟動”機制,先發(fā)少量的數(shù)據(jù),探探路,摸清當前的網(wǎng)絡擁堵狀態(tài),再決定按照多大的速度傳輸數(shù)據(jù),這就是擁塞控制機制。如果傳遞的數(shù)據(jù)多了,出現(xiàn)了大量的丟包,那么 TCP 會將發(fā)送的數(shù)據(jù)量調小,然后再嘗試慢慢的增加發(fā)送的數(shù)據(jù)量,通過這種動態(tài)發(fā)送數(shù)據(jù)包的形式,來實現(xiàn)適合當前網(wǎng)速的數(shù)據(jù)傳遞,這就是 TCP 擁塞控制的具體實現(xiàn)。
6、丟棄重復數(shù)據(jù)
通過前面的知識我們知道,在確認應答時,由于確認應答消息的丟失,那么接收方可能會收到發(fā)送方的重復數(shù)據(jù),如下圖所示:
而此時對于業(yè)務方來說,只需要一個數(shù)據(jù)就可以了,所以 TCP 還有一個機制,丟棄重復數(shù)據(jù)的機制,這樣就能保證業(yè)務方接收到的數(shù)據(jù)是正確的了。TCP 會給每一個發(fā)送的包上加上一個編號,如果接收到了編號相同的數(shù)據(jù)包,那么就說明接收端得到了重復的包,丟棄即可。
總結
TCP 保證可靠性的主要手段有 6 個:校驗和、確認應答、超時重傳、流量控制、擁塞控制、丟棄重復數(shù)據(jù)。其中流量控制和擁塞控制很容易搞混,我們要清楚的知道,流量控制是針對接收端接收能力的控制機制,而擁塞控制是針對當前網(wǎng)絡的控制機制,所以千萬不要搞混了。