這次一定讓你記住 TCP 三次握手、四手揮手!

架構文摘發表於2020-10-12

TCP協議全稱為:Transmission Control Protocol,是一種面向連結、保證資料傳輸安全、可靠的資料傳輸協議。為了確保資料的可靠傳輸,不僅需要對發出的每個位元組進行編號確認,還需要驗證每一個資料包的有效性。每個TCP資料包是封閉在IP包中的,每個一IP包的後面緊跟著的是TCP頭,TCP報文格式如下:

源埠和目的埠欄位

  • TCP源埠(Source Port):源計算機上的應用程式的埠號,佔 16 位。
  • TCP目的埠(Destination Port):目標計算機的應用程式埠號,佔 16 位。

序列號欄位

CP序列號(Sequence Number):佔 32 位。它表示本報文段所傳送資料的第一個位元組的編號。在 TCP 連線中,所傳送的位元組流的每一個位元組都會按順序編號。當SYN標記不為1時,這是當前資料分段第一個字母的序列號;如果SYN的值是1時,這個欄位的值就是初始序列值(ISN),用於對序列號進行同步。這時,第一個位元組的序列號比這個欄位的值大1,也就是ISN加1。

確認號欄位

TCP 確認號(Acknowledgment Number,ACK Number):佔 32 位。它表示接收方期望收到傳送方下一個報文段的第一個位元組資料的編號。其值是接收計算機即將接收到的下一個序列號,也就是下一個接收到的位元組的序列號加1。

資料偏移欄位

TCP 首部長度(Header Length):資料偏移是指資料段中的“資料”部分起始處距離 TCP 資料段起始處的位元組偏移量,佔 4 位。其實這裡的“資料偏移”也是在確定 TCP 資料段頭部分的長度,告訴接收端的應用程式,資料從何處開始。

保留欄位

保留(Reserved):佔 4 位。為 TCP 將來的發展預留空間,目前必須全部為 0。

標誌位欄位

  • CWR(Congestion Window Reduce):擁塞視窗減少標誌,用來表明它接收到了設定 ECE 標誌的 TCP 包。並且,傳送方收到訊息之後,通過減小傳送視窗的大小來降低傳送速率。
  • ECE(ECN Echo):用來在 TCP 三次握手時表明一個 TCP 端是具備 ECN 功能的。在資料傳輸過程中,它也用來表明接收到的 TCP 包的 IP 頭部的 ECN 被設定為 11,即網路線路擁堵。
  • URG(Urgent):表示本報文段中傳送的資料是否包含緊急資料。URG=1 時表示有緊急資料。當 URG=1 時,後面的緊急指標欄位才有效。
  • ACK:表示前面的確認號欄位是否有效。ACK=1 時表示有效。只有當 ACK=1 時,前面的確認號欄位才有效。TCP 規定,連線建立後,ACK 必須為 1。
  • PSH(Push):告訴對方收到該報文段後是否立即把資料推送給上層。如果值為 1,表示應當立即把資料提交給上層,而不是快取起來。
  • RST:表示是否重置連線。如果 RST=1,說明 TCP 連線出現了嚴重錯誤(如主機崩潰),必須釋放連線,然後再重新建立連線。
  • SYN:在建立連線時使用,用來同步序號。當 SYN=1,ACK=0 時,表示這是一個請求建立連線的報文段;當 SYN=1,ACK=1 時,表示對方同意建立連線。SYN=1 時,說明這是一個請求建立連線或同意建立連線的報文。只有在前兩次握手中 SYN 才為 1。
  • FIN:標記資料是否傳送完畢。如果 FIN=1,表示資料已經傳送完成,可以釋放連線。

視窗大小欄位

視窗大小(Window Size):佔 16 位。它表示從 Ack Number 開始還可以接收多少位元組的資料量,也表示當前接收端的接收視窗還有多少剩餘空間。該欄位可以用於 TCP 的流量控制。

TCP 校驗和欄位

校驗位(TCP Checksum):佔 16 位。它用於確認傳輸的資料是否有損壞。傳送端基於資料內容校驗生成一個數值,接收端根據接收的資料校驗生成一個值。兩個值必須相同,才能證明資料是有效的。如果兩個值不同,則丟掉這個資料包。Checksum 是根據偽頭 + TCP 頭 + TCP 資料三部分進行計算的。

緊急指標欄位

緊急指標(Urgent Pointer):僅當前面的 URG 控制位為 1 時才有意義。它指出本資料段中為緊急資料的位元組數,佔 16 位。當所有緊急資料處理完後,TCP 就會告訴應用程式恢復到正常操作。即使當前視窗大小為 0,也是可以傳送緊急資料的,因為緊急資料無須快取。

可選項欄位

選項(Option):長度不定,但長度必須是 32bits 的整數倍。

TCP建立連線

TCP建立連線需要三個步驟,也就是大家熟知的三次握手。下圖了正常情形下通過三次握手建立連線的過程:

  • A機器發出一個資料包SYN設定為1,表示希望建立連線。這個包中的假設seq為x
    • 機器A傳送SYN資料包後,會進入SYN_SENT狀態
  • B機器收到A傳送的SYN資料後,響應一個資料包將SYNACK設定為1,假設這個響應包的序列號為y,同時期望下一次收到的資料庫的序列為x+1
    • B回覆響應包後,進入SYN_RECD狀態
  • A收到B的響應包後,對響應包做應答將ACK標誌設定為1,序列號為x + 1,期望下一次收到的資料包的序列號為y+1
    • A機器和B機器連線建立成功

TCP三次握手抓包驗證

以為驗證三次握手是否描述正確,在下使用Wireshark進行抓包驗證。首先使用ping命令獲取www.baidu.com的ip地址:

正在 Ping www.a.shifen.com [183.232.231.174] 具有 32 位元組的資料:
來自 183.232.231.174 的回覆: 位元組=32 時間=16ms TTL=54
來自 183.232.231.174 的回覆: 位元組=32 時間=16ms TTL=54
來自 183.232.231.174 的回覆: 位元組=32 時間=16ms TTL=54

183.232.231.172 的 Ping 統計資訊:
    資料包: 已傳送 = 3,已接收 = 3,丟失 = 0 (0% 丟失),
往返行程的估計時間(以毫秒為單位):
    最短 = 16ms,最長 = 16ms,平均 = 16ms

以上輸出顯示www.baidu.com的ip地址: 183.232.231.174,然後使用Wireshark的過濾器僅顯示與www.baidu.com通訊的tcp資料包:

ip.src_host == "183.232.231.174" or ip.dst_host == "183.232.231.174" and tcp

使用Wireshark抓包分析後,驗證TCP正常連線三次握手與上節描述的一致。

為什麼是三次握手?

為什麼是三次握手?三次握手主要有兩個目的:資訊對等防止超時

資訊對等

兩臺機器通訊時都需要確認四個資訊:

  • 自己發報文的能力
  • 自己收報文的能力
  • 對方發報文的能力
  • 對方收報文的通知
第一次握手

第一次握手A機器向B機器傳送SYN資料包,此時只有B機器能確認自己收報文的能力對方發報文的能力

一次握手完成B機器能夠確認的資訊有:

  • [x] B機器收報文的能力
  • [x] A機器發報文的能力
第二次握手

每二次握手後B響應A機器的SYN資料包,此時A機器就能確認:自己發報文的能力自己收報文的能力對方發報文的能力對方收報文的能力

二次握手完成A機器能夠確認的資訊有:

  • [x] A機器發報文的能力
  • [x] A機器收報文的能力
  • [x] B機器發報文的能力
  • [x] B機器收報文的能力
第三次握手

每三次握手後A應答B機器的SYN + ACK資料包,此時B機器就能確認:自己發報文的能力對方收報文的能力

二次握手完成A機器能夠確認的資訊有:

  • [x] B機器發報文的能力
  • [x] A機器收報文的能力

至此經過三次握手A、B機器就能做到資訊對等,雙方都能確認自己和對方的收、發報文的能力,最後方便理解將資訊對等製作成一個小表格:

防止超時

三次握手除了保證資訊對等也是了防止請求超時導致髒連線。TTL網路報文的生存往往會超過TCP請求超時時間,如果兩次握手就能建立連線,傳輸資料並釋放連線後,第一個超時的連線請求才到達B機器,B機器 會以為是 A 建立新連線的請求,然後確認同意建立連線。因為A機器的狀態不是SYN_SENT,所以會直接丟棄了B的確認資料,導致 B 機器單方面的建立連線完畢。

如果是三次握手,則 B 機器收到連線請求後,同樣會向 A 機器確同意建立連線,但因為 A 不是SYN_SENT狀態,所以 A機器 不會回覆 B 機器確認建立連線請求,而 B 機器到一段時間後由於長時間沒有收到確認資訊,最終會導致連線建立失敗,因此不會出現髒連線。

