TCP 三次握手和四次揮手的面試題

一、TCP頭格式

在這裏插入圖片描述
序列號:在建立連接時由計算機生成的隨機數作爲其初始值,通過 SYN 包傳給接收端主機,每發送一次數據,就「累加」一次該「數據字節數」的大小。用來解決網絡包亂序問題。

確認應答號:指下一次「期望」收到的數據的序列號,發送端收到這個確認應答以後可以認爲在這個序號以前的數據都已經被正常接收。用來解決不丟包的問題。

控制位

  • ACK:該位爲 1 時,「確認應答」的字段變爲有效,TCP 規定除了最初建立連接時的 SYN 包之外該位必須設置爲 1
  • RST:該位爲 1 時,表示 TCP 連接中出現異常必須強制斷開連接。
  • SYN:該位爲 1 時,表示希望建立連接,並在其「序列號」的字段進行序列號初始值的設定
  • FIN:該位爲 1 時,表示今後不會再有數據發送,希望斷開連接。當通信結束希望斷開連接時,通信雙方的主機之間就可以相互交換 FIN 位置爲 1 的 TCP 段。

二、爲什麼需要 TCP 協議? TCP 工作在哪一層?

IP 層是「不可靠」的,它不保證網絡包的交付不保證網絡包的按序交付、也不保證網絡包中的數據的完整性
在這裏插入圖片描述
如果需要保障網絡數據包的可靠性,那麼就需要由上層(傳輸層)的 TCP 協議來負責

因爲 TCP 是一個工作在傳輸層的可靠數據傳輸的服務,它能確保接收端接收的網絡包是無損壞、無間隔、非冗餘和按序的。


三、什麼是 TCP ?

TCP 是面向連接的、可靠的、基於字節流傳輸層通信協議。
在這裏插入圖片描述

  • 面向連接:一定是「一對一」才能連接,不能像 UDP 協議 可以一個主機同時向多個主機發送消息,也就是一對多是無法做到的;
  • 可靠的:無論的網絡鏈路中出現了怎樣的鏈路變化,TCP 都可以保證一個報文一定能夠到達接收端;
  • 字節流消息是「沒有邊界」的,所以無論我們消息有多大都可以進行傳輸。並且消息是「有序的」,當「前一個」消息沒有收到的時候,即使它先收到了後面的字節已經收到,那麼也不能扔給應用層去處理,同時對「重複」的報文會自動丟棄

四、什麼是 TCP 連接?

用於保證可靠性和流量控制維護的某些狀態信息,這些信息的組合,包括Socket、序列號和窗口大小稱爲連接

在這裏插入圖片描述
所以我們可以知道,建立一個 TCP 連接是需要客戶端與服務器端達成上述三個信息的共識

Socket:由 IP 地址和端口號組成
序列號:用來解決亂序問題等
窗口大小:用來做流量控制


五、如何唯一確定一個 TCP 連接呢?

TCP 四元組可以唯一的確定一個連接,四元組包括如下:

源地址
源端口
目的地址
目的端口

在這裏插入圖片描述
**源地址和目的地址的字段(32位)**是在 IP 頭部中,作用是通過 IP 協議發送報文給對方主機。

源端口和目的端口的字段(16位)是在 TCP 頭部中,作用是告訴 TCP 協議應該把報文發給哪個進程。


六、有一個 IP 的服務器監聽了一個端口,它的 TCP 的最大連接數是多少?

服務器通常固定在某個本地端口上監聽,等待客戶端的連接請求。

因此,客戶端 IP 和 端口是可變的,其理論值計算公式如下:
在這裏插入圖片描述
IPv4客戶端的 IP 數最多爲 2 的 32 次方客戶端的端口數最多爲 2 的 16 次方,也就是服務端單機最大 TCP 連接數,約爲 2 的 48 次方

當然,服務端最大併發 TCP 連接數遠不能達到理論上限

  • 首先主要是文件描述符限制Socket 都是文件,所以首先要通過 ulimit 配置文件描述符的數目;
  • 另一個是內存限制,每個 TCP 連接都要佔用一定內存,操作系統是有限的。

七、UDP 和 TCP 有什麼區別呢?分別的應用場景是?

