TCP 三次握手背的滾瓜亂熟,那意外情況呢?丟包了呢?故意不回復 ACK 呢?


一、序

當我們聊到 TCP 協議的時候,聊的最多的就是三次握手與四次揮手,但是你有沒有想過,三次握手或者四次揮手時,如果發生異常了,是如何處理的?又是由誰來處理?

TCP 作為一個靠譜的協議,在傳輸數據的前后,需要在雙端之間建立連接,並在雙端各自維護連接的狀態。TCP 並沒有什么特別之處,在面對着多變的網絡情況,也只能通過不斷的重傳和各種算法來保證可靠性。

建立連接前,TCP 會通過三次握手來保證雙端狀態正確,然后就可以正常傳輸數據了,在數據傳輸完畢后,又通過四次揮手來保證雙端合理的斷開連接並回收各自的資源。

我們在學習 TCP 建連和斷連時,都是一個標准的流程,但是網絡是多變的,很多時候並不像教科書那樣標准,那么今天就來聊聊 TCP 三次握手出現異常的時候,是如何處理的。

二、TCP 三次握手

2.1 簡單理解三次握手

雖然是說三次握手的異常情況,我們還是先來了解一下三次握手。

在通過 TCP 傳輸數據時,第一步就是要先建立一個連接。TCP 建立連接的過程,就是我們常說的三次握手。

我們經常將三次握手,描述成「請求 → 應答 → 應答之應答」。

至於 TCP 握手為什么是三次,其實就是要讓雙端都經歷一次「請求 → 應答」的過程,來確認對方還在。網絡情況是多變的,雙端都需要一次自己主動發起的請求和對方回復的應答過程,來確保對方和網絡是正常的。

下面這張圖,是比較經典的 TCP 三次握手的消息和雙端狀態的變化。

 

 

我們先來解釋一下這張圖:

1. 在初始時,雙端處於 CLOSE 狀態,服務端為了提供服務,會主動監聽某個端口,進入 LISTEN 狀態。

2. 客戶端主動發送連接的「SYN」包,之后進入 SYN-SENT 狀態,服務端在收到客戶端發來的「SYN」包后,回復「SYN,ACK」包,之后進入 SYN-RCVD 狀態。

3. 客戶端收到服務端發來的「SYN,ACK」包后,可以確認對方存在,此時回復「ACK」包,並進入 ESTABLISHED 狀態。

4. 服務端收到最后一個「ACK」包后,也進入 ESTABLISHED 狀態。

正常的三次握手之后,雙端都進入 ESTABLISHED 狀態,在此之后,就是正常的數據傳輸過程。

2.2 TCP 握手的異常情況

三次握手的正常發包和應答,以及雙端的狀態扭轉我們已經講了,接下來就來看看在這三次握手的過程中,出現的異常情況。

1. 客戶端第一個「SYN」包丟了。

如果客戶端第一個「SYN」包丟了,也就是服務端根本就不知道客戶端曾經發過包,那么處理流程主要在客戶端。

而在 TCP 協議中,某端的一組「請求-應答」中,在一定時間范圍內,只要沒有收到應答的「ACK」包,無論是請求包對方沒有收到,還是對方的應答包自己沒有收到,均認為是丟包了,會觸發超時重傳機制。

所以此時會進入重傳「SYN」包。根據《TCP/IP詳解卷Ⅰ:協議》中的描述,此時會嘗試三次,間隔時間分別是 5.8s、24s、48s,三次時間大約是 76s 左右,而大多數伯克利系統將建立一個新連接的最長時間,限制為 75s。

也就是說三次握手第一個「SYN」包丟了,會重傳,總的嘗試時間是 75s。

參考:http://docs.52im.net/extend/docs/book/tcpip/vol1/18/

2. 服務端收到「SYN」並回復的「SYN,ACK」包丟了。

