天天看點

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

作者:小林coding

圖解計算機基礎(作業系統、計算機網絡、計算機組成、資料庫等)網站:​https://xiaolincoding.com

大家好,我是小林。

之前寫過 TCP 三次握手和四次揮手過程中,途中某一步的封包丢失會發生什麼的文章。

當時,主要是文字描述,可能不太好記憶,是以我針對每一步的異常情況,重新畫了圖,友善大家了解和記憶。

發車!

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

TCP 三次握手丢包情況

第一次握手丢失了,會發生什麼?

當用戶端想和服務端建立 TCP 連接配接的時候,首先第一個發的就是 SYN 封包,然後進入到 ​

​SYN_SENT​

​ 狀态。

在這之後,如果用戶端遲遲收不到服務端的 SYN-ACK 封包(第二次握手),就會觸發「逾時重傳」機制,重傳 SYN 封包,而且重傳的 SYN 封包的序列号都是一樣的。

不同版本的作業系統可能逾時時間不同,有的 1 秒的,也有 3 秒的,這個逾時時間是寫死在核心裡的,如果想要更改則需要重新編譯核心,比較麻煩。

當用戶端在 1 秒後沒收到服務端的 SYN-ACK 封包後,用戶端就會重發 SYN 封包,那到底重發幾次呢?

在 Linux 裡,用戶端的 SYN 封包最大重傳次數由 ​

​tcp_syn_retries​

​核心參數控制,這個參數是可以自定義的,預設值一般是 5。

# cat /proc/sys/net/ipv4/tcp_syn_retries
5      

通常,第一次逾時重傳是在 1 秒後,第二次逾時重傳是在 2 秒,第三次逾時重傳是在 4 秒後,第四次逾時重傳是在 8 秒後,第五次是在逾時重傳 16 秒後。沒錯,每次逾時的時間是上一次的 2 倍。

當第五次逾時重傳後,會繼續等待 32 秒,如果服務端仍然沒有回應 ACK,用戶端就不再發送 SYN 包,然後斷開 TCP 連接配接。

是以,總耗時是 1+2+4+8+16+32=63 秒,大約 1 分鐘左右。

舉個例子,假設 tcp_syn_retries 參數值為 3,那麼當用戶端的 SYN 封包一直在網絡中丢失時,會發生下圖的過程:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

具體過程:

  • 當用戶端逾時重傳 3 次 SYN 封包後,由于 tcp_syn_retries 為 3,已達到最大重傳次數,于是再等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到服務端的第二次握手(SYN-ACK 封包),那麼用戶端就會斷開連接配接。

第二次握手丢失了,會發生什麼?

當服務端收到用戶端的第一次握手後,就會回 SYN-ACK 封包給用戶端,這個就是第二次握手,此時服務端會進入 ​

​SYN_RCVD​

​ 狀态。

第二次握手的 ​

​SYN-ACK​

​ 封包其實有兩個目的 :

  • 第二次握手裡的 ACK, 是對第一次握手的确認封包;
  • 第二次握手裡的 SYN,是服務端發起建立 TCP 連接配接的封包;

是以,如果第二次握手丢了,就會發生比較有意思的事情,具體會怎麼樣呢?

因為第二次握手封包裡是包含對用戶端的第一次握手的 ACK 确認封包,是以,如果用戶端遲遲沒有收到第二次握手,那麼用戶端就覺得可能自己的 SYN 封包(第一次握手)丢失了,于是用戶端就會觸發逾時重傳機制,重傳 SYN 封包。

然後,因為第二次握手中包含服務端的 SYN 封包,是以當用戶端收到後,需要給服務端發送 ACK 确認封包(第三次握手),服務端才會認為該 SYN 封包被用戶端收到了。

那麼,如果第二次握手丢失了,服務端就收不到第三次握手,于是服務端這邊會觸發逾時重傳機制,重傳 SYN-ACK 封包。

