作者 | 磊哥

來源 | Java面試真題解析(ID:aimianshi666)


(資料圖片僅供參考)

轉(zhuǎn)載請聯(lián)系授權(quán)(微信ID:GG_Stone)

相比于 UDP 來說,TCP 的主要特性是三個(gè):有連接、可靠、面向數(shù)據(jù)流。所謂的“有連接”指的是 TCP 中的連接管理機(jī)制,也就是著名的三次握手和四次揮手,就像打電話一樣,想要正常的交流,必須先和對方建立起連接,這就是所謂的“有連接”,而面向數(shù)據(jù)流的機(jī)制咱們以后再講,我們今天要討論的主題是:TCP 是如何保證可靠性的?TCP 之所以能保證可靠性,主要是通過以下 6 個(gè)手段:

校驗(yàn)和確認(rèn)應(yīng)答超時(shí)重傳流量控制擁塞控制丟棄重復(fù)數(shù)據(jù)

接下來,我們詳細(xì)來看這幾種手段的具體實(shí)現(xiàn)。

1、校驗(yàn)和

TCP 協(xié)議的數(shù)據(jù)格式如下圖所示:

(圖片來源:許許如生xxrs) 從上圖可以看出“校驗(yàn)和”是保存在 TCP 首部中的一個(gè)數(shù)據(jù),TCP 的發(fā)送端和接收端會采用相同的算法,根據(jù)發(fā)送的數(shù)據(jù)計(jì)算出一個(gè) 16 位的校驗(yàn)和,并且校驗(yàn)和會連同數(shù)據(jù)一起發(fā)送給接收端。接收端在得到數(shù)據(jù)之后,會根據(jù)接收的數(shù)據(jù)生成一個(gè)新的校驗(yàn)和,然后用新的校驗(yàn)和與傳遞過來的校驗(yàn)和做對比,如果校驗(yàn)和相同,那么說明數(shù)據(jù)在傳遞過程中沒有發(fā)生任何改變,是一個(gè)有效的數(shù)據(jù),反之則為無效數(shù)據(jù),舍棄即可。

校驗(yàn)和基本算法

TCP/UDP/IP 等協(xié)議的校驗(yàn)和算法都是相同的,采用的都是將數(shù)據(jù)流視為 16 位整數(shù)流進(jìn)行重復(fù)疊加計(jì)算。為了計(jì)算檢驗(yàn)和,首先把檢驗(yàn)和字段置為 0,然后,對有效數(shù)據(jù)范圍內(nèi)中每個(gè) 16 位進(jìn)行二進(jìn)制反碼求和,結(jié)果存在檢驗(yàn)和字段中,如果數(shù)據(jù)長度為奇數(shù)則補(bǔ)一字節(jié) 0。當(dāng)收到數(shù)據(jù)后,同樣對有效數(shù)據(jù)范圍中每個(gè) 16 位數(shù)進(jìn)行二進(jìn)制反碼的求和。由于接收方在計(jì)算過程中包含了發(fā)送方存在首部中的檢驗(yàn)和,因此,如果首部在傳輸過程中沒有發(fā)生任何差錯(cuò),那么接收方計(jì)算的結(jié)果應(yīng)該為全 0 或全 1(具體看實(shí)現(xiàn)了,本質(zhì)一樣) 。如果結(jié)果不是全 0 或全 1,那么表示數(shù)據(jù)錯(cuò)誤。

2、確認(rèn)應(yīng)答

確認(rèn)應(yīng)答機(jī)制是保證消息傳遞可靠性的關(guān)鍵手段,也是幾乎所有消息中間件(MQ)中,最常用的技術(shù)之一,比如主流的消息中間件 RabbitMQ、Kafka、RocketMQ 中都有確認(rèn)應(yīng)答機(jī)制,也就是我們常說的 ACK(ACKnowledge Character,確認(rèn)字符)。確認(rèn)應(yīng)答機(jī)制是 TCP 中,保證消息可靠性的核心機(jī)制。怎么才能確認(rèn)你發(fā)的消息對方一定收到了呢?最有效的手段無疑是對方告訴你,它已經(jīng)收到了,這就是確認(rèn)應(yīng)答。確認(rèn)應(yīng)答的流程如下圖所示:

3、超時(shí)重傳

消息在確認(rèn)應(yīng)答的過程中可能會出現(xiàn)兩個(gè)問題:第一,消息在發(fā)送的時(shí)候丟失了,第二,消息在確認(rèn)應(yīng)答時(shí)丟失了,如下圖所示:

