【建議收藏】最詳細的TCP三次握手、四次揮手講解

針對面試,知道往往是不夠的,重要的是你能否在面試的場景下利用你的回答突顯你的優點,刻意練習+費曼技巧,助你拿到大廠 offer,後續的文章中我也會講述我如何在大二就進入騰訊實習。

問題描述

你能否講解一下TCP的三次握手與四次揮手呢?

面試官如果從整體到局部入手,那我們就先講講整個三次握手和四次揮手的過程,但不要忘記,講的同時應該適當體現你對該知識點掌握的深度和廣度,具體怎麼說,我們後面慢慢道來。

三次握手

所謂的握手即一次發包到接收的過程,可能從客戶端發送到服務端,也可能從服務端發送到客戶端。

過程描述

先上一張TCP報文結構圖,待會我們會回來看這張圖:

TCP報文結構

先上三次握手的流程圖:

三次握手

接下來我們來詳細講解下上圖的過程:

  • 客戶主機發起連接請求,設置SYN標誌位爲1,同時客戶端隨機選擇了一個初始序號client_isn,並且存放在TCP報文字段的序號中,如下圖:
    第一次握手:SYN報文

  • 接下來,當服務端接收到該報文後,會爲其分配TCP 緩存和變量(這使得TCP容易受到被稱爲SYN 洪泛攻擊的拒絕服務攻擊)緊接着,服務端會返回一個SYNACK 報文到客戶端,其中SYN標誌位爲1確認號設置爲client_isn + 1,並且選一個自己的初始序號server_isn,並放置在序號字段中,如下圖:

    第二次握手:SYNACK報文

  • 當收到服務器發來的SYNACK報文段後,客戶端也需要給該連接分配緩存和變量,然後再次發送一個確認報文給服務端,其中,SYN標誌位設置爲0,將確認號設置爲server_isn + 1,另外,此次報文可以攜帶負載數據:

    第三次握手:ACK報文

細節拓展

  • 三次握手的狀態轉換圖(建議達到能默寫下來的熟練程度)
    三次握手狀態圖
  • 服務器爲什麼要使用特殊的初始序號server_isn?這爲什麼是必要的呢?

這個細節和問題深究第3題是一致的,服務器使用特定的初始序列號 server_isn(從目的地IP端口散列中獲取)可以用來抵禦SYN洪水攻擊。具體爲什麼,以及什麼是SYN 洪泛攻擊,問題深究部分我們會再詳談。

爲了下文描述方便,我們將三次握手分別稱爲:SYN 報文SYNACK 報文ACK 報文

問題深究

1.爲什麼要三次握手而不是兩次?

簡單來說,三次握手的目的是爲了讓雙方驗證各自的接收能力和發送能力

  • 第一次握手,A 發送SYNBB接收到了後,能確認什麼呢?
    顯然,B能確認A發送能力和B接收能力;

  • 第二次握手,B發送SYNACKAA接收到後,能確認什麼呢?
    A能確認B的發送能力和A自己的接收能力,此外,A收到了SYNACK,那麼說明前面A發的SYN成功到達B的手中,所以也能確認A自己的發送能力和B接收能力;至此,A已經確認了雙方各自的發送能力和接收能力都是OK的,因此轉爲ESTABLISHED狀態;

  • 第三次握手,A發送ACKBB接收後,能確認什麼呢?

    直接的,B能確認A發送能力和B接收能力,另外由於B能收到ACK說明前面發送的SYNACK已經成功被接受了,說明能確認A接收能力和B發送能力。

如果使用兩次握手,就不能確認上述所說的四種能力,那麼就會導致問題。

假定不採用第三次報文握手,那麼只要B發出確認,新的連接就建立了。

現假定一種異常情況,即A發出的SYN報文段並沒有丟失,而是在某些網絡節點長時間滯留了,以致延誤到連接釋放後的某個時間纔到達B。本來這是一個早已失效的報文段。但B收到此失效的連接請求報文段後,卻誤以爲是A又發出一次新的連接請求,於是就向A發出確認報文段,同意建立連接。