此時服務端已經收到了數據包並回復,如果這個回復的「SYN,ACK」包丟了,站在客戶端的角度,會認為是最開始的那個「SYN」丟了,那么就繼續重傳,就是我們前面說的「錯誤 1 流程」。

而對服務端而言,如果發送的「SYN,ACK」包丟了,在超時時間內沒有收到客戶端發來的「ACK」包,也會觸發重傳,此時服務端處於 SYN_RCVD 狀態,會依次等待 3s、6s、12s 后,重新發送「SYN,ACK」包。

而這個「SYN,ACK」包的重傳次數,不同的操作系統下有不同的配置,例如在 Linux 下可以通過 tcp_synack_retries 進行配置,默認值為 5。如果這個重試次數內,仍未收到「ACK」應答包,那么服務端會自動關閉這個連接。

同時由於客戶端在沒有收到「SYN,ACK」時,也會進行重傳,當客戶端重傳的「SYN」收到后,會立即重新發送「SYN,ACK」包。

3. 客戶端最后一次回復「SYN,ACK」的「ACK」包丟了。

如果最后一個「ACK」包丟了,服務端因為收不到「ACK」會走重傳機制,而客戶端此時進入 ESTABLISHED 狀態。

多數情況下,客戶端進入 ESTABLISHED 狀態后,則認為連接已建立,會立即發送數據。但是服務端因為沒有收到最后一個「ACK」包,依然處於 SYN-RCVD 狀態。

那么這里的關鍵,就在於服務端在處於 SYN-RCVD 狀態下,收到客戶端的數據包后如何處理?

這也是比較有爭議的地方,有些資料里會寫到當服務端處於 SYN-RCVD 狀態下,收到客戶端的數據包后,會直接回復 RTS 包響應,表示服務端錯誤,並進入 CLOSE 狀態。

但是這樣的設定有些過於嚴格,試想一下,服務端還在通過三次握手階段確定對方是否真實存在,此時對方的數據已經發來了,那肯定是存在的。

所以當服務端處於 SYN-RCVD 狀態下時,接收到客戶端真實發送來的數據包時,會認為連接已建立,並進入 ESTABLISHED 狀態。

實踐出真知,具體測試流程可以參考這篇文章:https://blog.csdn.net/zerooffdate/article/details/79359726

那么實際情況,為什么會這樣呢?

當客戶端在 ESTABLISHED 狀態下,開始發送數據包時,會攜帶上一個「ACK」的確認序號,所以哪怕客戶端響應的「ACK」包丟了,服務端在收到這個數據包時,能夠通過包內 ACK 的確認序號,正常進入 ESTABLISHED 狀態。

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

4. 客戶端故意不發最后一次「SYN」包。

前面一直在說正常的異常邏輯,雙方都還算友善,按規矩做事,出現異常主要也是因為網絡等客觀問題,接下來說一個惡意的情況。

如果客戶端是惡意的,在發送「SYN」包后,並收到「SYN,ACK」后就不回復了,那么服務端此時處於一種半連接的狀態,雖然服務端會通過 tcp_synack_retries 配置重試的次數,不會無限等待下去,但是這也是有一個時間周期的。

如果短時間內存在大量的這種惡意連接,對服務端來說壓力就會很大,這就是所謂的 SYN FLOOD 攻擊

這就屬於安全攻防的范疇了,今天就不討論了,有興趣可以自行了解。

三、小結時刻

今天我們聊了 TCP 在建立連接的三次握手階段,出現異常,如何處理的一些事兒。

大多數情況下,都是依賴超時重傳來保證 TCP 的可靠性,但是重傳的次數,狀態的轉換,這些細節,就是本文的主題。

本來想着一口氣聊完 TCP 的三次握手和四次揮手的異常處理,但是看篇幅有點過長了,不利於公眾號中的閱讀,所以分為兩篇來寫。

有任何問題歡迎留言討論,有所幫助也別忘了轉發和點好看支持一下,謝謝!

https://mp.weixin.qq.com/s/LpkCPTh6GmL7GvB4QiEqeg


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM