首頁 > 軟體

TCP三次握手與四次揮手詳解

2020-06-16 16:35:11
TCP報文結構

 

  • 源埠和目的埠:各占2個位元組,分別寫入源埠號和目的埠號。
  • 序號:占4個位元組。序號使用mod運算。TCP是面向位元組流的,在一個TCP連線中傳送的位元組流中的每一個位元組都按順序編號。故該欄位也叫做“報文段序號”。
  • 確認序號:占4個位元組,是期望收到對方下一個報文段的第一個資料位元組的序號。若確認序號=N,則表明:到序號N-1為止的所有資料都已正確收到。
  • 資料偏移:占4位元,表示TCP報文段的首部長度。注意,“資料偏移”的單位是32位元字(即以4位元組長的字為計算單位)。故TCP首部的最大長度為60位元組。
  • 保留:占6位,保留為今後使用,目前置為0;
  • 緊急URG:當URG=1,表明緊急指標欄位有效。這時傳送方TCP就把緊急資料插入到本報文段資料的最前面,而在緊急資料後面的資料仍是普通資料。
  • 確認ACK:當ACK=1時,確認欄位才有效。當ACK=0時,確認號無效。TCP規定,在連線建立後所有傳送的報文段都必須把ACK置1。
  • 推播PSH:接收方TCP收到PSH=1的報文段,就盡快地交付給接收應用進程,而不再等到整個快取都填滿了後再向上交付。
  • 復位RST:當RST=1時,表明TCP連線中出現嚴重差錯,必須釋放連線,然後再重新建立運輸連線。
  • 同步SYN:在連線建立時用來同步序號。當SYN=1而ACK=0時,表明這是一個連線請求報文段。對方若同意建立連線,則應在響應的報文段中使SYN=1和ACK=1。故SYN置為1,就表示這是一個連線請求和連線接收報文。
  • 終止FIN:用來釋放連線。當FIN=1時,表明此報文段的傳送方的資料已傳送完畢,並要求釋放運輸連線。
  • 視窗:占2個位元組。視窗值作為接收方讓傳送方設定其傳送視窗的依據。
  • 檢驗和:占2位元組。檢驗和欄位檢驗的範圍包括首部和資料這兩部分。和UDP資料包一樣,在計算檢驗和時,也要在TCP報文段的前面加上12位元組的偽首部。偽首部的格式與UDP使用者資料包的偽首部一樣,但要將偽首部第四個欄位中的17 改為6(協定號),把第5欄位中的UDP長度改為TCP長度。
  • 緊急指標:占2位元組。緊急指標僅在URG=1時才有意義,它指出本報文段中的緊急資料的位元組數。

TCP三次握手

整個流程為:

  1. 用戶端主動開啟,傳送連線請求報文段,將SYN標識位置為1,Sequence Number置為x(TCP規定SYN=1時不能攜帶資料,x為隨機產生的一個值),然後進入SYN_SEND狀態
  2. 伺服器收到SYN報文段進行確認,將SYN標識位置為1,ACK置為1,Sequence Number置為y,Acknowledgment Number置為x+1,然後進入SYN_RECV狀態,這個狀態被稱為半連線狀態
  3. 用戶端再進行一次確認,將ACK置為1(此時不用SYN),Sequence Number置為x+1,Acknowledgment Number置為y+1發向伺服器,最後用戶端與伺服器都進入ESTABLISHED狀態

為什麼在第3步中用戶端還要再進行一次確認呢?

這主要是為了防止已經失效的連線請求報文段突然又傳回到伺服器端而產生錯誤的場景:所謂"已失效的連線請求報文段"是這樣產生的。正常來說,用戶端發出連線請求,但因為連線請求報文丟失而未收到確認。於是用戶端再次發出一次連線請求,後來收到了確認,建立了連線。資料傳輸完畢後,釋放了連線,用戶端一共傳送了兩個連線請求報文段,其中第一個丟失,第二個到達了伺服器端,沒有"已失效的連線請求報文段"。

現在假定一種異常情況,即用戶端發出的第一個連線請求報文段並沒有丟失,只是在某些網路節點長時間滯留了,以至於延誤到連線釋放以後的某個時間點才到達伺服器端。本來這個連線請求已經失效了,但是伺服器端收到此失效的連線請求報文段後,就誤認為這是用戶端又發出了一次新的連線請求。於是伺服器端又向用戶端發出請求報文段,同意建立連線。假定不採用三次握手,那麼只要伺服器端發出確認,連線就建立了。