在 Linux 下,SYN-ACK 封包的最大重傳次數由 ​

​tcp_synack_retries​

​核心參數決定,預設值是 5。

# cat /proc/sys/net/ipv4/tcp_synack_retries
5      

是以,當第二次握手丢失了,用戶端和服務端都會重傳:

  • 用戶端會重傳 SYN 封包,也就是第一次握手,最大重傳次數由​

    ​tcp_syn_retries​

    ​核心參數決定;
  • 服務端會重傳 SYN-ACK 封包,也就是第二次握手,最大重傳次數由​

    ​tcp_synack_retries​

    ​ 核心參數決定。

舉個例子,假設 tcp_syn_retries 參數值為 1,tcp_synack_retries 參數值為 2,那麼當第二次握手一直丢失時,發生的過程如下圖:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

具體過程:

  • 當用戶端逾時重傳 1 次 SYN 封包後,由于 tcp_syn_retries 為 1,已達到最大重傳次數,于是再等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到服務端的第二次握手(SYN-ACK 封包),那麼用戶端就會斷開連接配接。
  • 當服務端逾時重傳 2 次 SYN-ACK 封包後,由于 tcp_synack_retries 為 2,已達到最大重傳次數,于是再等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到用戶端的第三次握手(ACK 封包),那麼服務端就會斷開連接配接。

第三次握手丢失了,會發生什麼?

用戶端收到服務端的 SYN-ACK 封包後,就會給服務端回一個 ACK 封包,也就是第三次握手,此時用戶端狀态進入到 ​

​ESTABLISH​

​ 狀态。

因為這個第三次握手的 ACK 是對第二次握手的 SYN 的确認封包,是以當第三次握手丢失了,如果服務端那一方遲遲收不到這個确認封包,就會觸發逾時重傳機制,重傳 SYN-ACK 封包,直到收到第三次握手,或者達到最大重傳次數。

注意,ACK 封包是不會有重傳的,當 ACK 丢失了,就由對方重傳對應的封包。

舉個例子,假設 tcp_synack_retries 參數值為 2,那麼當第三次握手一直丢失時,發生的過程如下圖:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

具體過程:

  • 當服務端逾時重傳 2 次 SYN-ACK 封包後,由于 tcp_synack_retries 為 2,已達到最大重傳次數,于是再等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到用戶端的第三次握手(ACK 封包),那麼服務端就會斷開連接配接。

TCP 四次揮手丢包情況

第一次揮手丢失了,會發生什麼?

當用戶端(主動關閉方)調用 close 函數後,就會向服務端發送 FIN 封包,試圖與服務端斷開連接配接,此時用戶端的連接配接進入到 ​

​FIN_WAIT_1​

​ 狀态。

正常情況下,如果能及時收到服務端(被動關閉方)的 ACK,則會很快變為 ​

​FIN_WAIT2​

​狀态。

如果第一次揮手丢失了,那麼用戶端遲遲收不到被動方的 ACK 的話,也就會觸發逾時重傳機制,重傳 FIN 封包,重發次數由 ​

​tcp_orphan_retries​

​ 參數控制。

當用戶端重傳 FIN 封包的次數超過 ​

​tcp_orphan_retries​

​​ 後,就不再發送 FIN 封包,則會在等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到第二次揮手,那麼直接進入到 ​

​close​

​ 狀态。

舉個例子,假設 tcp_orphan_retries 參數值為 3,當第一次揮手一直丢失時,發生的過程如下圖:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

具體過程:

  • 當用戶端逾時重傳 3 次 FIN 封包後,由于 tcp_orphan_retries 為 3,已達到最大重傳次數,于是再等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到服務端的第二次揮手(ACK封包),那麼用戶端就會斷開連接配接。

第二次揮手丢失了,會發生什麼?

當服務端收到用戶端的第一次揮手後,就會先回一個 ACK 确認封包,此時服務端的連接配接進入到 ​

