1. 程式人生 > 實用技巧 >TCP之“3次握手,4次揮手”問題

TCP之“3次握手,4次揮手”問題

你知道“3次握手,4次揮手”嗎?

當面試官問你什麼是“3次握手,4次揮手”,你是不是要開啟“誦經”模式了?
作為程式設計師,要有“刨根問底”的精神。近期在複習網路時,遇到了這個頻繁被面試官q到的問題,今天特此總結一下底層原理:


文章整體目錄如下:

一、什麼是“3次握手,4次揮手”

TCP是一種面向連線的單播協議,在傳送資料前,通訊雙方必須在彼此間建立一條連線。所謂的“連線”,其實是客戶端和伺服器的記憶體裡儲存的一份關於對方的資訊,如ip地址、埠號等。

TCP可以看成是一種位元組流,它會處理IP層或以下的層的丟包、重複以及錯誤問題。在連線的建立過程中,雙方需要交換一些連線的引數。這些引數可以放在TCP頭部。

TCP提供了一種可靠的、面向連線、位元組流、傳輸層的服務,採用三次握手建立一個連線,採用4次揮手來關閉一個連線。

  • TCP服務模型

在瞭解了建立連線、關閉連線的“三次握手和四次揮手”後,我們再來看下TCP相關的東西。

一個TCP連線由一個4元組構成,分別是兩個IP地址兩個埠號

一個TCP連線通常分為個階段:啟動、資料傳輸、退出(關閉)

在講解之前,先了解一下相關的概念:

  • 序號 :用於對位元組流進行編號,例如序號為 301,表示第一個位元組的編號為 301,如果攜帶的資料長度為 100位元組,那麼下一個報文段的序號應為 401。
  • 確認號 :期望收到的下一個報文段的序號。例如 B 正確收到 A 傳送來的一個報文段,序號為 501,攜帶的資料長度為 200 位元組,因此 B 期望下一個報文段的序號為 701,B 傳送給 A 的確認報文段中確認號就為 701。
  • 資料偏移 :指的是資料部分距離報文段起始處的偏移量,實際上指的是首部的長度。
  • 確認 ACK :當 ACK=1 時確認號欄位有效,否則無效。TCP 規定,在連線建立後所有傳送的報文段都必須把ACK 置 1。
  • 同步 SYN 在連線建立時用來同步序號。當 SYN=1,ACK=0 時表示這是一個連線請求報文段。若對方同意建立連線,則響應報文中 SYN=1,ACK=1。
  • 終止 FIN :用來釋放一個連線,當 FIN=1 時,表示此報文段的傳送方的資料已傳送完畢,並要求釋放連線。
  • 視窗 :視窗值作為接收方讓傳送方設定其傳送視窗的依據。之所以要有這個限制,是因為接收方的資料快取空間是有限的。

當TCP接收到另一端的資料時,它會發送一個確認,但這個確認不會立即傳送,一般會延遲一會兒。ACK是累積的,一個確認位元組號N的ACK表示所有直到N的位元組(不包括N)已經成功被接收了。這樣的好處是如果一個ACK丟失,很可能後續的ACK就足以確認前面的報文段了。

一個完整的TCP連線是雙向和對稱的,資料可以在兩個方向上平等地流動。給上層應用程式提供一種雙工服務。一旦建立了一個連線,這個連線的一個方向上的每個TCP報文段都包含了相反方向上的報文段的一個ACK。

序列號的作用:是使得一個TCP接收端可丟棄重複的報文段,記錄以雜亂次序到達的報文段。因為TCP使用IP來傳輸報文段,而IP不提供重複消除或者保證次序正確的功能。另一方面,TCP是一個位元組流協議,絕不會以雜亂的次序給上層程式傳送資料。因此TCP接收端會被迫先保持大序列號的資料不交給應用程式,直到缺失的小序列號的報文段被填滿。

  • TCP頭部

當新建一個連線時,從客戶端傳送到服務端的第一個報文段的SYN位被啟用,這稱為SYN報文段,這時序列號欄位包含了在本次連線的這個方向上要使用的第一個序列號,即初始序列號ISN,之後傳送的資料是ISN加1,因此SYN位欄位會消耗一個序列號,這意味著使用重傳進行可靠傳輸。而不消耗序列號的ACK則不是。

頭部長度(圖中的資料偏移)以32位字為單位,也就是以4bytes為單位,它只有4位,最大為15,因此頭部最大長度為60位元組,而其最小為5,也就是頭部最小為20位元組(可變選項為空)。

當一個連線被建立或被終止時,交換的報文段只包含TCP頭部,而沒有資料。

  • 狀態轉換

三次握手:

假設 A 為客戶端,B 為伺服器端。

握手過程如下:

  1. 首先 B 處於 LISTEN(監聽)狀態,等待客戶的連線請求。
  2. A 向 B 傳送連線請求報文,SYN=1,ACK=0,選擇一個初始的序號 x。
  3. B 收到連線請求報文,如果同意建立連線,則向 A 傳送連線確認報文,SYN=1,ACK=1,確認號為 x+1,同時也選擇一個初始的序號 y。
  4. A 收到 B 的連線確認報文後,還要向 B 發出確認,確認號為 y+1,序號為 x+1。
  5. B 收到 A 的確認後,連線建立。

如果分三步來講的話:

就是

  1. 客戶端傳送一個SYN段,並指明客戶端的初始序列號,即ISN(c).
  2. 服務端傳送自己的SYN段作為應答,同樣指明自己的ISN(s)。為了確認客戶端的SYN,將ISN(c)+1作為ACK數值。這樣,每傳送一個SYN,序列號就會加1. 如果有丟失的情況,則會重傳。
  3. 為了確認伺服器端的SYN,客戶端將ISN(s)+1作為返回的ACK數值。