TCP詳解

不要必應發表於2019-08-30

一、三次握手

連線是一個通訊行為。建立連線,就能使用連線進行通訊。連線作用與兩個節點。TCP是有狀態的協議,因此兩個節點之間想要通過TCP傳送資料,必須先建立可靠有效的連線。

TCP是雙通道的通訊模式,因此TCP的連線其實是兩條物理連線,即客戶端到伺服器和伺服器到客戶端的連線

  1. 客戶端傳送一個帶SYN標誌的TCP報文到伺服器。這是三次握手過程中的報文1。
  2. 伺服器端迴應客戶端的,這是三次握手中的第2個報文,這個報文同時帶ACK標誌和SYN標誌。因此它表示對剛才客戶端SYN報文的迴應;同時又標誌SYN給客戶端,詢問客戶端是否準備好進行資料通訊。
  3. 客戶必須再次迴應服務段一個ACK報文,這是報文段3。 
    三次握手
為什麼需要“三次握手”?

為了防止已失效的連線請求報文段突然又傳送到了服務端,因而產生錯誤。

具體例子: ——“已失效的連線請求報文段”的產生在這樣一種情況下:

client發出的第一個連線請求報文段並沒有丟失,而是在某個網路結點長時間的滯留了,以致延誤到連線釋放以後的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連線請求報文段後,就誤認為是client再次發出的一個新的連線請求。於是就向client發出確認報文段,同意建立連線。假設不採用“三次握手”,那麼只要server發出確認,新的連線就建立了。由於現在client並沒有發出建立連線的請求,因此不會理睬server的確認,也不會向server傳送資料。但server卻以為新的運輸連線已經建立,並一直等待client發來資料。這樣,server的很多資源就白白浪費掉了。採用“三次握手”的辦法可以防止上述現象發生。

例如剛才那種情況,client不會向server的確認發出確認。server由於收不到確認,就知道client並沒有要求建立連線。

二、四次揮手

由於TCP連線是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的資料傳送任務後就傳送一個FIN來終止這個方向的連線。收到一個 FIN只意味著這一方向上沒有資料流動,一個TCP連線在收到一個FIN後仍能傳送資料。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

  1. TCP客戶端傳送一個FIN,用來關閉客戶到伺服器的資料傳送(報文段4)。
  2. 伺服器收到這個FIN,它發回一個ACK,確認序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號。
  3. 伺服器關閉客戶端的連線,傳送一個FIN給客戶端(報文段6)。
  4. 客戶段發回ACK報文確認,並將確認序號設定為收到序號加1(報文段7)。

四次揮手

為什麼需要“四次揮手”?

TCP協議是一種面向連線的、可靠的、基於位元組流的運輸層通訊協議。TCP是全雙工模式,這就意味著,當主機1發出FIN報文段時,只是表示主機1已經沒有資料要傳送了,主機1告訴主機2,它的資料已經全部傳送完畢了;但是,這個時候主機1還是可以接受來自主機2的資料;當主機2返回ACK報文段時,表示它已經知道主機1沒有資料傳送了,但是主機2還是可以傳送資料到主機1的;當主機2也傳送了FIN報文段時,這個時候就表示主機2也沒有資料要傳送了,就會告訴主機1,我也沒有資料要傳送了,之後彼此就會愉快的中斷這次TCP連線。

為什麼要等待2MSL?

MSL:報文段最大生存時間,它是任何報文段被丟棄前在網路內的最長時間。

原因有二:

  • 保證TCP協議的全雙工連線能夠可靠關閉
  • 保證這次連線的重複資料段從網路中消失

第一點:如果主機1直接CLOSED了,那麼由於IP協議的不可靠性或者是其它網路原因,導致主機2沒有收到主機1最後回覆的ACK。那麼主機2就會在超時之後繼續傳送FIN,此時由於主機1已經CLOSED了,就找不到與重發的FIN對應的連線。所以,主機1不是直接進入CLOSED,而是要保持TIME_WAIT狀態。當再次收到FIN的時候,能夠保證對方收到ACK,最後正確的關閉連線。