由於現在A並沒有發出建立連接的請求,因此不會理睬B的確認,也不會向B發送數據,但B卻以爲新的運輸連接已經建立了,並一直等待A發來的數據。B的許多資源就這樣白白浪費了。

2.兩個TCP建立請求相互之間同時發起時會發生什麼?建立幾個連接?

首先理解題意,我們知道三次握手正是建立TCP連接的過程,我們假設 A 是客戶端,B 是服務端:

rfc793-同時啓動

這裏先給大家講一下上圖中一些符號的含義:

  • 右箭頭 (–>) :從 A 發送到 B 的 TCP 報文段,且 B 接收到了;
  • 左箭頭 (<–) :從 B 發送到 A 的 TCP 報文段,且 A 接收到了;
  • 省略號 (…) :TCP 報文段仍在網絡中(delayed);
  • 丟失 (“XXX”) :TCP 報文段丟失或者被拒絕。
  • 註釋會放在括號中;
  • TCP 狀態代表了處於中間的報文段到達之後的狀態(AFTER);
  • 報文段的內容只顯示了序列號(SEQ)、控制符(CTL)和 ACK,其餘內容被省略。

接下來我們詳細來看看上圖中,兩個請求同時相互發起時,兩個TCP均會經歷如下狀態的轉換:

同步請求的狀態轉換

上述的狀態轉換圖可以跟前面的三次握手的轉換圖進行對比理解,同時發起的兩個請求最終只會建立一個連接

SYN-RECEIVEDSYN-RCVD是一樣的,前者rcf793中的描述方法,後者是《計算機網絡-自頂向下方法》中的使用方法。

3. 客戶端正在和服務端建立TCP 連接,然而當服務器變爲SYN-RCVD後,此時一箇舊的SYN 報文又到達了,服務器會如何處理?

其實這道題更加深挖了TCP 建立連接的過程,我們可以在rfc793中瞭解到詳細信息。

rfc793-RST

從上圖可以看到,第三行就是舊的SYN 連接到達服務器時,第四行是服務器照常返回,第五行是客戶端給服務端發送RST 報文,將服務端重置爲LISTEN

我們需要從上圖瞭解到的一點是,服務端在SYN_RECEIVED狀態下,接收到舊的SYN 報文時是不能作出判斷的,而是照常返回,當客戶端接收到該報文後發現異常,纔會發送RST 報文,重置連接。

關於RST 報文,我一開始也很疑惑,直到看到rfc793 原文

rfc793-page33

確實說明了TCP B不能檢測這個舊的SYN 報文是否正確,所以正常返回。而客戶端收到會進行檢測,發現是舊的報文,就會返回RST 報文

4.第三次握手失敗了怎麼辦?

這個問題在網上找到的答案質量參差不齊,翻閱了rfc793,仔細研究後,最終整理出以下答案:

首先考慮失敗的情況:

ACK報文丟失導致第三次握手失敗

當客戶端收到服務端的SYNACK應答後,其狀態變爲ESTABLISHED,並會發送ACK包給服務端,準備發送數據了。如果此時ACK在網絡中丟失(如上圖所示),過了超時計時器後,那麼服務端會重新發送SYNACK包,重傳次數根據/proc/sys/net/ipv4/tcp_synack_retries來指定,默認是5次。如果重傳指定次數到了後,仍然未收到ACK應答,那麼一段時間後,Server自動關閉這個連接。

問題就在這裏,客戶端已經認爲連接建立,而服務端則可能處在SYN-RCVD或者CLOSED,接下來我們需要考慮這兩種情況下服務端的應答:

  • 服務端處於CLOSED,當接收到連接已經關閉的請求時,服務端會返回RST 報文,客戶端接收到後就會關閉連接,如果需要的話則會重連,那麼那就是另一個三次握手了。
  • 服務端處於SYN-RCVD,此時如果接收到正常的ACK 報文,那麼很好,連接恢復,繼續傳輸數據;如果接收到寫入數據等請求呢?注意了,此時寫入數據等請求也是帶着ACK 報文的,實際上也能恢復連接,使服務器恢復到ESTABLISHED狀態,繼續傳輸數據。