UDP 不提供複雜的控制機制,利用 IP 提供面向「無連接」的通信服務

UDP 協議真的非常簡單,頭部只有 8 個字節( 64 位),UDP 的頭部格式如下:
在這裏插入圖片描述

目標和源端口:主要是告訴 UDP 協議應該把報文發給哪個進程。
包長度:該字段保存了 UDP 首部的長度跟數據的長度之和。
校驗和:校驗和是爲了提供可靠的 UDP 首部和數據而設計。


八、TCP 和 UDP 區別:

  1. 連接

TCP 是面向連接的傳輸層協議,傳輸數據前先要建立連接。
UDP 是不需要連接即刻傳輸數據。

  1. 服務對象

TCP 是一對一的兩點服務,即一條連接只有兩個端點。
UDP 支持一對一、一對多、多對多的交互通信

  1. 可靠性

TCP 是可靠交付數據的,數據可以無差錯、不丟失、不重複、按序到達
UDP 是盡最大努力交付,不保證可靠交付數據

  1. 擁塞控制、流量控制

TCP 有擁塞控制和流量控制機制,保證數據傳輸的安全性。
UDP 則沒有,即使網絡非常擁堵了,也不會影響 UDP 的發送速率。

  1. 首部開銷

TCP 首部長度較長,會有一定的開銷,首部在沒有使用「選項」字段時是 20 個字節,如果使用了「選項」字段則會變長的。
UDP 首部只有 8 個字節,並且是固定不變的,開銷較小。

九、TCP 和 UDP 應用場景:

由於 TCP 是面向連接,能保證數據的可靠性交付,因此經常用於:

FTP 文件傳輸
HTTP / HTTPS

由於 UDP 面向無連接,它可以隨時發送數據,再加上UDP本身的處理既簡單又高效,因此經常用於:

包總量較少的通信,如 DNS 、SNMP 等
視頻、音頻等多媒體通信
廣播通信


十、關於UDP和TCP首部的對比?

爲什麼 UDP 頭部沒有「首部長度」字段,而 TCP 頭部有「首部長度」字段呢?

原因是 TCP 有可變長的「選項」字段,而 UDP 頭部長度則是不會變化的,無需多一個字段去記錄 UDP 的首部長度。

爲什麼 UDP 頭部有「包長度」字段,而 TCP 頭部則沒有「包長度」字段呢?

先說說 TCP 是如何計算負載數據長度:
在這裏插入圖片描述
其中 IP 總長度 和 IP 首部長度,在 IP 首部格式是已知的。TCP 首部長度,則是在 TCP 首部格式已知的,所以就可以求得 TCP 數據的長度。

大家這時就奇怪了問:“ UDP 也是基於 IP 層的呀,那 UDP 的數據長度也可以通過這個公式計算呀? 爲何還要有「包長度」呢?”

這麼一問,確實感覺 UDP 「包長度」是冗餘的。

因爲爲了網絡設備硬件設計和處理方便首部長度需要是 4字節的整數倍

如果去掉 UDP 「包長度」字段,那 UDP 首部長度就不是 4 字節的整數倍了,所以小林覺得這可能是爲了補全 UDP 首部長度是 4 字節的整數倍,才補充了「包長度」字段


十一、TCP 三次握手過程和狀態變遷

TCP 是面向連接的協議,所以使用 TCP 前必須先建立連接,而建立連接是通過三次握手而進行的。
在這裏插入圖片描述

  • 一開始,客戶端和服務端都處於 CLOSED 狀態。先是服務端主動監聽某個端口,處於 LISTEN 狀態
    在這裏插入圖片描述
  • 客戶端會隨機初始化序號(client_isn),將此序號置於 TCP 首部的「序號」字段中,同時把 SYN 標誌位置爲 1 ,表示 SYN 報文。接着把第一個 SYN 報文發送給服務端,表示向服務端發起連接,該報文不包含應用層數據,之後客戶端處於 SYN-SENT 狀態。
    在這裏插入圖片描述
  • 服務端收到客戶端的 SYN 報文後,首先服務端也隨機初始化自己的序號(server_isn),將此序號填入 TCP 首部的「序號」字段中,其次把 TCP 首部的**「確認應答號」字段填入 client_isn + 1,** 接着把 SYN 和 ACK 標誌位置爲 1。最後把該報文發給客戶端,該報文也不包含應用層數據,之後服務端處於 SYN-RCVD 狀態。

