TCP/IP 四次斷開

昀溪發表於2018-09-02

網路連線狀態

網路連線狀態(11種)非常重要這裡既包含三次握手中的也包括四次斷開中的,所以要熟悉。

  • LISTEN 被動開啟,首先伺服器需要開啟一個socket進行監聽,監聽來自遠方TCP埠的連線請求,等於伺服器端執行socket、bind、listen三個函式之後阻塞在accept處。
  • SYN_SENT 表示主動連線,客戶端能透過應用程式呼叫connect()函式進行active open。於是客戶端TCP傳送一個SYN以請求建立一個連線,之後狀態為SYN_SEND,表示已傳送一個SYN到伺服器端,等待SYN 1+ACK 0響應。
  • SYN_RECV 伺服器端收到客戶端的SYN 1,然後狀態變為SYN_RECV。表示伺服器收到了客戶端發來的SYN,然後自己也響應了給客戶端一個SYN 1+ACK 1,然後等待客戶端確認。 這時候客戶端過來的連線(屬於半連線狀態)被放在一個SYN佇列裡面,SYN泛洪***也是這樣的,就是伺服器響應了SYN+ACK之後,客戶端就不在傳送ACK了,然後繼續傳送SYN,直到把伺服器的最大連線數量耗盡。 半連線佇列長度是由核心引數tcp_max_syn_backlog來決定的。
  • ESTABLISHED 代表一個開啟的連線,客戶端收到伺服器傳送的SYN 1+ACK 1,就變為這個狀態,然後向伺服器傳送ACK,如果伺服器收到這個ACK,那麼它也變為這個狀態。這個狀態就是表示連線以及建立,正在或即將傳輸資料。伺服器收到ACK以後就會把半連線從上面提到的SYN佇列中刪除,然後放到ACCEPT佇列中,這時這個半連線的狀態就變成了ESTABLISHED。
  • FIN_WAIT1 主動關閉端(可以是伺服器也可以是客戶端)應用程式呼叫了close,於是其TCP發出FIN主動關閉請求,也就是四次斷開的第一次,之後就進入了FIN_WAIT1狀態,等待遠端主機的ACK請求。
  • CLOSE_WAIT 被動關閉端(可以是伺服器有可以是客戶端)收到了對方發來的FIN後,進入該狀態,然後發出ACK+1以回應FIN請求(它的接收也作為檔案結束符傳遞給上層應用程式)。這個狀態實際上是說客戶端告訴伺服器我沒有請求或者資料要傳送了,等待看看伺服器或者說是程式還有沒有資料要傳送,如果有則繼續傳送,如果沒有的話,就傳送反向關閉指令。如果伺服器大量連線是這個狀態就要去檢視程式,很有可能是程式設計的問題。
  • FIN_WAIT2 主動關閉端收到ACK+1後,就進入的FIN_WAIT2狀態,也就等伺服器是否還有資料發來,如果伺服器沒有資料了,那麼伺服器就傳送的反向關閉指令。也就是反向關閉連線指令FIN1+ACK1。實際上是告訴客戶端我的資料傳送完了,可以關閉連線了。
  • LAST_ACK 被動關閉端,傳送反向結束連線請求FIN 1+ACK 1,然後進入LAST_ACK狀態,等待主動關閉端傳送ACK。
  • TIME_WAIT 主動關閉端收到FIN 1 +ACK 1後,並進入TIME_WAIT狀態,然後傳送ACK+1,等待一段時間(2MSL)以確保伺服器收到了ACK+1,然後自己進入CLOSED狀態。這個階段主要是客戶端為了再次確認一下伺服器是否可以關閉連線,因為網路畢竟是不可靠的。對於伺服器有大量TIME_WAIT這個問題通常調整sysctl來解決。
  • CLOSING 比較少見,表示等待遠端TCP對連線中斷的確認。
  • CLOSED 被動關閉端在收到ACK包以後,就進入closed狀態,連線結束。

四次斷開過程

客戶端:傳送FIN給伺服器表示主動請求斷開連線,然後自己進入FIN_WAIT1階段,等待伺服器響應
伺服器:收到客戶端的FIN請求,傳送ACK給客戶端表示自己同意斷開連線,伺服器告訴上層【應用程式】有一個連線要請求斷開並且進入CLOSE_WAIT階段,等待應用程式做好關閉準備,其實就是把還沒有傳送完的資料傳送完畢。
客戶端:收到ACK以後進入FIN_WAIT2狀態等待伺服器傳送FIN。
伺服器:當應用程式做好關閉連線的準備後則傳送FIN給客戶端,並且伺服器進入LAST_ACK階段,等待客戶端最後確認。
客戶端:收到伺服器的FIN以後傳送ACK表示確認,此時客戶端進入TIME_WAIT階段,它並不是等什麼確認而是等一段時間保證ACK可以有足夠的時間被伺服器收到,這個時間為1-4分鐘。之後連線關閉。
伺服器:收到客戶端的ACK以後關閉連線。

關於MSL時長

任何TCP實現必須選擇一個MSL值,有兩種規範一種是一個MSL是2分鐘,另外一種是30秒,所以這就意味著TIME_WAIT狀態的持續時間為1-4分鐘。MSL也是IP資料包在網際網路上能存活的最長時間。另外我們追蹤一個IP包的時候會有跳數概念,最大跳數255,所以即便到達最大跳數也不能超過一個MSL時間。

那為什麼等2個MSL而不是1個或者3個呢?

IP資料包的最大存活時長是1個MSL,如果1個MSL內客戶端傳送的ACK丟了那麼伺服器肯定會重發最後一個FIN,而最後一個FIN這個資料包的最大存活時間也是1個MSL,所以客戶端如果在2MSL時間內又收到伺服器發來的FIN,就表示之前客戶端傳送的ACK確實丟了,這時候客戶端會再次傳送ACK。所以設定為1個太短,3個沒必要。

關於TIME_WAIT的作用

  1. 客戶端傳送最後的ACK以後,如果伺服器端收不到那麼伺服器會重發FIN,所以在1一個MSL時間內是為了確保客戶端可以收到伺服器重發的FIN(如果有必要)
  2. 如果在1MSL內伺服器沒有重發FIN,表示伺服器已經收到客戶端最後的ACK,那麼也就表示雙方都正常關閉了連線
  3. 在TIME_WATI階段,主動關閉的一方不能重新建立一個和之前一樣的連線,這是為了避免造成屬於上一個連線的資料被歸屬到這次新連線,所以經過2MSL之後屬於之前連線的資料包能到的也就到了不能到的也就被丟棄了,同時對於伺服器來說它的應答資料也最多存活1MSL。

為什麼上圖的主動關閉是客戶端

其實任何一方都可以主動關閉,只是我們在實際過程中雙方通訊肯定有一方作為客戶端角色一方作為伺服器角色存在,所以這裡只是一個角色並不是說伺服器不能主動關閉。在這裡你需要牢記的是主動關閉和被動關閉的一方各自都會有什麼狀態。

相關文章