​CLOSE_WAIT​

​ 狀态。

在前面我們也提了,ACK 封包是不會重傳的,是以如果服務端的第二次揮手丢失了,用戶端就會觸發逾時重傳機制,重傳 FIN 封包,直到收到服務端的第二次揮手,或者達到最大的重傳次數。

舉個例子,假設 tcp_orphan_retries 參數值為 2,當第二次揮手一直丢失時,發生的過程如下圖:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

具體過程:

  • 當用戶端逾時重傳 2 次 FIN 封包後,由于 tcp_orphan_retries 為 2,已達到最大重傳次數,于是再等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到服務端的第二次揮手(ACK 封包),那麼用戶端就會斷開連接配接。

這裡提一下,當用戶端收到第二次揮手,也就是收到服務端發送的 ACK 封包後,用戶端就會處于 ​

​FIN_WAIT2​

​ 狀态,在這個狀态需要等服務端發送第三次揮手,也就是服務端的 FIN 封包。

對于 close 函數關閉的連接配接,由于無法再發送和接收資料,是以​

​FIN_WAIT2​

​​ 狀态不可以持續太久,而 ​

​tcp_fin_timeout​

​ 控制了這個狀态下連接配接的持續時長,預設值是 60 秒。

這意味着對于調用 close 關閉的連接配接,如果在 60 秒後還沒有收到 FIN 封包,用戶端(主動關閉方)的連接配接就會直接關閉,如下圖:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

但是注意,如果主動關閉方使用 shutdown 函數關閉連接配接,指定了隻關閉發送方向,而接收方向并沒有關閉,那麼意味着主動關閉方還是可以接收資料的。

此時,如果主動關閉方一直沒收到第三次揮手,那麼主動關閉方的連接配接将會一直處于 ​

​FIN_WAIT2​

​​ 狀态(​

​tcp_fin_timeout​

​ 無法控制 shutdown 關閉的連接配接)。如下圖:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

第三次揮手丢失了,會發生什麼?

當服務端(被動關閉方)收到用戶端(主動關閉方)的 FIN 封包後,核心會自動回複 ACK,同時連接配接處于 ​

​CLOSE_WAIT​

​ 狀态,顧名思義,它表示等待應用程序調用 close 函數關閉連接配接。

此時,核心是沒有權利替代程序關閉連接配接,必須由程序主動調用 close 函數來觸發服務端發送 FIN 封包。

服務端處于 CLOSE_WAIT 狀态時,調用了 close 函數,核心就會發出 FIN 封包,同時連接配接進入 LAST_ACK 狀态,等待用戶端傳回 ACK 來确認連接配接關閉。

如果遲遲收不到這個 ACK,服務端就會重發 FIN 封包,重發次數仍然由 ​

​tcp_orphan_retrie​

​s 參數控制,這與用戶端重發 FIN 封包的重傳次數控制方式是一樣的。

舉個例子,假設 ​

​tcp_orphan_retrie​

​s = 3,當第三次揮手一直丢失時,發生的過程如下圖:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

具體過程:

  • 當服務端重傳第三次揮手封包的次數達到了 3 次後,由于 tcp_orphan_retries 為 3,達到了重傳最大次數,于是再等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到用戶端的第四次揮手(ACK封包),那麼服務端就會斷開連接配接。
  • 用戶端因為是通過 close 函數關閉連接配接的,處于 FIN_WAIT_2 狀态是有時長限制的,如果 tcp_fin_timeout 時間内還是沒能收到服務端的第三次揮手(FIN 封包),那麼用戶端就會斷開連接配接。

第四次揮手丢失了,會發生什麼?

當用戶端收到服務端的第三次揮手的 FIN 封包後,就會回 ACK 封包,也就是第四次揮手,此時用戶端連接配接進入 ​

​TIME_WAIT​

​ 狀态。