在這裏插入圖片描述

  • 客戶端收到服務端報文後,還要向服務端迴應最後一個應答報文,首先該應答報文 TCP 首部 ACK 標誌位置爲 1 ,其次「確認應答號」字段填入 server_isn + 1 ,最後把報文發送給服務端,這次報文可以攜帶客戶到服務器的數據,之後客戶端處於 ESTABLISHED 狀態。

  • 服務器收到客戶端的應答報文後,也進入 ESTABLISHED 狀態。

從上面的過程可以發現第三次握手是可以攜帶數據的,前兩次握手是不可以攜帶數據的,這也是面試常問的題。

一旦完成三次握手,雙方都處於 ESTABLISHED 狀態,此致連接就已建立完成,客戶端和服務端就可以相互發送數據了。


十二、如何在 Linux 系統中查看 TCP 狀態?

TCP 的連接狀態查看,在 Linux 可以通過 netstat -napt 命令查看。
在這裏插入圖片描述


十三、爲什麼是三次握手?不是兩次、四次?

在前面我們知道了什麼是 TCP 連接:

用於保證可靠性和流量控制維護的某些狀態信息,這些信息的組合,包括Socket、序列號和窗口大小稱爲連接

所以,重要的是爲什麼三次握手纔可以初始化Socket、序列號和窗口大小並建立 TCP 連接

接下來以三個方面分析三次握手的原因:

三次握手纔可以阻止重複歷史連接的初始化(主要原因)
三次握手纔可以同步雙方的初始序列號
三次握手纔可以避免資源浪費

原因一:避免歷史連接

我們來看看 RFC 793 指出的 TCP 連接使用三次握手的首要原因:

The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.

簡單來說,三次握手的首要原因是爲了防止舊的重複連接初始化造成混亂

網絡環境是錯綜複雜的,往往並不是如我們期望的一樣,先發送的數據包,就先到達目標主機,反而它很騷,可能會由於網絡擁堵等亂七八糟的原因,會使得舊的數據包,先到達目標主機,那麼這種情況下 TCP 三次握手是如何避免的呢?

在這裏插入圖片描述
客戶端連續發送多次 SYN 建立連接的報文,在網絡擁堵等情況下:

  • 一個「舊 SYN 報文」比「最新的 SYN 」 報文早到達了服務端;
  • 那麼此時服務端就會回一個 SYN + ACK 報文給客戶端;
  • 客戶端收到後可以根據自身的上下文,判斷這是一個歷史連接(序列號過期或超時),那麼客戶端就會發送 RST 報文給服務端,表示中止這一次連接。

如果是兩次握手連接,就不能判斷當前連接是否是歷史連接三次握手則可以在客戶端(發送方)準備發送第三次報文時客戶端因有足夠的上下文來判斷當前連接是否是歷史連接

  • 如果是歷史連接(序列號過期或超時),則第三次握手發送的報文是 RST 報文,以此中止歷史連接;
  • 如果不是歷史連接,則第三次發送的報文是 ACK 報文,通信雙方就會成功建立連接;

所以, TCP 使用三次握手建立連接的最主要原因是防止歷史連接初始化了連接。
原因二:同步雙方初始序列號

TCP 協議的通信雙方, 都必須維護一個「序列號」, 序列號是可靠傳輸的一個關鍵因素,它的作用:

  • 接收方可以去除重複的數據
  • 接收方可以根據數據包的序列號按序接收
  • 可以標識發送出去的數據包中, 哪些是已經被對方收到的;

可見,序列號在 TCP 連接中佔據着非常重要的作用,所以當客戶端發送攜帶「初始序列號」的 SYN 報文的時候,需要服務端回一個 ACK 應答報文,表示客戶端的 SYN 報文已被服務端成功接收,那當服務端發送「初始序列號」給客戶端的時候,依然也要得到客戶端的應答迴應,這樣一來一回,才能確保雙方的初始序列號能被可靠的同步