第二點:如果主機1直接CLOSED,然後又再向主機2發起一個新連線,我們不能保證這個新連線與剛關閉的連線的埠號是不同的。也就是說有可能新連線和老連線的埠號是相同的。一般來說不會發生什麼問題,但是還是有特殊情況出現:假設新連線和已經關閉的老連線埠號是一樣的,如果前一次連線的某些資料仍然滯留在網路中(稱為Lost Duplicate),這些延遲資料在建立新連線之後才到達主機2,由於新連線和老連線的埠號是一樣的,TCP協議就認為那個延遲的資料是屬於新連線的,這樣就和真正的新連線的資料包發生混淆了。所以TCP連線要在TIME_WAIT狀態等待2倍MSL,保證本次連線的所有資料都從網路中消失。

(TCP 連線和斷開的過程中,都是一問一答的方式,建立連線的問是syn,回答是ack,同樣斷開連線的問是fin,回答是ack。有問必有答,那麼連線就能正常的建立和關閉。因為tcp通訊是雙通道的,因此一個TCP邏輯連線,實際上是兩條成對client和server端的物理連線。無論連線和斷開,都需要把這兩個連線都處理完畢才能完成。也因為這兩個過程,中間衍生出了很多狀態。這些狀態在建立連線的時候有client和server端的區分,在斷開的連線的時候就沒有特別區別,只有主動斷開和被動斷開的差別。)

三、TCP流量控制

如果傳送方把資料傳送得過快,接收方可能會來不及接收,這就會造成資料的丟失。所謂流量控制就是讓傳送方的傳送速率不要太快,要讓接收方來得及接收。

利用滑動視窗機制可以很方便地在TCP連線上實現對傳送方的流量控制。

設A向B傳送資料。在連線建立時,B告訴了A:“我的接收視窗是 rwnd = 400 ”(這裡的 rwnd 表示 receiver window) 。因此,傳送方的傳送視窗不能超過接收方給出的接收視窗的數值。請注意,TCP的視窗單位是位元組,不是報文段。假設每一個報文段為100位元組長,而資料包文段序號的初始值設為1。大寫ACK表示首部中的確認位ACK,小寫ack表示確認欄位的值ack。

滑動視窗

從圖中可以看出,B進行了三次流量控制。第一次把視窗減少到 rwnd = 300 ,第二次又減到了 rwnd = 100 ,最後減到 rwnd = 0 ,即不允許傳送方再傳送資料了。這種使傳送方暫停傳送的狀態將持續到主機B重新發出一個新的視窗值為止。B向A傳送的三個報文段都設定了 ACK = 1 ,只有在ACK=1時確認號欄位才有意義。

TCP為每一個連線設有一個持續計時器(persistence timer)。當TCP連線中的傳送方收到接收方的零視窗通知時,傳送方就啟動持續計時器。若持續計時器設定的時間到期,傳送方就傳送一個零視窗控測報文段(攜1位元組的資料)給接收方。如果接收方可以接收資料,就重新開始傳送資料;如果接收方不能接收資料,就重新設定持續計時器。

四、TCP擁塞控制

1.慢開始和擁塞避免

傳送方維持一個擁塞視窗 cwnd ( congestion window )的狀態變數。擁塞視窗的大小取決於網路的擁塞程度,並且動態地在變化。傳送方讓自己的傳送視窗等於擁塞視窗。

傳送方控制擁塞視窗的原則是:只要網路沒有出現擁塞,擁塞視窗就再增大一些,以便把更多的分組傳送出去。但只要網路出現擁塞,擁塞視窗就減小一些,以減少注入到網路中的分組數。

慢開始演算法:

當主機開始傳送資料時,如果立即所大量資料位元組注入到網路,那麼就有可能引起網路擁塞,因為現在並不清楚網路的負荷情況。

因此,較好的方法是,先探測一下,即由小到大逐漸增大傳送視窗,也就是說,由小到大逐漸增大擁塞視窗數值。