在 Linux 系統,TIME_WAIT 狀态會持續 2MSL 後才會進入關閉狀态。

然後,服務端(被動關閉方)沒有收到 ACK 封包前,還是處于 LAST_ACK 狀态。

如果第四次揮手的 ACK 封包沒有到達服務端,服務端就會重發 FIN 封包,重發次數仍然由前面介紹過的 ​

​tcp_orphan_retries​

​ 參數控制。

舉個例子,假設 tcp_orphan_retries 為 2,當第四次揮手一直丢失時,發生的過程如下:

TCP 三次握手和四次揮手,中間失敗了會發生什麼?

具體過程:

  • 當服務端重傳第三次揮手封包達到 2 時,由于 tcp_orphan_retries 為 2, 達到了最大重傳次數,于是再等待一段時間(時間為上一次逾時時間的 2 倍),如果還是沒能收到用戶端的第四次揮手(ACK 封包),那麼服務端就會斷開連接配接。
  • 用戶端在收到第三次揮手後,就會進入 TIME_WAIT 狀态,開啟時長為 2MSL 的定時器,如果途中再次收到第三次揮手(FIN 封包)後,就會重置定時器,當等待 2MSL 時長後,用戶端就會斷開連接配接。

完!

怎麼樣,這下很清晰了吧

更多網絡文章

  • 網絡基礎篇
  • ​​TCP/IP 網絡模型有哪幾層?​​
  • ​​鍵入網址到網頁顯示,期間發生了什麼?​​
  • ​​Linux 系統是如何收發網絡包的?​​
  • HTTP 篇
  • ​​HTTP 常見面試題​​
  • ​​HTTP/1.1如何優化?​​
  • ​​HTTPS RSA 握手解析​​
  • ​​HTTPS ECDHE 握手解析​​
  • ​​HTTPS 如何優化?​​
  • ​​HTTP/2 牛逼在哪?​​
  • ​​HTTP/3 強勢來襲​​
  • ​​既然有 HTTP 協定,為什麼還要有 RPC?​​
  • TCP 篇
  • ​​TCP 三次握手與四次揮手面試題​​
  • ​​TCP 重傳、滑動視窗、流量控制、擁塞控制​​
  • ​​TCP 實戰抓包分析​​
  • ​​TCP 半連接配接隊列和全連接配接隊列​​
  • ​​如何優化 TCP?​​
  • ​​如何了解是 TCP 面向位元組流協定?​​
  • ​​為什麼 TCP 每次建立連接配接時,初始化序列号都要不一樣呢?​​
  • ​​SYN 封包什麼時候情況下會被丢棄?​​
  • ​​四次揮手中收到亂序的 FIN 包會如何處理?​​
  • ​​在 TIME_WAIT 狀态的 TCP 連接配接,收到 SYN 後會發生什麼?​​
  • ​​TCP 連接配接,一端斷電和程序崩潰有什麼差別?​​
  • ​​拔掉網線後, 原本的 TCP 連接配接還存在嗎?​​
  • ​​tcp_tw_reuse 為什麼預設是關閉的?​​
  • ​​HTTPS 中 TLS 和 TCP 能同時握手嗎?​​
  • ​​TCP Keepalive 和 HTTP Keep-Alive 是一個東西嗎?​​
  • ​​TCP 有什麼缺陷?​​
  • ​​如何基于 UDP 協定實作可靠傳輸?​​
  • ​​TCP 和 UDP 可以使用同一個端口嗎?​​
  • ​​服務端沒有 listen,用戶端發起連接配接建立,會發生什麼?​​
  • ​​沒有 accpet,可以建立 TCP 連接配接嗎?​​
  • ​​用了 TCP 協定,資料一定不會丢嗎?​​
  • IP 篇
  • ​​IP 基礎知識全家桶​​
  • ​​ping 的工作原理​​
  • 學習心得
  • ​​計算機網絡怎麼學?​​

繼續閱讀