TCP格式三次握手與四次揮手,看這一篇就懂了

艾陽丶發表於2017-05-04

TCP 協議格式


    TCP協議間交換的資料單元稱為“TCP段”。它包括兩部分,分別為首部資料。而首部標準長為20位元組(bytes),攜帶選項的首部最長可達到60位元組(bytes)。如下圖所示。


(1)源埠、目的埠號

    各佔2位元組,埠號加上IP地址共同構成socket。互相通訊的程式使用一對socket,包括協議、源IP、源埠、目的IP、目的埠,這五個元素唯一確定一個TCP連線。

(2) 序號

    佔4位元組,是TCP段所傳送的資料部分第一個位元組的序號。在TCP傳送的資料流中,每一個位元組都有一個序號。建立連線時,傳送方將初始序號(Initial Sequence Number, ISN)填寫到第一個傳送的TCP段序號中。

(3)確認號

    佔4位元組,是期望收到對方下次傳送的資料的第一個位元組的序號,也就是期望收到的下一個TCP段的首部中的序號,等於已經成功收到的TCP段的最後一個位元組序號加1。確認號在ACK標誌為1時有意義,除了主動發起連線的第一個TCP段不設定ACK標誌外,其後傳送的TCP段都會設定ACK標誌。

(4) 資料偏移

佔4位元,表示資料開始的地方離TCP段的起始處有多遠。實際上就是TCP段首部的長度。由於首部長度不固定,因此資料偏移欄位是必要的。資料偏移以32位為長度單位,因此TCP首部的最大長度是60(15*4)個位元組。

(5)控制位

    一共6個,佔6位元,設定為1時有效。按順序依次為:URG、ACK、PSH、RST、SYN、FIN。

URG

緊急位,為1時,首部中的緊急指標有效

ACK

確認位,為1時,首部中的確認號有效

PSH

推位,為1時,要求把資料儘快交給應用程式

RST

復位標誌,為1時,復位連線,一般在出錯或關閉連線時使用

SYN

同步位,在建立連線時使用,當SYN=1而ACK=0時,表明這是一個連線請求報文段。對方若同意建立連線,在發回的報文段中使SYN=1和ACK=1

FIN

結束位,為1時,表示傳送方完成了資料傳送


(6) 視窗

    佔2位元組,表示報文段傳送方期望接收的位元組數,可接收的序號範圍是從接收方的確認號開始到確認號加上視窗大小之間的資料。

(7)校驗和

    校驗和包含了偽首部、TCP首部和資料,校驗和是TCP強制要求的,由傳送方計算,接收方驗證。

(8)緊急指標

    URG標誌為1時,緊急指標有效,表示資料需要優先處理。緊急指標指出在TCP段中的緊急資料的最後一個位元組的序號,使接收方可以知道緊急資料共有多長。

(9)選項

    最常用的選項是最大段大小(Maximum Segment Size,MSS),向對方通知本機可以接收的最大TCP段長度。MSS選項只在建立連線的請求中傳送。

三次握手

在 TCP/IP 協議中,TCP 協議提供可靠的連線服務,採用三次握手建立一個連線。

 


第一次握手:建立連線時,客戶端傳送 syn 包(tcp協議中syn位置1,序號為J)到伺服器,並進入 SYN_SEND 狀態,等待伺服器確認; 


第二次握手:伺服器收到 syn 包,必須確認客戶的 SYN,同時自己也傳送一個 SYN 包,即 SYN+ACK包(tcp協議中syn位置1,ack位置1,序號K,確定序號為J+1),此時伺服器進入 SYN_RECV 狀態; 


第三次握手:客戶端收到伺服器的 SYN+ACK 包,向伺服器傳送確認包 ACK(tcp協議中ack位置1,確認序號K+1),此包傳送完畢,客戶端和伺服器進入 ESTABLISHED 狀態,完成三次握手。


 


    通過這樣的三次握手,客戶端與服務端建立起可靠的雙工的連線,開始傳送資料。 三次握手的最主要目的是保證連線是雙工的,可靠更多的是通過重傳機制來保證的。但是為什麼一定要進行三次握手來保證連線是雙工的呢,一次不行麼?兩次不行麼?


我們舉一個現實生活中兩個人進行語言溝通的例子來模擬三次握手


第一次對話: 
    老婆讓甲出去打醬油,半路碰到一個朋友乙,甲問了一句:哥們你吃飯了麼?結果乙帶著耳機聽歌呢,根本沒聽到,沒反應。甲心裡想:跟你說話也沒個音,不跟你說了,溝通失敗。說明乙接受不到甲傳過來的資訊的情況下溝通肯定是失敗的。如果乙聽到了甲說的話,那麼第一次對話成功,接下來進行第二次對話。 


