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