這個結論也可以在STACKFLOW上找到驗證:

What if a TCP handshake segment is lost?

上圖圈住的部分:

總的來說,如果一個ACK 報文丟失了,但它的下一個數據包沒有丟失,那麼連接正常,否則,連接會被重置。

5.知道SYN攻擊嗎?如何防範?

所謂SYN 洪泛攻擊,就是利用SYNACK 報文的時候,服務器會爲客戶端請求分配緩存,那麼黑客(攻擊者),就可以使用一批虛假的ip向服務器大量地發建立TCP 連接的請求,服務器爲這些虛假ip分配了緩存後,處在SYN_RCVD狀態,存放在半連接隊列中;另外,服務器發送的請求又不可能得到回覆(ip都是假的,能回覆就有鬼了),只能不斷地重發請求,直到達到設定的時間/次數後,纔會關閉。

服務器不斷爲這些半開連接分配資源(但從未使用),導致服務器的連接資源被消耗殆盡,不過所幸,我們可以使用SYN Cookie進行有效地防禦。

所謂的SYN Cookie防禦系統,與前面接收到SYN 報文就分配緩存不同,此時暫不分配資源;同時利用SYN 報文目的地IP端口,以及服務器存儲的一個祕密數,使用它們進行散列,得到server_isn,然後附着在SYNACK 報文中發送給客戶端,接下來就是對ACK 報文進行判斷,如果其返回的ack字段正好等於server_isn + 1,說明這是一個合法的ACK,那麼服務器纔會爲其生成一個具有套接字的全開的連接。

SYN Cookie 防禦

當然這種方案也有一定缺點,最明顯的就是服務器不保存連接的半開狀態,就喪失重發SYN-ACK消息的能力,這一方面會降低正常用戶的連接成功率,另一方面會導致某些情況下正常通信的雙方會對連接是否成功打開產生誤解,如客戶端發給服務端的第三次握手消息(ACK)半路遺失,客戶端認爲連接成功了,服務端認爲沒收到ACK,連接沒成功,這種情況就需要上層應用採取策略特別處理了。

6.(ISN)是固定的嗎?

不固定,client_isn是隨機生成的,而server_isn則需要根據SYN 報文中的源、ip和端口,加上服務器本身的密碼數進行相同的散列得到,顯然這也不是固定的。

7.三次握手過程中可以攜帶數據嗎?

講過程的時候其實已經講了,第三次握手是可以攜帶數據的,而前兩次不行。

8. 關於 https 的認證過程?

限於篇幅,此處暫時不講,留意後續文章。歡迎大家關注我的公衆號【編程充電寶】,你的支持,是我持續輸出的最大動力~

四次揮手

握手類似,每次揮手也代表一次報文的發出和接收。

過程描述

因爲自頂向上這本書裏面的圖比較簡略,這裏採用謝希仁版本的計算機網絡中的圖:

計算機網絡-謝希仁-四次揮手

首先,當前客戶端和服務器的狀態都爲ESTABLISHED,接下來我們詳細講解下上圖的過程:

  • 客戶主機發起連接釋放的請求,設置FIN1,當然,序號seq也會帶上,這裏假設爲u;發送完畢後,客戶端進入 FIN-WAIT-1 狀態。

    第一次揮手:FIN報文

  • 服務端接收到FIN 報文後,會返回一個ACK 報文回去,此時設置ACK1確認號u + 1;表明自己接受到了客戶端關閉連接的請求,但還沒有準備好關閉連接。發送完畢後,服務器端進入 CLOSE-WAIT 狀態,客戶端接收到這個確認包之後,進入 FIN-WAIT-2 狀態,等待服務器端關閉連接。
    第二次揮手:ACK報文

  • 服務器端準備好關閉連接時,向客戶端發送結束連接請求,FIN 置爲1;發送完畢後,服務器端進入 LAST-ACK 狀態,等待來自客戶端的最後一個ACK

    第三次揮手:FIN報文

  • 客戶端接收到服務端傳來的FIN 報文後,知道服務器已經準備好關閉了,發送一個確認包,並進入 TIME-WAIT狀態,等待可能出現的要求重傳的ACK 報文;服務器端接收到這個確認包之後,關閉連接,進入 CLOSED 狀態。

    客戶端等待了某個固定時間(兩個最大段生命週期,2MSL,2 Maximum Segment Lifetime)之後,沒有收到服務器端的 ACK ,認爲服務器端已經正常關閉連接,於是自己也關閉連接,進入 CLOSED 狀態。
    第四次揮手:ACK報文

