TCP:三次握手和四次揮手,面試無死角答覆

帶你聊技術發表於2023-10-31

來源:碼農本農
TCP:三次握手和四次揮手,面試無死角答覆

什麼是TCP

在瞭解三次握手四次揮手前必須先了解什麼是tcp。

TCP是面向連線的,可靠的,基於位元組流的傳輸層協議。

連線

所謂連線其實是保證可靠性和流量控制的狀態資訊的總和,包括sokict,滑動視窗和序列號。

可靠性

tcp透過序列號,重傳機制,滑動視窗等一系列控制機制保證資料的無重複,無丟失,有序的被接受端處理。

位元組流

tcp的資料是基於位元組流,因此是無邊界,資料是可以無限大的,tcp可以透過分片機制將資料有序傳送到接收端。

TCP結構

TCP的頭部在無“選項”欄位的情況下是20個位元組。包括:

  • 2位元組的源埠
  • 2位元組的目標埠
  • 4位元組序列號
  • 4位元組確認序列號
  • 4位的首部長度
  • 6位保留欄位
  • 6位標誌位(SYN,ACK,RST,FIN,URG,PSH)
  • 2位元組視窗大小
  • 2位元組校驗和
  • 2位元組緊急指標

這裡需要說明的是“選項”這個欄位是用來輔助解決可靠性問題的,正是因為這個欄位的長度是不確定的,所以需要“首部長度”這個欄位來表示TCP頭部的長度。

TCP三次握手過程


TCP:三次握手和四次揮手,面試無死角答覆


什麼是三次握手

TCP是基於連線的,所以TCP在使用前必須先建立連線,TCP建立連線的過程是基於三次握手的。

  1. 首先服務端的應用程式監聽某個埠,也就是建立一個listened狀態的Socket,服務端處於listen狀態。

  2. 當客戶端建立一個Socket,並呼叫connect函式連線服務端的時候,會向服務端傳送一個SYN狀態為1的tcp報文,並攜帶自己的隨機序列號。客戶端處於syn_send狀態。

  3. 服務端接收到SYN報文後,會建立一個連線放入當前Socket的半連線佇列,然後回覆ACK+SYN報文並攜帶自己的隨機序列號和確認序列號(客戶端序列號+1)。服務端處於syn_recv狀態。

  4. 客戶端接受到服務端的ack後,經過一定處理,會給服務端回覆一個ack報文,並攜帶確認序列號(服務端序列號+1)。此時客戶端處於establisten狀態。

  5. 服務端收到ack報文後,服務端會把半連線佇列中的連線放入全連線佇列。然後處於establisten狀態。

至此,tcp連線建立完成,注意第三次握手是可以傳輸資料的。在這之前不能傳輸資料。

為什麼是三次握手

一般大家都會認為三次握手是為了保證客戶端和服務端雙方都能確認自身和接收端建立單向連線和保證自身能夠傳送和接受成功資料。

這樣答本身也沒有錯,但是太粗化了。

既然握手是為保證連線的建立,那就要先知道什麼是TCP連線。

TCP連線是保證可靠性和流量控制的狀態資訊的總和,包括socket,序列號,滑動視窗。

在這裡這個序列號至關重要,是保證訊息無重複,無丟失,有序的關鍵,因此這裡其實就是為了保證序列號的同步。

客戶端給服務端傳送一個初始序列號,服務端回覆syn+ack,就是告訴客戶端序列號已經收到了並且把服務端的初始序列號傳送給客戶端,客戶端收到後也要回復給服務端表示序列號已經收到,這樣就能保證雙方都能確保序列號同步。

但是這還不是最重要的原因,最重要的原因是防止歷史連線初始化再次連線。比如有這樣一種情況,客戶端傳送syn包給服務端,但是網路阻塞了,服務端沒有收到,所以服務端也不會回覆,客戶端收不到回覆就會重新傳送syn包,但是就在這時候服務端接收到了第一個syn包,並且回覆客戶端,這個時候客戶端會進行比對校驗這是不是自己最新傳送的syn回覆包,如果不是的話就會給服務端傳送rst包,表示要求服務端中斷這個連線。這也是三次握手的意義所在。