第二次對話: 
    乙聽到了甲說的話,但是他是老外,中文不好,不知道甲說的啥意思也不知道怎樣回答,於是隨便回答了一句學過的中文 :我去廁所了。甲一聽立刻笑噴了,“去廁所吃飯”?道不同不相為謀,離你遠點吧,溝通失敗。說明乙無法做出正確應答的情況下溝通失敗。如果乙聽到了甲的話,做出了正確的應答,並且還進行了反問:我吃飯了,你呢?那麼第二次握手成功。 
通過前兩次對話證明了乙能夠聽懂甲說的話,並且能做出正確的應答。接下來進行第三次對話。 

第三次對話: 

    甲剛和乙打了個招呼,突然老婆喊他,“你個死鬼,打個醬油咋這麼半天,看我回家咋收拾你”,甲是個妻管嚴,聽完嚇得二話不說就跑回家了,把乙自己晾那了。乙心想:這什麼人啊,得,我也回家吧,溝通失敗。說明甲無法做出應答的情況下溝通失敗。 如果甲也做出了正確的應答:我也吃了。那麼第三次對話成功,兩人已經建立起了順暢的溝通渠道,接下來開始持續的聊天。 

    通過第二次和第三次的對話證明了甲能夠聽懂乙說的話,並且能做出正確的應答。 可見,兩個人進行有效的語言溝通,這三次對話的過程是必須的。
 
同理對於TCP為什麼需要進行三次握手我們可以一樣的理解: 
    為了保證服務端能收接受到客戶端的資訊並能做出正確的應答而進行前兩次(第一次和第二次)握手,為了保證客戶端能夠接收到服務端的資訊並能做出正確的應答而進行後兩次(第二次和第三次)握手。 


四次揮手


    由於 TCP 連線是全雙工的,因此每個方向都必須單獨進行關閉。這好比,我們打電話(全雙工),正常的情況下(出於禮貌),通話的雙方都要說再見後才能掛電話,保證通訊雙方都把話說完了才掛電話。



那TCP 的四次握手,是為了保證通訊雙方都關閉了連線,具體過程如下:




1)客戶端 A 在應用層呼叫close時會激發底層傳送一個 FIN(tcp協議中FIN位置1、序號為M,結合上圖分析)請求,用來關閉客戶 A 到伺服器 B 的資料傳送,客戶端A此時處於半關閉狀態應用層無法接收資料但底層還可以接收資料);


2)伺服器 B 底層收到客戶端A的FIN時會做兩件事

2.1)第1件事:收到客戶端A的FIN時底層會主動回發一個ACK(tcp協議中ACK位置1,確認序號M+1

2.2)第2件事:收到客戶端A的FIN時,導致伺服器B的應用層read()返回0(告訴伺服器B應用層:客戶端A關閉了


3)伺服器B應用層呼叫close()激發底層給客戶端 A 傳送一個 FIN(tcp協議中FIN位置1、序號為N),這是伺服器B已處於半關閉狀態;


4)客戶端 A 底層發 ACK(tcp協議中ACK位置1,確認序號N+1) 給伺服器B,這是客戶端A、伺服器B都處於完全關閉狀態,回收相應的資源。





為什麼建立連線協議是三次握手,而關閉連線卻是四次握手呢?


    這是因為服務端的 LISTEN 狀態下的 SOCKET 當收到 SYN 報文的建連請求後,它可以把 ACK 和 SYN(ACK 起應答作用,而 SYN 起同步作用)放在一個報文裡來傳送。但關閉連線時,當收到FIN 報文通知時,如果能將ACK、FIN放在一個報文裡那麼就有了三次揮手,但是這是不可能,因為ACK是伺服器B一收到FIN報文底層就回發的,而伺服器B的FIN是應用層呼叫close()激發的,所以它這裡的 ACK 報文和 FIN 報文在傳送的時間上都是分開的,不可能同時傳送


為什麼 TIME_WAIT 狀態還需要等 2MS L後才能返回到 CLOSED 狀態?


    這是因為雖然雙方都同意關閉連線了,而且握手的 4 個報文也都協調和傳送完畢,按理可以直接回到 CLOSED 狀態(就好比從 SYN_SEND 狀態到 ESTABLISH 狀態那樣);但是因為我們必須要假想網路是不可靠的,你無法保證你最後傳送的 ACK 報文會一定被對方收到,因此對方處於 LAST_ACK 狀態下的 SOCKET 可能會因為超時未收到 ACK 報文,而重發 FIN 報文,所以這個 TIME_WAIT 狀態的作用就是用來重發可能丟失的 ACK 報文。






相關文章