天天看點

終于搞懂了 TCP 的 11 種狀态 ,太不容易了…

點選上方“肉眼品世界”,選擇“設為星标”

深度價值體系傳遞

終于搞懂了 TCP 的 11 種狀态 ,太不容易了…

本來想寫運維過程中,nginx 伺服器中 time_wait 的相關測試及解決方法的,然後發現TCP 的狀态需要先鋪墊一下,于是就整理了這篇文章。

網上很多大佬整理TCP三向交握、四次揮手,看到過很多人寫,但其實從運維角度來說,我們分析 TCP 連結狀态的時候,首先是用

netstat

ss

來檢視。
終于搞懂了 TCP 的 11 種狀态 ,太不容易了…

之後才會根據 TCP 狀态的情況進行抓包分析,進一步确認一些問題,是以我們首先看到的會是 TCP 的狀态,那麼就需要很清楚的了解 TCP 的11種狀态代表着什麼。

TCP 的11種狀态分别對應 TCP 三次握手過程的5種狀态和TCP四次揮手斷開過程中的6種狀态。

終于搞懂了 TCP 的 11 種狀态 ,太不容易了…

如上圖,就是11種狀态,在整個TCP建立連接配接和斷開連接配接的整個過程

下面我用 tcpdump 抓了個完整的用戶端和服務端的三次握手和四次揮手的包,可以對應上面的狀态圖

終于搞懂了 TCP 的 11 種狀态 ,太不容易了…
下面分開來詳細看,首先是三次握手
終于搞懂了 TCP 的 11 種狀态 ,太不容易了…
上面這個圖就是完整的三次握手過程

  • 首先由 client 送出請求連接配接,即SYN=1 ACK=0,TCP 規定 SYN=1 時不能攜帶資料,但要消耗一個 seq,是以聲明自己的seq=x
  • 然後 Server 進行回複确認,即 SYN=1 ACK=1 seq=y ack=x+1
  • 最後 Client 再進行一次确認,但不用SYN了,即ACK=1 seq=x+1 ack=y+1

整個過程中對應的TCP狀态如下:

  • CLOSED:初始狀态,表示TCP連接配接是”關閉着的”或”未打開的”
  • LISTEN:表示伺服器端的某個SOCKET處于監聽狀态,可以接受用戶端的連接配接
  • SYN_RCVD:表示伺服器接收到了來自用戶端請求連接配接的SYN封包。這個狀态是在服務端的,但是它是一個中間狀态,很短暫,平常我們用netstat或ss的時候,不太容易看到這種狀态,但是遇到SYN flood之類的SYN攻擊時,會出現大量的這種狀态,即收不到三次握手最後一個用戶端發來的ACK,是以一直是這個狀态,不會轉換到ESTABLISHED
  • SYN_SENT:這個狀态與SYN_RCVD狀态相呼應,,它是TCP連接配接用戶端的狀态,當用戶端SOCKET執行connect()進行連接配接時,它首先發送SYN封包,然後随機進入到SYN_SENT狀态,并等待服務端的SYN和ACK,該狀态表示用戶端的SYN已發送
  • ESTABLISHED:表示TCP連接配接已經成功建立,開始傳輸資料

以上就是三次握手的五種TCP狀态,單從用戶端服務端角度來區分的話,CLOSED和ESTABLISHED會在用戶端和服務端都出現,而LISTEN和SYN_RCVD通常是出現在服務端,SYN_SENT出現在用戶端

但通常在伺服器和用戶端并不是絕對的,比如 Nginx 的伺服器中,Nginx 通常作為 web 代理伺服器,它既是服務端,也是用戶端,是以在查詢統計 TCP 狀态的時候,最好通過比對端口來區分是用戶端的還是服務端的,來更精确的定位問題。

接着看四次揮手的狀态

終于搞懂了 TCP 的 11 種狀态 ,太不容易了…
  • FIN_WAIT_1:這個狀态在實際工作中很少能看到,當用戶端想要主動關閉連接配接時,它會向服務端發送FIN封包,此時TCP狀态就進入到FIN_WAIT_1的狀态,而當服務端回複ACK,确認關閉後,則用戶端進入到FIN_WAIT_2的狀态,也就是隻有在沒有收到服務端ACK的情況下,FIN_WAIT_1狀态才能看到,然後長時間收不到ACK,通常會在預設逾時時間60s(由核心參數tcp_fin_timeout控制)後,直接進入CLOSED狀态
  • FIN_WAIT_2:這個狀态相比較常見,也是需要注意的一個狀态,FIN_WAIT_1在接收到服務端ACK之後就進入到FIN_WAIT_2的狀态,然後等待服務端發送FIN,是以在收到對端FIN之前,TCP都會處于FIN_WAIT_2的狀态,也就是,在主動斷開的一端發現大量的FIN_WAIT_2狀态時,需要注意,可能時網絡不穩定或程式中忘記調用連接配接關閉,FIN_WAIT_2也有逾時時間,也是由核心參數tcp_fin_timeout控制,當FIN_WAIT_2狀态逾時後,連接配接直接銷毀
  • CLOSE_WAIT:表示正在等待關閉,該狀态隻在被動端出現,即當主動斷開的一端調用close()後發送FIN封包給被動端,被動段必然會回應一個ACK(這是由TCP協定層決定的),這個時候,TCP連接配接狀态就進入到CLOSE_WAIT
  • LAST_ACK:當被動關閉的一方在發送FIN封包後,等待對方的ACK封包的時候,就處于LAST_ACK的狀态,當收到對方的ACK之後,就進入到CLOSED狀态了
  • TIME_WAIT:該狀态是最常見的狀态,主動方在收到對方FIN後,就由FIN_WAIT_2狀态進入到TIME_WAIT狀态
  • CLOSING:這個狀态是一個比較特殊的狀态,也比較少見,正常情況下不會出現,但是當雙方同時都作為主動的一方,調用 close() 關閉連接配接的時候,兩邊都進入FIN_WAIT_1 的狀态,此時期望收到的是ACK包,進入 FIN_WAIT_2 的狀态,但是卻先收到了對方的FIN包,這個時候,就會進入到 CLOSING 的狀态,然後給對方一個ACK,接收到 ACK 後直接進入到 CLOSED 狀态。

繼續閱讀