TCP斷開連線

TCP是全雙工通訊,雙方都能作為資料的傳送方和接收方,但TCP會有斷開的時候。TCP建立連線需要三次握手而斷開連線卻要四次,如圖所示為TCP斷開連線四次揮手過程:

  • A 機器傳送關閉資料包將FIN設定為1,假設序列號為u,發完關閉資料包後此時 A 機器處理FIN_WAIT_1狀態
  • B 收到關閉連線請求後,通知應用程式處理完剩下的資料
  • B 響應 A 的關閉連線請求,將ACK標誌設定為1,seq為v,ack為u+1,隨後 B 機器處於 CLOSE_WAIT狀態
  • A 收到應答後,處於FIN_WAIT_2狀態,繼續等待 B 機器的FIN資料包
  • B 處理好現場後,主動向 A 機器傳送資料包,並將FINACK標誌設定為1,seq為w,ack為u+1,隨後處於LAST_WAIT狀態等待 A 機器的應答
  • A 機器收到FIN資料包後,隨後傳送ACK資料包,seq為u+1,ack為w+1, 此時 A 機器處理TIME_WAIT狀態
  • B 機器收到ACK響應包後,進行CLOSED狀態,連線正常關閉
  • A 機器在TIME_WAIT狀態等待2MSL後,也進入CLOSEED狀態,連線關閉

什麼是2MSL:MSL是Maximum Segment Lifetime英文的縮寫,中文可以譯為“報文最大生存時間”,
2MSL即兩倍的MSL

四次揮手斷開連線可以用更形象的方式來表達:

  • 男生 :我們分手吧。
  • 女生 :好的,我需要去家裡把東西收拾完,再發訊息給你。(此時男生不能再擁抱女生)
  • 。。。,一個小時後
  • 女生 :我收拾完了,分手吧(此時女生也不能再擁抱男生)
  • 男生:好的(此時雙方約定一段時間後,才可以分別找新的物件)

TCP四次揮手抓包驗證

抓包過程與與三次握手抓包過程一致,這裡不描述。直接看訪問後抓包的截圖:

  • 第一個包是由192.168.1.6這臺機器(也就是客戶機),傳送了一個FIN包,seq為80,ack為2782
  • 第二個包由183.232.231.174(伺服器),對192.168.1.6這臺機器(也就是客戶機)傳送了一個ACK包,seq為2782,ack為81
  • 第三個包由183.232.231.174(伺服器),對192.168.1.6這臺機器(也就是客戶機)傳送了一個ACKFIN包,seq為2782,ack為81
  • 第四個包由192.168.1.6,向伺服器響應了一個ACK包,seq為81,ack為2783

四次揮手流程與我們描述的一致。

TIME_WAIT 狀態

主動要求關閉的機器(機器A)表示收到對方的FIN報文後,併傳送出ACK報文後,進行TIME_WAIT狀態,等待2MSL後進行CLOSED狀態。如果在TIME_WAIT_1 時收到FIN標誌和ACK標誌報文時,可以直接進入TIME_WAIT狀態,而無需進入TIME_WAIT_2狀態。

為什麼要有 TIME_WAIT

確認被動關閉(機器B)能夠順利進入CLOSED狀態

假如A機器傳送最後一個ACK後,但由於網路原因ACK包未能到達 B 機器,此時 B機器通常會認為 A機器 沒有收到 FIN+ACK報文,會重發一次FIN+ACK報文。如果 A機器 傳送最後一個ACK後,自私的關閉連線進入 CLOSED狀態,就可能導致 B 無法收到ACK報文,無法正常關閉。

防止失效請求

TIME_WAIT 狀態可以防止已失效的請求包與正常連線的請求資料包混淆而發生異常。因為TIME_WAIT 狀態無法真正釋放控制程式碼資源,在此期間, Socket中使用的本地埠在預設情況下不能再被使用。

CLOSE_WAIT 狀態

被動關閉的機器(機器B)在收到對方傳送的,FIN報文後,馬上回復ACK報文,進入CLOSE_WAIT狀態。通知應用程式,處理剩下的資料,釋放資源。

歡迎關注我的公眾號:架構文摘,獲得獨家整理120G的免費學習資源助力你的架構師學習之路!

公眾號後臺回覆arch028獲取資料:

相關文章