顯然,即使有了確認(rèn)應(yīng)答機(jī)制也保證不了消息不丟失,那怎么辦呢?消息丟了沒關(guān)系,發(fā)送端在確認(rèn)了消息丟失之后,再補(bǔ)償一個(gè)同樣的消息給接收端不就解決了?這就是超時(shí)重傳機(jī)制。

巧妙的超時(shí)重傳機(jī)制

TCP 的超時(shí)重傳機(jī)制在設(shè)計(jì)上也非常巧妙,它為了保證消息在任何環(huán)境中,都能高效的通訊,所以 TCP 采用的是“動態(tài)時(shí)間”的超時(shí)重傳機(jī)制。比如第一次如果消息丟了,那么發(fā)送端會在 500ms 之后再發(fā)送一個(gè)消息,如果發(fā)送的第二個(gè)消息也丟了,那么發(fā)送端會在 1000ms 之后再發(fā)送一個(gè)消息,如果第三個(gè)消息也丟了,那么它會在 2000ms 之后再發(fā)送一個(gè)消息,如果累計(jì)了一定的次數(shù),消息還沒有成功的發(fā)送,那么 TCP 會認(rèn)為對方主機(jī)存在異常,會強(qiáng)制關(guān)閉連接,這就是 TCP 超時(shí)重傳的主要執(zhí)行流程。

4、流量控制

接收端處理數(shù)據(jù)的速度是有限的,如果發(fā)送端發(fā)的太快,那么就會導(dǎo)致接收端的緩沖區(qū)被打滿,這個(gè)時(shí)候如果發(fā)送端繼續(xù)發(fā)送,就會造成丟包,繼而引起丟包重傳等等一系列連鎖反應(yīng)。因此 TCP 會根據(jù)接收端的處理情況,動態(tài)調(diào)整發(fā)送數(shù)據(jù)的大小,這個(gè)機(jī)制就叫流量控制(Flow Control)。

5、擁塞控制

擁塞控制指的是 TCP 會根據(jù)當(dāng)前網(wǎng)絡(luò)的情況,動態(tài)的控制發(fā)送數(shù)據(jù)的多少,以適合的速度來傳遞數(shù)據(jù)。想象一下,如果 TCP 在不清楚網(wǎng)絡(luò)情況的環(huán)境下,貿(mào)然的發(fā)送大量的數(shù)據(jù)給接收端,這樣就會導(dǎo)致更多的丟包及超時(shí)重傳,從而引起一系列的連鎖反應(yīng),導(dǎo)致數(shù)據(jù)傳遞變慢。而TCP 采取的是“慢啟動”機(jī)制,先發(fā)少量的數(shù)據(jù),探探路,摸清當(dāng)前的網(wǎng)絡(luò)擁堵狀態(tài),再決定按照多大的速度傳輸數(shù)據(jù),這就是擁塞控制機(jī)制。如果傳遞的數(shù)據(jù)多了,出現(xiàn)了大量的丟包,那么 TCP 會將發(fā)送的數(shù)據(jù)量調(diào)小,然后再嘗試慢慢的增加發(fā)送的數(shù)據(jù)量,通過這種動態(tài)發(fā)送數(shù)據(jù)包的形式,來實(shí)現(xiàn)適合當(dāng)前網(wǎng)速的數(shù)據(jù)傳遞,這就是 TCP 擁塞控制的具體實(shí)現(xiàn)。

6、丟棄重復(fù)數(shù)據(jù)

通過前面的知識我們知道,在確認(rèn)應(yīng)答時(shí),由于確認(rèn)應(yīng)答消息的丟失,那么接收方可能會收到發(fā)送方的重復(fù)數(shù)據(jù),如下圖所示:

而此時(shí)對于業(yè)務(wù)方來說,只需要一個(gè)數(shù)據(jù)就可以了,所以 TCP 還有一個(gè)機(jī)制,丟棄重復(fù)數(shù)據(jù)的機(jī)制,這樣就能保證業(yè)務(wù)方接收到的數(shù)據(jù)是正確的了。TCP 會給每一個(gè)發(fā)送的包上加上一個(gè)編號,如果接收到了編號相同的數(shù)據(jù)包,那么就說明接收端得到了重復(fù)的包,丟棄即可。

總結(jié)

TCP 保證可靠性的主要手段有 6 個(gè):校驗(yàn)和、確認(rèn)應(yīng)答、超時(shí)重傳、流量控制、擁塞控制、丟棄重復(fù)數(shù)據(jù)。其中流量控制和擁塞控制很容易搞混,我們要清楚的知道,流量控制是針對接收端接收能力的控制機(jī)制,而擁塞控制是針對當(dāng)前網(wǎng)絡(luò)的控制機(jī)制,所以千萬不要搞混了。

標(biāo)簽: