理解TIME_WAIT

理解TIME_WAIT
轉自http://www.firefoxbug.com/index.php/archives/2795/

前言

TIME_WAIT 是在TCP協議中很模糊的概念,它可能使socke能陷入的一種時間相對比較長的狀態,過多的TIME_WAIT會影響新socket的建立。TIME_WAIT爲什麼會存在?它的作用又是什麼?下面我們就來理解下TIME_WAIT。
這裏寫圖片描述
這張圖詳細的列出了TCP建立連接和斷開連接的各個TCP狀態之間的轉換。紅色的代表server,藍色的代表client。下面列出各自的TCP狀態轉換條件

TCP建立連接

Client: 向server發送 SYN 包,表示請求建立連接,進入 SYN_SENT 狀態;
Server: 接收來自client的 SYN 包,發送 SYN/ACK 包,代表client->server單向tcp連接已經建立, 進入 SYN_RCVD 狀態;
Client: 接收到來自server的 SYN/ACK 包,發送給server ACK 包,進入 Established 狀態;
Server: 收到client的 ACK 包,代表 server->client 的單向tcp連接也建立,此時進入 Established 狀態;
TCP斷開連接

先引入兩個概念,首先調用close()是”主動關閉”(active close),另一個是”被動關閉”(passive close)。一般我們連上ftp或者http,斷開連接的都是客戶端。看上面的圖,”主動關閉”端狀態要經歷3個狀態,而TIME_WAIT是屬於“主動關閉”端最後的一個tcp狀態。

這裏寫圖片描述

client: 主動調用close(),發送 FIN 包,此時client就”主動關閉”端,進入 FIN_WAIT_1 狀態;
Server: server自然成爲”被動關閉”端,收到來自client的 FIN 包,發送 ACK 包,代表client->server單向tcp連接已經關閉,進入 CLOSE_WAIT 狀態;
Client: 接收到來自server的 ACK 包,啥都不做,client->server單向的tcp連接已經斷開,不能再發送應用層數據,進入 FIN_WAIT_2 狀態;
Server: server端給client端發送 FIN 包,代表準備關閉server->client的tcp連接,server進入LAST_ACK 狀態;
Client: 收到來自server的 FIN 包,發送 ACK 包,此時進入 TIME_WAIT 狀態;
Server: 收到Client的 ACK 包,就進入closed狀態,Server端此次socket tcp連接完全端口;
Client: 持續TIME_WAIT狀態”一段時間”;
理解TIME_WAIT

理解了上面的原理之後,接着就是正式介紹TIME_WAIT。TIME_WAIT的時間大多數情況下都是2倍的MSL(Maximum Segment Lifetime),MSL是一個數據包在網絡上能生存的最長生命週期,一旦超過MSL的包就會被丟棄。從上面可以看到,TIME_WAIT是“主動關閉”端的最後一個狀態,引入TIME_WAIT的原因有:

  1. 確保”主動關閉”端最後發出的 ACK 到達”被動關閉”端
  2. 保證新tcp連接和老tcp連接不會干擾
    原因1: 確保”主動關閉”端最後發出的 ACK 到達”被動關閉”端

看上面tcp斷開連接的圖,由client主動調用close(),發出FIN包,然後接收到server的ACK/FIN包,客戶端最後發一個FIN包,進入TIME_WAIT。

設想一下,如果沒有TIME_WAIT,client端發送最後的FIN包后里面關閉連接,如果由於網絡原因,最後發出的FIN包沒有順利到達server(此時的server一直處於LAST_ACK狀態等待最後FIN),server長時間沒有接收到FIN包,會認爲之前由server發出的ACK/FIN包client沒有收到,server會重新發送一個ACK/FIN包,這時候client收到ACK/FIN包,發現端口已經關閉,協議棧直接回復RST包,導致server端接收到RST包報錯,影響應用進程。

所以 TIME_WAIT 的作用可以保證最後的ACK包必然能到達對方,確保最後的連接正常端口。也解釋了TIME_WAIT時間是2*MSL的原因。

原因2: 保證新tcp連接和老tcp連接不會干擾

看看下面的圖

這裏寫圖片描述

End Point2發送FIN包後,沒有進入TIME_WAIT狀態,此時新的tcp請求又來了,而且src_ip,src_port,dst_ip,dst_port都是一樣的,新的連接建立TCP請求後,老的連接包可能會干擾新連接的包,導致亂序。所以引入TIME_WAIT,2*MSL能讓老連接的包徹底在網絡中消失,保證新連接絕對乾淨。

TIME_WAIT數量多?

TIME_WAIT是佔資源的,包括端口資源,協議棧隊列,所以大量的TIME_WAIT會影響socket建立新連接,這點特別在高性能的Web服務器中很講究,那麼有辦法去減少TIME_WAIT數量嗎?嘿嘿,看了上面TIME_WAIT存在的原因後,還想去調整tcp_tw_recycle或者tcp_tw_reuse等參數嗎?這很有可能會引發未知的TCP錯誤,而且很詭異,很難排查。所以在高性能的Web服務器裏面,必然會去設置HTTP的KeepAlive,不然Web服務器立馬就被大量的TIME_WAIT影響服務。

調整TIME_WAIT數量

要調整TIME_WAIT的數量,網上都是這幾個參數,要修改的話還是悠着點吧?

net.ipv4.tcp_tw_reuse = 0 表示開啓重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認爲0,表示關閉;打開tcp_tw_reuse的時候要注意,是客戶端還是服務端,如果是
net.ipv4.tcp_tw_recycle = 0 表示開啓TCP連接中TIME-WAIT sockets的快速回收,默認爲0,表示關閉。
net.ipv4.tcp_fin_timeout = 30 表示如果套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間。
net.ipv4.tcp_tw_timeout = 15 標識TIME_WAIT的回收時間。
tcp_tw_recycle

tcp_tw_recycle必須和tcp_timestamps一起打開,默認情況linux的tcp_timestamps都是打開的,tcp_tw_recycle到底是多久回收sockets?正常是700ms。

tcp_tw_recycle的坑:當多個客戶端通過NAT方式聯網並與服務端交互時,服務端看到的是同一個IP,也就是說對服務端而言這些客戶端實際上等同於一個,可惜由於這些客戶端的時間戳可能存在差異,於是乎從服務端的視角看,便可能出現時間戳錯亂的現象,進而直接導致時間戳小的數據包被丟棄

注意:在NAT模型中,tcp_tw_recycle打開可能會導致丟包

tcp_tw_reuse

tcp_tw_reuse選項和tcp_timestamps選項也必須同時打開;
重用TIME_WAIT的條件是收到最後一個包後超過1s
總結

TIME_WATI出現在TCP連接”主動關閉”端,理論上會持續2*MSL(根據不通系統而定,因爲IP有TTL),TIME_WAIT的出現是爲了解決兩個問題

  1. 確保連接能正確斷開(確保”主動關閉”端最後發出的 ACK 到達”被動關閉”端)
  2. 確保新的tcp連接和老的tcp連接不會干擾

更多文章:

tcp短連接TIME_WAIT問題解決方法大全-tcp_tw_recycle

tcp短連接TIME_WAIT問題解決方法大全-tcp_tw_reuse

圖片來源: http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章