如果說沒有第三次握手,那麼在發生上面的這個情況後,服務端就會為每個syn請求建立連線,連線是需要佔用記憶體的,就會耗費很多的資源。造成資源浪費,所以三次握手很有必要。

那麼四次握手是否可以呢?

四次握手的話也是可以的,四次握手其實就是客戶端傳送syn包給服務端,服務端回覆ack包,服務端傳送syn包給客戶端,客戶端回覆ack包,三次握手中的第二次握手回覆的是syn+ack包,所有相當於合併了四次握手中的中間兩次,所以三次握手最好。


TCP:三次握手和四次揮手,面試無死角答覆


四次揮手的過程

TCP是雙向連線,所以兩個方向上的連線都要斷開。

  1. 斷開前客戶端和服務端都處於ESTABLISTENED狀態。
  2. 客戶端呼叫close方法盡心斷開連線操作,客戶端會傳送fin包給服務端。客戶端處於fin_wait1狀態
  3. 服務端接收到fin包後,會回覆一個ack。此時服務端處於closed_wait狀態。
  4. 客戶端收到服務端的ack後,表示已經斷開了自己到服務端的連線,但是服務端到客戶端的連線還沒有斷開,客戶端需要等待服務端主動請求斷開。此時客戶端處於fin_wait2狀態。
  5. 服務端之所以不會立刻給客戶端傳送fin包是因為服務端可能還存在要傳送的資料,所以服務端需要把要處理的資料處理完在傳送fin包給客戶端,此時資料已經處理完,服務端主動給客戶端傳送fin包,此時服務端處於last_ack狀態。
  6. 客戶端收到fin包後,會回覆ack給服務端,此時客戶端處於time_wait狀態。
  7. 服務端收到ack後將狀態置為close.
  8. 客戶端此時並不會直接進入close狀態,而是會進入time_wait狀態, 這個狀態會持續2MSL時間。

在網路傳輸的世界裡,有兩個值是用來表示資料包失效的:

  • MSL是報文在網路中的最大存活時間,超過這個時間就會被丟棄。

  • TTL:在ip層的頭部中有一個TTL欄位儲存所經過的路由數,沒經過一個路由數就會減1,當為0的時候,資料就會被丟棄。

所以一般情況下MSL會大於TTL減為0所消耗的時間。

這裡為什麼是2倍的MSL呢?

因為當客戶端接收到服務端的fin包後,會向服務端回覆ack,但是客戶端不知道這個ack是否傳送成功了,所以客戶端需要確認服務端接受成功後才能置為close狀態,怎麼確認呢,因為失敗重傳機制的存在,如果因為網路阻塞服務端沒有收到ack,服務端會再次傳送一次fin,一次ack包和再一次fin包就是2倍的MSL。MSL的計時是從收到fin包並且傳送ack包開始的。

除了上面說的保證客戶端的ack傳送到服務端,並被正確接收,從而保證被關閉連線的一方可以正確關閉。

還能保證那些阻塞在網路中舊的連線,在埠又被複用的情況下,被接收到,這樣就會發生資料錯亂,而time_wait可以保證全部的網路中的連線被丟棄。

MSL預設是30秒。需要注意的是time_wait 的狀態多了以後會佔用記憶體資源和埠資源,所以不宜太多。

為什麼要進行四次揮手?

tcp是雙向連線,客戶端傳送fin包給服務端,服務端回覆ack,只是客戶端告訴服務端不再向服務端傳送資料。

還需要服務端告訴客戶端,服務端不再向客戶端傳送資料了,也就是服務端也要想客戶端傳送fin包,客戶端也要給服務端回覆ack包,這時候服務端和客戶端才能進入close狀態。

服務端在收到客戶端傳送的fin包並回復ack包後,服務端並不能馬上向傳送端傳送fin包,因為此時可能還有連線在處理資料,必須等到資料處理完後才能向客戶端傳送fin包。

正因為這個原因,不能像三次握手那樣把中間兩次合併。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024922/viewspace-2991980/,如需轉載,請註明出處,否則將追究法律責任。

相關文章