在這裏插入圖片描述
四次握手其實也能夠可靠的同步雙方的初始化序號,但由於第二步和第三步可以優化成一步,所以就成了「三次握手」

兩次握手只保證了一方的初始序列號能被對方成功接收,沒辦法保證雙方的初始序列號都能被確認接收。

原因三:避免資源浪費

如果只有「兩次握手」,當客戶端的 SYN 請求連接在網絡中阻塞,客戶端沒有接收到 ACK 報文,就會重新發送 SYN ,由於沒有第三次握手,服務器不清楚客戶端是否收到了自己發送的建立連接的 ACK 確認信號,所以每收到一個 SYN 就只能先主動建立一個連接,這會造成什麼情況呢?

如果客戶端的 SYN 阻塞了,重複發送多次 SYN 報文,那麼服務器在收到請求後就會建立多個冗餘的無效鏈接,造成不必要的資源浪費。
在這裏插入圖片描述
即兩次握手會造成消息滯留情況下,服務器重複接受無用的連接請求 SYN 報文,而造成重複分配資源。

小結:
TCP 建立連接時,通過三次握手能防止歷史連接的建立,能減少雙方不必要的資源開銷,能幫助雙方同步初始化序列號序列號能夠保證數據包不重複、不丟棄和按序傳輸。

不使用「兩次握手」和「四次握手」的原因:
「兩次握手」:無法防止歷史連接的建立,會造成雙方資源的浪費,也無法可靠的同步雙方序列號
「四次握手」:三次握手就已經理論上最少可靠連接建立,所以不需要使用更多的通信次數。


十四、爲什麼客戶端和服務端的初始序列號 ISN 是不相同的?

因爲網絡中的報文會延遲、會複製重發、也有可能丟失,這樣會造成的不同連接之間產生互相影響,所以爲了避免互相影響,客戶端和服務端的初始序列號是隨機且不同的


十五、初始序列號 ISN 是如何隨機產生的?

起始 ISN 是基於時鐘的,每 4 毫秒 + 1,轉一圈要 4.55 個小時。

RFC1948 中提出了一個較好的初始化序列號 ISN 隨機生成算法。

ISN = M + F (localhost, localport, remotehost, remoteport)

M 是一個計時器,這個計時器每隔 4 毫秒加 1。
F 是一個 Hash 算法,根據源 IP、目的 IP、源端口、目的端口生成一個隨機數值。要保證 Hash 算法不能被外部輕易推算得出,用 MD5 算法是一個比較好的選擇。


十六、既然 IP 層會分片,爲什麼 TCP 層還需要 MSS 呢?

在這裏插入圖片描述

MTU:一個網絡包的最大長度,以太網中一般爲 1500 字節;
MSS除去 IP 和 TCP 頭部之後一個網絡包所能容納的 TCP 數據的最大長度;

如果在 TCP 的整個報文(頭部 + 數據)交給 IP 層進行分片,會有什麼異常呢?

當 IP 層有一個超過 MTU 大小的數據(TCP 頭部 + TCP 數據)要發送,那麼 IP 層就要進行分片,把數據分片成若干片,保證每一個分片都小於 MTU。把一份 IP 數據報進行分片以後,由目標主機的 IP 層來進行重新組裝後,在交給上一層 TCP 傳輸層。

這看起來井然有序,但這存在隱患的,那麼當如果一個 IP 分片丟失,整個 IP 報文的所有分片都得重傳

因爲 IP 層本身沒有超時重傳機制,它由傳輸層的 TCP 來負責超時和重傳。

當接收方發現 TCP 報文(頭部 + 數據)的某一片丟失後,則不會響應 ACK 給對方,那麼發送方的 TCP 在超時後,就會重發「整個 TCP 報文(頭部 + 數據)」。

因此,可以得知由 IP 層進行分片傳輸,是非常沒有效率的。

所以,爲了達到最佳的傳輸效能 TCP 協議在建立連接的時候通常要協商雙方的 MSS 值,當 TCP 層發現數據超過 MSS 時,則就先會進行分片,當然由它形成的 IP 包的長度也就不會大於 MTU ,自然也就不用 IP 分片了。