這裏我們再來看下rfc793中關於四次揮手的簡單例子:

rfc793-正常的關閉例子

細節拓展

  • 四次揮手重要的是TIME-WAIT狀態,爲什麼需要這個狀態呢?

要確保服務器是否已經收到了我們的ACK 報文,如果沒有收到的話,服務器會重新發FIN 報文給客戶端,那麼客戶端再次收到FIN 報文之後,就知道之前的 ACK 報文丟失了,就會再次發送ACK 報文

問題深究

1.爲什麼握手只要三次,揮手卻要四次?

關鍵就在中間兩步。

  • 建立連接時,當服務器收到客戶端的SYN 報文後,可以直接發送SYNACK 報文。其中ACK是用來應答的,SYN是用來同步的。
  • 但是關閉連接時,當服務器收到FIN 報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK 報文,告訴客戶端,“你發的FIN 報文我收到了”。只有等到服務器所有的報文都發送/接收完了,我才能發送FIN 報文,因此不能一起發送,需要四次握手。

2.爲什麼 TIME_WAIT 狀態需要經過 2MSL 才能轉換到 CLOSE 狀態?

  • 第一,爲了保證客戶端發送的最後一個ACK 報文能夠到達服務器。我們必須假設網絡是不可靠的,ACK 報文可能丟失。如果服務端發出FIN 報文後沒有收到ACK 報文,就會重發FIN 報文,此時處於TIME-WAIT狀態的客戶端就會重發ACK 報文。當然,客戶端也不能無限久的等待這個可能存在的FIN 報文,因爲如果服務端正常接收到了ACK 報文後是不會再發FIN 報文的。因此,客戶端需要設置一個計時器,那麼等待多久最合適呢?所謂的MSL(Maximum Segment Lifetime)指一個報文在網絡中最大的存活時間,2MSL就是一個發送和一個回覆所需的最大時間。如果直到2MSL時間後,客戶端都沒有再次收到FIN 報文,那麼客戶端推斷ACK 報文已經被服務器成功接收,所以結束TCP 連接
  • 第二,防止已失效的連接請求報文段出現在新的連接中。客戶端在發送完最後一個ACK 報文後,再經過時間2MSL,就可以使由於網絡不通暢產生的滯留報文段失效。這樣下一個新的連接中就不會出現舊的連接請求報文。

致謝

以下書籍和博文爲本文的撰寫提供不少幫助:

計算機網絡 (第7版) 謝希仁

計算機網絡-自頂向下方法 第七版

rfc793 原文連接:https://tools.ietf.org/rfc/rfc793.txt

https://hit-alibaba.github.io/interview/basic/network/TCP.html Github【HIT-Alibaba/interview】

https://stackoverflow.com/questions/16259774/what-if-a-tcp-handshake-segment-is-lost StackFlow

https://mp.weixin.qq.com/s/8t_KFtrrBkFyZKPJg_y6pw 公衆號【苦逼的碼農】

https://www.cnblogs.com/Andya/p/7272462.html 博客園 【Andya】

https://www.cnblogs.com/popduke/p/5823801.html 博客園 【YonnyHao】

https://blog.csdn.net/scuzoutao/article/details/81774100 csdn博主【鄒啊濤】

https://blog.csdn.net/qq_38950316/article/details/81087809 csdn博主 【青柚_】

讀者們也可將上述書籍和博文作爲延伸閱讀。

img

發佈了97 篇原創文章 · 獲贊 80 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章