由於現在用戶端並沒有發出連線建立的請求,因此不會理會伺服器端的確認,也不會向伺服器端傳送資料,但是伺服器端卻以為新的傳輸連線已經建立了,並一直等待用戶端發來資料,這樣伺服器端的許多資源就這樣白白浪費了。

採用三次握手的辦法可以防止上述現象的發生。比如在上述的場景下,用戶端不向伺服器端的發出確認請求,伺服器端由於收不到確認,就知道用戶端並沒有要求建立連線。

TCP四次揮手

TCP三次握手是TCP連線建立的過程,TCP四次揮手則是TCP連線釋放的過程下面是TCP四次揮手的流程圖:

 

當用戶端沒有資料再需要傳送給伺服器端時,就需要釋放用戶端的連線,這整個過程為:

  1. 用戶端傳送一個報文給伺服器端(沒有資料),其中FIN設定為1,Sequence Number置為u,用戶端進入FIN_WAIT_1狀態
  2. 伺服器端收到來自用戶端的請求,傳送一個ACK給用戶端,Acknowledge置為u+1,同時傳送Sequence Number為v,伺服器端年進入CLOSE_WAIT狀態
  3. 伺服器端傳送一個FIN給用戶端,ACK置為1,Sequence置為w,Acknowledge置為u+1,用來關閉伺服器端到用戶端的資料傳送,伺服器端進入LAST_ACK狀態
  4. 用戶端收到FIN後,進入TIME_WAIT狀態,接著傳送一個ACK給伺服器端,Acknowledge置為w+1,Sequence Number置為u+1,最後用戶端和伺服器端都進入CLOSED狀態

 

 

為什麼建連結要3次握手,斷連結需要4次揮手?

  • 對於建連結的3次握手,主要是要初始化Sequence Number 的初始值。通訊的雙方要互相通知對方自己的初始化的Sequence Number(縮寫為ISN:Inital Sequence Number)——所以叫SYN,全稱Synchronize Sequence Numbers。也就上圖中的 x 和 y。這個號要作為以後的資料通訊的序號,以保證應用層接收到的資料不會因為網路上的傳輸的問題而亂序(TCP會用這個序號來拼接資料)。
  • 對於4次揮手,其實你仔細看是2次,因為TCP是全雙工的,所以,傳送方和接收方都需要Fin和Ack。只不過,有一方是被動的,所以看上去就成了所謂的4次揮手。如果兩邊同時斷連線,那就會就進入到CLOSING狀態,然後到達TIME_WAIT狀態。下圖是雙方同時斷連線的示意圖(你同樣可以對照著TCP狀態機看):

 

使用Wireshark抓包驗證TCP三次握手過程

為了加深對TCP三次握手的理解,抓包看一下TCP三次握手的過程。

抓包下來的內容為:

這裡多說一句,由於wireshark抓包針對的是網絡卡,因此只要某張網絡卡上有網路存取,就會有封包,這會導致Wireshark的抓包結果裡面會有大量封包,而大多數都不是想要的,這種情況可以使用Wireshark的過濾規則。我這裡由於知道目標ip,因此使用的是"ip.src == xxx.xxx.xxx.xxx or ip.dst == xxx.xxx.xxx.xxx"這條規則只過濾特定的ip。

從抓包結果看來,整個過程符合TCP三次握手的預期:

  1. 用戶端傳送SYN給伺服器端
  2. 伺服器端返回SYN+ACK給用戶端
  3. 用戶端確認,返回ACK給伺服器端

至於Sequence Number和Acknowledge Number就不看了,但是注意,前面說了Sequence Number是隨機產生的一個值,但是這裡確是0,不光這裡是0,抓其他的任何包這個值都是0。但其實這裡並不是真的0,而是Wireshark為了顯示更好閱讀,使用了relative sequence number相對序號,Sequence Number具體值我們也是可以看到的:

第一個紅框就是上面說的relative sequence number,第二個紅框就是Sequence Number的真實值0xc978aa7e,轉換為十進位制為3380128382,就是隨機產生的Sequence Number。

順便能看到,下一個封包就是HTTP???封包,因為TCP三次握手已完成,連線建立,正式傳輸應用層資料,傳輸的HTTP內容大小為704位元組。


IT145.com E-mail:sddin#qq.com