在這裏插入圖片描述
經過 TCP 層分片後,如果一個 TCP 分片丟失後,進行重發時也是以 MSS 爲單位,而不用重傳所有的分片,大大增加了重傳的效率。


十七、什麼是 SYN 攻擊?如何避免 SYN 攻擊?

SYN 攻擊
我們都知道 TCP 連接建立是需要三次握手,假設攻擊者短時間僞造不同 IP 地址的 SYN 報文服務端每接收到一個 SYN 報文,就進入SYN_RCVD 狀態,但服務端發送出去的 ACK + SYN 報文,無法得到未知 IP 主機的 ACK 應答,久而久之就會佔滿服務端的 SYN 接收隊列(未連接隊列),使得服務器不能爲正常用戶服務。

在這裏插入圖片描述

  1. 避免 SYN 攻擊方式一:
    其中一種解決方式是通過修改 Linux 內核參數控制隊列大小和當隊列滿時應做什麼處理
  • 當網卡接收數據包的速度大於內核處理的速度時,會有一個隊列保存這些數據包。控制該隊列的最大值如下參數:
    net.core.netdev_max_backlog
  • SYN_RCVD 狀態連接的最大個數:
    net.ipv4.tcp_max_syn_backlog
  • 超出處理能時,對新的 SYN 直接回報 RST,丟棄連接:
    net.ipv4.tcp_abort_on_overflow
  1. 避免 SYN 攻擊方式二:
    先來看下Linux 內核的 SYN (未完成連接建立)隊列與 Accpet (已完成連接建立)隊列是如何工作的?
    在這裏插入圖片描述
    正常流程:
  • 當服務端接收到客戶端的 SYN 報文時,會將其加入到內核的「 SYN 隊列」;
  • 接着發送 SYN + ACK 給客戶端,等待客戶端迴應 ACK 報文;
  • 服務端接收到 ACK 報文後,從「 SYN 隊列」移除放入到「 Accept 隊列」;
  • 應用通過調用 accpet() socket 接口,從「 Accept 隊列」取出的連接。

在這裏插入圖片描述

應用程序過慢:如果應用程序過慢時,就會導致「 Accept 隊列」被佔滿

在這裏插入圖片描述

受到 SYN 攻擊:如果不斷受到 SYN 攻擊,就會導致「 SYN 隊列」被佔滿

tcp_syncookies 的方式可以應對 SYN 攻擊的方法:
net.ipv4.tcp_syncookies = 1

在這裏插入圖片描述

  • 當 「 SYN 隊列」滿之後,後續服務器收到 SYN 包,不進入「 SYN 隊列」
  • 計算出一個 cookie 值,再以 SYN + ACK 中的「序列號」返回客戶端
  • 服務端接收到客戶端的應答報文時,服務器會檢查這個 ACK 包的合法性。如果合法,直接放入到「 Accept 隊列」。
  • 最後應用通過調用 accpet() socket 接口,從「 Accept 隊列」取出的連接。

十八、TCP 四次揮手過程和狀態變遷

雙方都可以主動斷開連接,斷開連接後主機中的「資源」將被釋放。
在這裏插入圖片描述

  • 客戶端打算關閉連接,此時會發送一個 TCP 首部 FIN 標誌位被置爲 1 的報文,也即 FIN 報文,之後客戶端進入 FIN_WAIT_1 狀態。
  • 服務端收到該報文後,就向客戶端發送 ACK 應答報文,接着服務端進入 CLOSED_WAIT 狀態。
  • 客戶端收到服務端的 ACK 應答報文後,之後進入 FIN_WAIT_2 狀態。
  • 等待服務端處理完數據後,也向客戶端發送 FIN 報文,之後服務端進入 LAST_ACK 狀態。
  • 客戶端收到服務端的 FIN 報文後,回一個 ACK 應答報文,之後進入 TIME_WAIT 狀態
  • 服務器收到了 ACK 應答報文後,就進入了 CLOSE 狀態,至此服務端已經完成連接的關閉。
  • 客戶端在經過 2MSL 一段時間後,自動進入 CLOSE 狀態,至此客戶端也完成連接的關閉。

你可以看到,每個方向都需要一個 FIN 和一個 ACK,因此通常被稱爲四次揮手。

這裏一點需要注意是**:主動關閉連接的,纔有 TIME_WAIT 狀態**。


十九、爲什麼揮手需要四次?

再來回顧下四次揮手雙方發 FIN 包的過程,就能理解爲什麼需要四次了。

  • 關閉連接時,客戶端向服務端發送 FIN 時,僅僅表示客戶端不再發送數據了但是還能接收數據。
  • 服務器收到客戶端的 FIN 報文時,先回一個 ACK 應答報文,而服務端可能還有數據需要處理和發送,等服務端不再發送數據時,才發送 FIN 報文給客戶端來表示同意現在關閉連接。

從上面過程可知,服務端通常需要等待完成數據的發送和處理,所以服務端的 ACK 和 FIN 一般都會分開發送,從而比三次握手導致多了一次。


二十、爲什麼 TIME_WAIT 等待的時間是 2MSL?

在這裏插入圖片描述


二十一、爲什麼需要 TIME_WAIT 狀態?

主動發起關閉連接的一方,纔會有 TIME-WAIT 狀態。

需要 TIME-WAIT 狀態,主要是兩個原因:

  • 防止具有相同「四元組」的「舊」數據包被收到
  • 保證「被動關閉連接」的一方能被正確的關閉,即保證最後的 ACK 能讓被動關閉方接收,從而幫助其正常關閉;

原因一:防止舊連接的數據包

假設 TIME-WAIT 沒有等待時間或時間過短,被延遲的數據包抵達後會發生什麼呢?

在這裏插入圖片描述

  • 如上圖黃色框框服務端在關閉連接之前發送的 SEQ = 301 報文,被網絡延遲了。
  • 這時有相同端口的 TCP 連接被複用後,被延遲的 SEQ = 301 抵達了客戶端,那麼客戶端是有可能正常接收這個過期的報文,這就會產生數據錯亂等嚴重的問題。

所以,TCP 就設計出了這麼一個機制,經過 2MSL 這個時間,足以讓兩個方向上的數據包都被丟棄,使得原來連接的數據包在網絡中都自然消失,再出現的數據包一定都是新建立連接所產生的

原因二:保證連接正確關閉
TIME-WAIT 作用是等待足夠的時間以確保最後的 ACK 能讓被動關閉方接收,從而幫助其正常關閉。

假設 TIME-WAIT 沒有等待時間或時間過短,斷開連接會造成什麼問題呢?
在這裏插入圖片描述

  • 如上圖紅色框框客戶端四次揮手的最後一個 ACK 報文如果在網絡中被丟失了,此時如果客戶端 TIME-WAIT 過短或沒有,則就直接進入了 CLOSE 狀態了,那麼服務端則會一直處在 LASE-ACK 狀態。
  • 當客戶端發起建立連接的 SYN 請求報文後,服務端會發送 RST 報文給客戶端,連接建立的過程就會被終止。

如果 TIME-WAIT 等待足夠長的情況就會遇到兩種情況:

  • 服務端正常收到四次揮手的最後一個 ACK 報文,則服務端正常關閉連接。
  • 服務端沒有收到四次揮手的最後一個 ACK 報文時,則會重發 FIN 關閉連接報文並等待新的 ACK 報文。

所以客戶端在 TIME-WAIT 狀態等待 2MSL 時間後,就可以保證雙方的連接都可以正常的關閉。


二十二、如果已經建立了連接,但是客戶端突然出現故障了怎麼辦?

在這裏插入圖片描述
在這裏插入圖片描述


二十三、針對 TCP 應該如何 Socket 編程?

在這裏插入圖片描述

在這裏插入圖片描述


二十四、listen 時候參數 backlog 的意義?

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述


二十五、accept 發送在三次握手的哪一步?

先看看客戶端連接服務端時,發送了什麼?
在這裏插入圖片描述
在這裏插入圖片描述


二十六、客戶端調用 close 了,連接的斷開的流程是什麼?

看看客戶端主動調用了 close,會發生什麼?
在這裏插入圖片描述
在這裏插入圖片描述


版權聲明:本文爲CSDN博主「小林coding」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_34827674/article/details/105331617

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章