1. 程式人生 > >通訊系統經驗談TCP連線狀態分析:SYNC_RECV,CLOSE_WAIT,TIME_WAIT

通訊系統經驗談TCP連線狀態分析:SYNC_RECV,CLOSE_WAIT,TIME_WAIT

面試時看到應聘者簡歷中寫精通網路,TCP程式設計,我常問一個問題,TCP建立連線需要幾次握手?95%以上的應聘者都能答對是3次。問TCP斷開連線需要幾次握手,70%的應聘者能答對是4次通訊。再問CLOSE_WAIT,TIME_WAIT是什麼狀態,怎麼產生的,對服務有什麼影響,如何消除?有一部分同學就回答不上來。不是我扣細節,而是在通訊為主的前端伺服器上,必須有能力處理各種TCP狀態。比如統計在本廠的一臺前端機上高峰時間TCP連線的情況,統計命令:

Linux shell程式碼

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 

結果:
這裡寫圖片描述

除了ESTABLISHED,可以看到連線數比較多的幾個狀態是:FIN_WAIT1, TIME_WAIT, CLOSE_WAIT, SYN_RECV和LAST_ACK;下面的文章就這幾個狀態的產生條件、對系統的影響以及處理方式進行簡單描述。

TCP狀態
TCP狀態如下圖所示:
這裡寫圖片描述

可能有點眼花繚亂?再看看這個時序圖
這裡寫圖片描述

下面看下大家一般比較關心的三種TCP狀態
SYN_RECV
服務端收到建立連線的SYN沒有收到ACK包的時候處在SYN_RECV狀態。有兩個相關係統配置:
1,net.ipv4.tcp_synack_retries :INTEGER
預設值是5
對於遠端的連線請求SYN,核心會發送SYN + ACK資料報,以確認收到上一個 SYN連線請求包。這是所謂的三次握手( threeway handshake)機制的第二個步驟。這裡決定核心在放棄連線之前所送出的 SYN+ACK 數目。不應該大於255,預設值是5,對應於180秒左右時間。通常我們不對這個值進行修改,因為我們希望TCP連線不要因為偶爾的丟包而無法建立。
2,net.ipv4.tcp_syncookies


一般伺服器都會設定net.ipv4.tcp_syncookies=1來防止SYN Flood攻擊。假設一個使用者向伺服器傳送了SYN報文後突然宕機或掉線,那麼伺服器在發出SYN+ACK應答報文後是無法收到客戶端的ACK報文的(第三次握手無法完成),這種情況下伺服器端一般會重試(再次傳送SYN+ACK給客戶端)並等待一段時間後丟棄這個未完成的連線,這段時間的長度我們稱為SYN Timeout,一般來說這個時間是分鐘的數量級(大約為30秒-2分鐘)。

這些處在SYNC_RECV的TCP連線稱為半連線,並存儲在核心的半連線佇列中,在核心收到對端傳送的ack包時會查詢半連線佇列,並將符合的requst_sock資訊儲存到完成三次握手的連線的佇列中,然後刪除此半連線。大量SYNC_RECV的TCP連線會導致半連線佇列溢位,這樣後續的連線建立請求會被核心直接丟棄,這就是SYN Flood攻擊。

能夠有效防範SYN Flood攻擊的手段之一,就是SYN Cookie。SYN Cookie原理由D. J. Bernstain和 Eric Schenk發明。SYN Cookie是對TCP伺服器端的三次握手協議作一些修改,專門用來防範SYN Flood攻擊的一種手段。它的原理是,在TCP伺服器收到TCP SYN包並返回TCP SYN+ACK包時,不分配一個專門的資料區,而是根據這個SYN包計算出一個cookie值。在收到TCP ACK包時,TCP伺服器在根據那個cookie值檢查這個TCP ACK包的合法性。如果合法,再分配專門的資料區進行處理未來的TCP連線。

觀測服務上SYN_RECV連線個數為:7314,對於一個高併發連線的通訊伺服器,這個數字比較正常。

CLOSE_WAIT
發起TCP連線關閉的一方稱為client,被動關閉的一方稱為server。被動關閉的server收到FIN後,但未發出ACK的TCP狀態是CLOSE_WAIT。出現這種狀況一般都是由於server端程式碼的問題,如果你的伺服器上出現大量CLOSE_WAIT,應該要考慮檢查程式碼。

TIME_WAIT
根據TCP協議定義的3次握手斷開連線規定,發起socket主動關閉的一方 socket將進入TIME_WAIT狀態。TIME_WAIT狀態將持續2個MSL(Max Segment Lifetime),在Windows下預設為4分鐘,即240秒。TIME_WAIT狀態下的socket不能被回收使用. 具體現象是對於一個處理大量短連線的伺服器,如果是由伺服器主動關閉客戶端的連線,將導致伺服器端存在大量的處於TIME_WAIT狀態的socket, 甚至比處於Established狀態下的socket多的多,嚴重影響伺服器的處理能力,甚至耗盡可用的socket,停止服務。

為什麼需要TIME_WAIT?TIME_WAIT是TCP協議用以保證被重新分配的socket不會受到之前殘留的延遲重發報文影響的機制,是必要的邏輯保證。

和TIME_WAIT狀態有關的系統引數有一般由3個,本廠設定如下:
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30

net.ipv4.tcp_fin_timeout,預設60s,減小fin_timeout,減少TIME_WAIT連線數量。

net.ipv4.tcp_tw_reuse = 1表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線,預設為0,表示關閉;
net.ipv4.tcp_tw_recycle = 1表示開啟TCP連線中TIME-WAIT sockets的快速回收,預設為0,表示關閉。