TCP真的可靠吗
TCP真的可靠嗎
文章目錄
- TCP真的可靠嗎
- 一、TCP的特性
- 1.序列號(hào)、確認(rèn)應(yīng)答、超時(shí)重傳
- 2.窗口控制與高速重發(fā)控制/快速重傳(重復(fù)確認(rèn)應(yīng)答)
- 3.擁塞控制
- 4.慢啟動(dòng):
- 5.擁塞避免
- 7.快速重傳
- 8.鏈接機(jī)制
- 9.進(jìn)行三次握手、四次揮手及timewait的原因
- 二、問(wèn)題引入
- 三、TCP如何保證可靠性?
- 四、TCP并不能保證它所發(fā)送數(shù)據(jù)的可靠傳輸
- 五、故障類(lèi)型
- 1.收不到FIN的故障
- 2.能收到FIN的故障
- 3.總結(jié)
一、TCP的特性
TCP保證可靠性:
1.序列號(hào)、確認(rèn)應(yīng)答、超時(shí)重傳
- 數(shù)據(jù)到達(dá)接收方,接收方需要發(fā)出一個(gè)確認(rèn)應(yīng)答,表示已經(jīng)收到該數(shù)據(jù)段,并且確認(rèn)序號(hào)會(huì)說(shuō)明了它下一次需要接收的數(shù)據(jù)序列號(hào)。
- 如果發(fā)送發(fā)遲遲未收到確認(rèn)應(yīng)答,那么可能是發(fā)送的數(shù)據(jù)丟失,也可能是確認(rèn)應(yīng)答丟失,這時(shí)發(fā)送方在等待一定時(shí)間后會(huì)進(jìn)行重傳。這個(gè)時(shí)間一般是2*RTT(報(bào)文段往返時(shí)間)+一個(gè)偏差值。
2.窗口控制與高速重發(fā)控制/快速重傳(重復(fù)確認(rèn)應(yīng)答)
- TCP會(huì)利用窗口控制來(lái)提高傳輸速度,意思是在一個(gè)窗口大小內(nèi),不用一定要等到應(yīng)答才能發(fā)送下一段數(shù)據(jù),窗口大小就是無(wú)需等待確認(rèn)而可以繼續(xù)發(fā)送數(shù)據(jù)的最大值。如果不使用窗口控制,每一個(gè)沒(méi)收到確認(rèn)應(yīng)答的數(shù)據(jù)都要重發(fā)。
- 使用窗口控制,如果數(shù)據(jù)段1001-2000丟失,后面數(shù)據(jù)每次傳輸,確認(rèn)應(yīng)答都會(huì)不停地發(fā)送序號(hào)為1001的應(yīng)答,表示我要接收1001開(kāi)始的數(shù)據(jù),發(fā)送端如果收到3次相同應(yīng)答,就會(huì)立刻進(jìn)行重發(fā);
- 但還有種情況有可能是數(shù)據(jù)都收到了,但是有的應(yīng)答丟失了,這種情況不會(huì)進(jìn)行重發(fā),因?yàn)榘l(fā)送端知道,如果是數(shù)據(jù)段丟失,接收端不會(huì)放過(guò)它的,會(huì)瘋狂向它提醒…
3.擁塞控制
- 如果把窗口定的很大,發(fā)送端連續(xù)發(fā)送大量的數(shù)據(jù),可能會(huì)造成網(wǎng)絡(luò)的擁堵(大家都在用網(wǎng),你在這狂發(fā),吞吐量就那么大,當(dāng)然會(huì)堵),甚至造成網(wǎng)絡(luò)的癱瘓。所以TCP在為了防止這種情況而進(jìn)行了擁塞控制。
4.慢啟動(dòng):
- 定義擁塞窗口,一開(kāi)始將該窗口大小設(shè)為1,開(kāi)始發(fā)送數(shù)據(jù)的時(shí)候以低速傳輸,只要能夠得到對(duì)應(yīng)報(bào)文的ACK,就以指數(shù)級(jí)的速度提高速率。當(dāng)增長(zhǎng)到一個(gè)閾值時(shí),增長(zhǎng)速度就變成線(xiàn)性增長(zhǎng),而不是指數(shù)級(jí)的。或者是丟包嚴(yán)重了,說(shuō)明網(wǎng)絡(luò)出現(xiàn)擁塞,要降低發(fā)送速率,進(jìn)入擁塞避免階段。
5.擁塞避免
- 設(shè)置慢啟動(dòng)閾值,一般開(kāi)始都設(shè)為65536。擁塞避免是指當(dāng)擁塞窗口大小達(dá)到這個(gè)閾值,擁塞窗口的值不再指數(shù)上升,而是加法增加(每次確認(rèn)應(yīng)答/每個(gè)rtt,擁塞窗口大小+1),以此來(lái)避免擁塞。
- 將報(bào)文段的超時(shí)重傳看做擁塞,則一旦發(fā)生超時(shí)重傳,我們需要先將閾值設(shè)為當(dāng)前窗口大小的一半,并且將窗口大小設(shè)為初值1,然后重新進(jìn)入慢啟動(dòng)過(guò)程。
7.快速重傳
- 在遇到3次重復(fù)確認(rèn)應(yīng)答(高速重發(fā)控制)時(shí),代表收到了3個(gè)報(bào)文段,但是這之前的1個(gè)段丟失了,便對(duì)它進(jìn)行立即重傳。然后,先將閾值設(shè)為當(dāng)前窗口大小的一半,然后將擁塞窗口大小設(shè)為慢啟動(dòng)閾值+3的大小。
- 這樣可以達(dá)到:在TCP通信時(shí),網(wǎng)絡(luò)吞吐量呈現(xiàn)逐漸的上升,并且隨著擁堵來(lái)降低吞吐量,再進(jìn)入慢慢上升的過(guò)程,網(wǎng)絡(luò)不會(huì)輕易的發(fā)生癱瘓。
8.鏈接機(jī)制
- TCP建立連接和斷開(kāi)連接的過(guò)程:
-
三次握手:
-
Client將標(biāo)志位SYN置為1,隨機(jī)產(chǎn)生一個(gè)值seq=J,并將該數(shù)據(jù)包發(fā)送給Server,Client進(jìn)入SYN_SENT狀態(tài),等待Server確認(rèn)。
-
Server收到數(shù)據(jù)包后由標(biāo)志位SYN=1知道Client請(qǐng)求建立連接,Server將標(biāo)志位SYN和ACK都置為1,ack=J+1,隨機(jī)產(chǎn)生一個(gè)值seq=K,并將該數(shù)據(jù)包發(fā)送給Client以確認(rèn)連接請(qǐng)求,Server進(jìn)入SYN_RCVD狀態(tài)。
-
Client收到確認(rèn)后,檢查ack是否為J+1,ACK是否為1,如果正確則將標(biāo)志位ACK置為1,ack=K+1,并將該數(shù)據(jù)包發(fā)送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,Client和Server進(jìn)入ESTABLISHED狀態(tài),完成三次握手,隨后Client與Server之間可以開(kāi)始傳輸數(shù)據(jù)了。
-
四次揮手
-
由于TCP連接時(shí)全雙工的,因此,每個(gè)方向都必須要單獨(dú)進(jìn)行關(guān)閉,這一原則是當(dāng)一方完成數(shù)據(jù)發(fā)送任務(wù)后,發(fā)送一個(gè)FIN來(lái)終止這一方向的連接,收到一個(gè)FIN只是意味著這一方向上沒(méi)有數(shù)據(jù)流動(dòng)了,即不會(huì)再收到數(shù)據(jù)了,但是在這個(gè)TCP連接上仍然能夠發(fā)送數(shù)據(jù),直到這一方向也發(fā)送了FIN。首先進(jìn)行關(guān)閉的一方將執(zhí)行主動(dòng)關(guān)閉,而另一方則執(zhí)行被動(dòng)關(guān)閉。
-
1.數(shù)據(jù)傳輸結(jié)束后,客戶(hù)端的應(yīng)用進(jìn)程發(fā)出連接釋放報(bào)文段FIN(FIN = 1),序列號(hào)為u(seq = u),并停止發(fā)送數(shù)據(jù),客戶(hù)端進(jìn)入FIN_WAIT_1狀態(tài),此時(shí)客戶(hù)端依然可以接收服務(wù)器發(fā)送來(lái)的數(shù)據(jù)。
-
2.服務(wù)端發(fā)送ACK確認(rèn)報(bào)文(ACK = 1),序列號(hào)為v(seq = v),確認(rèn)報(bào)文u(ack = u + 1),進(jìn)入CLOSE-WAIT狀態(tài),繼續(xù)傳送數(shù)據(jù)。,客戶(hù)端收到上述報(bào)文進(jìn)入FIN-WAIT2狀態(tài),繼續(xù)接收服務(wù)端傳輸?shù)臄?shù)據(jù)
-
3.當(dāng)服務(wù)器沒(méi)有數(shù)據(jù)要發(fā)送時(shí),數(shù)據(jù)傳輸完畢后,發(fā)送FIN報(bào)文(FIN = 1,ACK = 1),序列號(hào)為w(seq = w),確認(rèn)報(bào)文u(ack = u + 1),進(jìn)入LAST-ACK狀態(tài),等待最后一個(gè)ACK。
-
4.客戶(hù)端發(fā)送ACK確認(rèn)報(bào)文(ACK = 1),序列號(hào)為u+1(seq = u + 1),確認(rèn)報(bào)文w(ack = w + 1),進(jìn)入TIME-WAIT狀態(tài),等待2MSL(最長(zhǎng)報(bào)文段壽命),客戶(hù)端進(jìn)入CLOSED狀態(tài),服務(wù)端收到后上述報(bào)文后進(jìn)入CLOSED狀態(tài)。
9.進(jìn)行三次握手、四次揮手及timewait的原因
-
1)三次握手原因:
-
三次握手是為了防止,客戶(hù)端的請(qǐng)求報(bào)文在網(wǎng)絡(luò)滯留,客戶(hù)端超時(shí)重傳了請(qǐng)求報(bào)文,服務(wù)端建立連接,傳輸數(shù)據(jù),釋放連接之后,服務(wù)器又收到了客戶(hù)端滯留的請(qǐng)求報(bào)文,建立連接一直等待客戶(hù)端發(fā)送數(shù)據(jù)。
-
服務(wù)器對(duì)客戶(hù)端的請(qǐng)求進(jìn)行回應(yīng)(第二次握手)后,就會(huì)理所當(dāng)然的認(rèn)為連接已建立,而如果客戶(hù)端并沒(méi)有收到服務(wù)器的回應(yīng)呢?此時(shí),客戶(hù)端仍認(rèn)為連接未建立,服務(wù)器會(huì)對(duì)已建立的連接保存必要的資源,如果大量的這種情況,服務(wù)器會(huì)崩潰。
-
因?yàn)門(mén)CP為保證可靠性,對(duì)傳輸對(duì)數(shù)據(jù)進(jìn)行序列號(hào),數(shù)據(jù)到達(dá)接收方,接收方需要發(fā)出一個(gè)確認(rèn)應(yīng)答,表示已經(jīng)收到該數(shù)據(jù)段,并且確認(rèn)序號(hào)會(huì)說(shuō)明了它下一次需要接收的數(shù)據(jù)序列號(hào),所以三次握手就是相互確認(rèn)序號(hào)的,兩次握手只能確認(rèn)一方的序列號(hào)
-
2)為什么TCP協(xié)議終止鏈接要四次?
-
當(dāng)客戶(hù)端確認(rèn)發(fā)送完數(shù)據(jù)且知道服務(wù)器已經(jīng)接收完了,想要關(guān)閉發(fā)送數(shù)據(jù)口(當(dāng)然確認(rèn)信號(hào)還是可以發(fā)),就會(huì)發(fā)FIN給服務(wù)器。
-
服務(wù)器收到客戶(hù)端發(fā)送的FIN,表示收到了,就會(huì)發(fā)送ACK回復(fù),這樣關(guān)閉客戶(hù)端到服務(wù)端的通信。
-
但這時(shí)候服務(wù)器可能還在發(fā)送數(shù)據(jù),沒(méi)有想要關(guān)閉數(shù)據(jù)口的意思,所以服務(wù)器的FIN與ACK不是同時(shí)發(fā)送的,而是等到服務(wù)器數(shù)據(jù)發(fā)送完了,才會(huì)發(fā)送FIN給客戶(hù)端。
-
客戶(hù)端收到服務(wù)器發(fā)來(lái)的FIN,知道服務(wù)器的數(shù)據(jù)也發(fā)送完了,回復(fù)ACK,客戶(hù)端等待2MSL以后,沒(méi)有收到服務(wù)器傳來(lái)的任何消息,知道服務(wù)器已經(jīng)收到自己的ACK了,客戶(hù)端就關(guān)閉鏈接,服務(wù)器也關(guān)閉鏈接了。
-
3)2MSL意義:
-
保證最后一次握手報(bào)文能到達(dá)服務(wù)端,可以進(jìn)行超時(shí)重傳
-
2MSL后,雙向連接產(chǎn)生的所有報(bào)文都會(huì)消失,不會(huì)影響下一次連接(msl是報(bào)文最大的生命周期)
-
如果沒(méi)有TIME_WAIT狀態(tài),主動(dòng)請(qǐng)求關(guān)閉鏈接的一方就會(huì)直接進(jìn)入關(guān)閉狀態(tài),如果因?yàn)榫W(wǎng)絡(luò)原因最后一個(gè)ACK發(fā)生了丟包,服務(wù)端就會(huì)不斷的請(qǐng)求FIN,等待最后一個(gè)ACK,鏈接并沒(méi)有成功關(guān)閉,并且如果此時(shí)打開(kāi)一個(gè)新的鏈接,那么服務(wù)器端就會(huì)把SYN請(qǐng)求當(dāng)成ACK,因而發(fā)生請(qǐng)求碼錯(cuò)誤,服務(wù)端就會(huì)發(fā)送RET重置鏈接。而TIME_WAIT的作用就是讓主動(dòng)請(qǐng)求的一方進(jìn)入TIME_WAIT狀態(tài)后等待2MSL時(shí)間關(guān)閉鏈接,等待這段時(shí)間是為了讓客戶(hù)端收到服務(wù)器端的FIN后可以有充分的時(shí)間回復(fù)ACK,讓網(wǎng)絡(luò)中延遲的FIN/ACK失效
上述所有的TCP可靠機(jī)制只是針對(duì)端與端之間的傳輸
二、問(wèn)題引入
-
面試官經(jīng)常會(huì)問(wèn)的一個(gè)問(wèn)題是,如果TCP服務(wù)器宕機(jī)了,會(huì)發(fā)生什么?換句話(huà)說(shuō),TCP真的可靠嗎?
-
這個(gè)問(wèn)題要從兩個(gè)方面來(lái)回答:
-
1.TCP是個(gè)可靠的協(xié)議,怎么保證它可靠的。
-
2.TCP并不能保證它所發(fā)送數(shù)據(jù)的可靠傳輸。
三、TCP如何保證可靠性?
首先,我們看看數(shù)據(jù)報(bào)不可靠有哪些問(wèn)題,以及TCP是怎么解決的?
- 1.差錯(cuò)
- TCP通過(guò)首部的校驗(yàn)和,可以校驗(yàn)首部和和數(shù)據(jù)。這是一種端到端的校驗(yàn),目的是檢測(cè)數(shù)據(jù)在傳輸過(guò)程中的任何變化,如果收到對(duì)端的校驗(yàn)和有差錯(cuò),TCP將這個(gè)包丟棄并且不確認(rèn)。
- 2.丟包
- TCP發(fā)出一個(gè)數(shù)據(jù)包后,啟動(dòng)一個(gè)定時(shí)器,等待對(duì)端確認(rèn)收到這個(gè)數(shù)據(jù)包,如果不能及時(shí)收到這個(gè)確認(rèn),將重發(fā)這個(gè)報(bào)文(超時(shí)重傳機(jī)制)。
- 3.失序
- TCP承載于IP數(shù)據(jù)包來(lái)傳輸,IP包的到達(dá)可能會(huì)失序,因此TCP數(shù)據(jù)包的到達(dá)也可能失序,TCP對(duì)收到的數(shù)據(jù)包按照首部的序列號(hào)進(jìn)行重新排序。
- 4.重復(fù)
- IP數(shù)據(jù)包會(huì)發(fā)生重復(fù),TCP接收端根據(jù)TCP首部的序列號(hào)將重復(fù)的數(shù)據(jù)丟棄。
- 此外,確認(rèn)數(shù)據(jù)包,也不能是確認(rèn)了一個(gè)數(shù)據(jù)包再發(fā)送下一個(gè)數(shù)據(jù)包,這不利于并行的批量發(fā)送,我們可以批量的發(fā)送,再批量的確認(rèn)。
- 這里有兩個(gè)問(wèn)題需要考慮:1.接收方的處理能力,2.網(wǎng)絡(luò)的處理能力。
- 1.首先來(lái)看看接收方的處理能力:當(dāng)接收方的硬件能力不如發(fā)送方,或者是系統(tǒng)繁忙,那發(fā)送過(guò)去的報(bào)文只能丟棄。要限制發(fā)送方的發(fā)送速度,接收方就要告訴發(fā)送方它的處理能力,好讓發(fā)送發(fā)方限制它的發(fā)送速度就可以了,這就是滑動(dòng)窗口的由來(lái)。
- 2.下面來(lái)看看網(wǎng)絡(luò)處理能力:如果發(fā)送TCP數(shù)據(jù)包的速度快于中間某個(gè)路由器的發(fā)速度,路由器就開(kāi)始丟包。導(dǎo)致較高的丟包率,如果TCP繼續(xù)保持這個(gè)速度送數(shù)據(jù),那么網(wǎng)絡(luò)的性能就會(huì)極大的降低。這就需要擁塞控制算法。它分為兩分,一個(gè)是慢啟動(dòng),一個(gè)是擁塞避免。
- 慢啟動(dòng)指的就是TCP在一開(kāi)始發(fā)送數(shù)據(jù)的時(shí)候以低速傳輸,只要能夠得到對(duì)應(yīng)報(bào)文的ACK,就以指數(shù)級(jí)的速度提高速率。當(dāng)增長(zhǎng)到一個(gè)閾值時(shí),增長(zhǎng)速度就變成線(xiàn)性增長(zhǎng),而不是指數(shù)級(jí)的。或者是丟包嚴(yán)重了,說(shuō)明網(wǎng)絡(luò)出現(xiàn)擁塞,要降低發(fā)送速率,進(jìn)入擁塞避免階段。
四、TCP并不能保證它所發(fā)送數(shù)據(jù)的可靠傳輸
可靠指的是什么,不可靠指的是什么?
- 上面我們討論了TCP通過(guò)很多機(jī)制保證可靠,這種可靠只是在端到端的通信上。
- 假設(shè)數(shù)據(jù)從A進(jìn)程送到B進(jìn)程,數(shù)據(jù)從A進(jìn)程通過(guò)它所在主機(jī)TCP/IP協(xié)議棧向下傳輸,經(jīng)過(guò)若干臺(tái)路由器,通過(guò)進(jìn)程B所在主機(jī)的TCP/IP協(xié)議棧向上傳輸,最后到達(dá)B進(jìn)程。這些路由器沒(méi)有TCP層,只是轉(zhuǎn)發(fā)IP數(shù)據(jù)報(bào),IP是個(gè)不可靠的協(xié)議。
- TCP能夠向進(jìn)程B保證所有到達(dá)的數(shù)據(jù)是按序且未受損的。但有個(gè)問(wèn)題, TCP已經(jīng)ACK的數(shù)據(jù)包實(shí)際上不一定會(huì)抵達(dá)應(yīng)用進(jìn)程。比如,接收端TCP剛對(duì)數(shù)據(jù)進(jìn)行ACK,但應(yīng)用程序還沒(méi)有讀走,就崩潰了。
- 針對(duì)TCP的ACK的數(shù)據(jù)報(bào)不能抵達(dá)目的應(yīng)用程序的解決方案
- 我們的解決方案是應(yīng)用層ACK。下面給一個(gè)簡(jiǎn)單的實(shí)現(xiàn),我們采用停等的方式來(lái)實(shí)現(xiàn)回射客戶(hù)服務(wù)器。
- 這里設(shè)計(jì)成客戶(hù)端和服務(wù)器有兩條通道,主要的原因是想讓發(fā)送數(shù)據(jù)模塊和接收網(wǎng)絡(luò)數(shù)據(jù)模塊都能夠獲取網(wǎng)絡(luò)中對(duì)端的狀態(tài),而不是將狀態(tài)混在一條通道上。
- 這里給大家實(shí)現(xiàn)向外發(fā)送數(shù)據(jù)模塊。實(shí)現(xiàn)思路是:發(fā)送一條消息后,在定時(shí)器到之前必須接收對(duì)等實(shí)體發(fā)過(guò)來(lái)的應(yīng)用層ACK,如果定時(shí)器時(shí)間到,我們就終止程序,當(dāng)然,你可以實(shí)現(xiàn)的更復(fù)雜,比如重傳。
當(dāng)然這里設(shè)計(jì)的是停等協(xié)議,如果需要做的更好,像TCP內(nèi)核協(xié)議棧一樣,可以考慮把應(yīng)用程序做成事件驅(qū)動(dòng)的,這是一個(gè)軟件設(shè)計(jì)的問(wèn)題
五、故障類(lèi)型
- 通過(guò)前面的討論我們可以看到網(wǎng)絡(luò)程序員不能認(rèn)為T(mén)CP為我們做好了一切。 我們可以把故障分為兩類(lèi):
- 1.收不到FIN的故障,比如網(wǎng)絡(luò)掉線(xiàn),或者主機(jī)崩潰都是這種情況。
- 2.能收到FIN的故障,比如對(duì)方應(yīng)用程序崩潰。
1.收不到FIN的故障
- 先來(lái)說(shuō)說(shuō)沒(méi)有FIN的故障,分成四種情況:
- 1.如果剛好阻塞在read函數(shù)上,這時(shí)沒(méi)法恢復(fù)。可以通過(guò)設(shè)置讀超時(shí)來(lái)解決。
- 2.如果是先write,再read,協(xié)議棧會(huì)持續(xù)重傳。經(jīng)過(guò)多次重傳不成功,協(xié)議棧會(huì)標(biāo)記連接異常,阻塞的read就會(huì)得到TIMEOUT錯(cuò)誤。
- 3.如果是阻塞在select或epoll上,建議做心跳包。下面是一個(gè)有心跳功能的回射客戶(hù)服務(wù)器程序客戶(hù)端程序。十秒中如果沒(méi)有數(shù)據(jù)通信,就心跳,執(zhí)行三次如果沒(méi)有應(yīng)答,就退出。
- 4.還有一種特殊情況就是,如果是主機(jī)崩潰又重啟了,這時(shí)對(duì)端主機(jī)得到RST錯(cuò)誤。
2.能收到FIN的故障
- 再來(lái)看看能收到FIN的故障,這里要意識(shí)到,從一個(gè)用程序角度,對(duì)端進(jìn)程崩潰還是調(diào)用了close以及exit是無(wú)法區(qū)分的,在這兩種情況下TCP都會(huì)向我們發(fā)送一個(gè)FIN
- 1.如果是read,直接得到FIN信息,返回0。
- 2.如果是write,則第一次調(diào)用會(huì)得到RST。
- 3.收到RST,再多次調(diào)用write就得到SIGPIPE信號(hào)。
3.總結(jié)
- 1.TCP通過(guò)序號(hào)和超時(shí)重傳保證了端到端的可靠。
- 2.TCP并不能保證應(yīng)用層的可靠。
- 3.異常的情況分為,網(wǎng)絡(luò)故障,主機(jī)崩潰和進(jìn)程崩潰。網(wǎng)絡(luò)故障和主機(jī)故障可以看作是一類(lèi)故障,當(dāng)然是指除了主機(jī)崩潰并在TCP放棄連接之前,就重啟了的情況。
總結(jié)
- 上一篇: 给你一个能生成1到5随机数的函数,用它写
- 下一篇: 美团--字符串计数