通常在剛剛開始傳送報文段時,先把擁塞視窗 cwnd 設定為一個最大報文段MSS(Maximum Segment Size,最大報文長度)的數值。而在每收到一個對新的報文段的確認後,把擁塞視窗增加至多一個MSS的數值(底數為2的指數增長規律)。用這樣的方法逐步增大傳送方的擁塞視窗 cwnd ,可以使分組注入到網路的速率更加合理。

慢開始

每經過一個傳輸輪次,擁塞視窗 cwnd 就加倍。

一個傳輸輪次所經歷的時間其實就是往返時間RTT。

不過“傳輸輪次”更加強調:把擁塞視窗cwnd所允許傳送的報文段都連續傳送出去,並收到了對已傳送的最後一個位元組的確認。

另,慢開始的“慢”並不是指cwnd的增長速率慢,而是指在TCP開始傳送報文段時先設定cwnd=1,使得傳送方在開始時只傳送一個報文段(目的是試探一下網路的擁塞情況),然後再逐漸增大cwnd。

為了防止擁塞視窗cwnd增長過大引起網路擁塞,還需要設定一個慢開始門限ssthresh狀態變數。慢開始門限ssthresh的用法如下:

當 cwnd < ssthresh 時,使用上述的慢開始演算法。 當 cwnd > ssthresh 時,停止使用慢開始演算法而改用擁塞避免演算法。 當 cwnd = ssthresh 時,既可使用慢開始演算法,也可使用擁塞控制避免演算法。

擁塞避免

讓擁塞視窗cwnd緩慢地增大,即每經過一個往返時間RTT就把傳送方的擁塞視窗cwnd加1,而不是加倍。這樣擁塞視窗cwnd按線性增長規律緩慢增長,比慢開始演算法的擁塞視窗增長速率緩慢得多。

無論在慢開始階段還是在擁塞避免階段,只要傳送方判斷網路出現擁塞(其根據就是沒有收到確認),就要把慢開始門限ssthresh設定為出現擁塞時的傳送 方視窗值的一半(但不能小於2)。然後把擁塞視窗cwnd重新設定為1,執行慢開始演算法。

這樣做的目的就是要迅速減少主機傳送到網路中的分組數,使得發生 擁塞的路由器有足夠時間把佇列中積壓的分組處理完畢。

如下圖,用具體數值說明了上述擁塞控制的過程。現在傳送視窗的大小和擁塞視窗一樣大。

擁塞控制

2.快重傳和快恢復

快重傳演算法首先要求接收方每收到一個失序的報文段後就立即發出重複確認(為的是使傳送方及早知道有報文段沒有到達對方)而不要等到自己傳送資料時才進行捎帶確認。

快重傳

接收方收到了M1和M2後都分別發出了確認。現在假定接收方沒有收到M3但接著收到了M4。

顯然,接收方不能確認M4,因為M4是收到的失序報文段。根據 可靠傳輸原理,接收方可以什麼都不做,也可以在適當時機傳送一次對M2的確認。

但按照快重傳演算法的規定,接收方應及時傳送對M2的重複確認,這樣做可以讓傳送方及早知道報文段M3沒有到達接收方。傳送方接著傳送了M5和M6。接收方收到這兩個報文後,也還要再次發出對M2的重複確認。這樣,傳送方共收到了接收方的四個對M2的確認,其中後三個都是重複確認。

快重傳演算法還規定,傳送方只要一連收到三個重複確認就應當立即重傳對方尚未收到的報文段M3,而不必 繼續等待M3設定的重傳計時器到期。

由於傳送方儘早重傳未被確認的報文段,因此採用快重傳後可以使整個網路吞吐量提高約20%。

與快重傳配合使用的還有快恢復演算法,其過程有以下兩個要點:

當傳送方連續收到三個重複確認,就執行“乘法減小”演算法,把慢開始門限ssthresh減半。

與慢開始不同之處是現在不執行慢開始演算法(即擁塞視窗cwnd現在不設定為1),而是把cwnd值設定為 慢開始門限ssthresh減半後的數值,然後開始執行擁塞避免演算法(“加法增大”),使擁塞視窗緩慢地線性增大。

image.png

相關文章