1. 程式人生 > >c 套接字程式設計中的time_wait close_wait解決

c 套接字程式設計中的time_wait close_wait解決

在進行伺服器維護的時候,發現後臺有大量程序處於TIME_WAIT狀態。

如:netstat -antp

tcp        0      0 10.1.10.147:52759           10.1.10.147:10086           TIME_WAIT  

而此時客戶端在連線伺服器的時候,出現很卡的現象。重啟伺服器程式(不重啟機器)後,卡頓現象消失,TIME_WAIT狀態的程式消失。在網路上查詢後,得知TIME_WAIT會佔用系統套介面資源,導致客戶端在連線伺服器的時候,套接字不夠用。

那麼什麼是TIME_WAIT狀態?

time_wait狀態是指套接字程式設計中,主動呼叫close關閉連線一方所處的狀態。與TIME_wai類似的還有以下幾種狀態:

TIME_WAIT     主動呼叫close一方的狀態

CLOSE_WAIT 被動關閉一方的狀態,相對於主動呼叫close方。

FIN_WAIT_1 執行close時,己方傳送結束訊號的狀態

FIN_WAIT_2 被動方同意主動方關閉的狀態

一個典型的TCP連線終止可以用圖描述如下: 

client                                               server 

                                           fin M
close FIN_WAIT_1 ----------------->close_wait

(被動關閉)            

                               ack m+1           
FIN_WAIT_2       <-----------------read 返回0

                                           fin n
TIME_WAIT        <----------------- close

ACK N+1 
                           -----------------> closed

首先客戶端呼叫close(傳送fin m訊號,並處於fin_wait_1狀態),因此客戶端最後會處於TIME_WAIT狀態。

此時伺服器端會接收到資料,此時呼叫read讀取資料,read會返回0.表示對方已經執行了關閉操作。此時伺服器端可以呼叫close,向客戶端傳送fin n訊號。

在程式設計上,客戶端呼叫close以後的所有工作就歸作業系統管理了,此時客戶端的作業系統會向伺服器傳送ack n+1的確認訊號,讓伺服器可以完整的關閉整個連線。這樣整個連線就正常的關閉了。但此時客戶端依然會處於time_wait狀態,因為難免最後的ack n+1訊號會丟失,需要重發。

以上講述了客戶端先呼叫close的情形,在實際情況下,伺服器端也會由於客戶端崩潰或其他機制主動執行close。此時伺服器端也會存在一些程序處於TIME_WAIT狀態。伺服器上如果TIME_WAIT程序較多,就會影響伺服器的效能。因為套接字也是一種系統資源,這種資源總是有限的。此時就需要調整系統引數來降低time_wait的程序數(如果是錯誤的程式設計的話,請先修改程式碼錯誤)。

方法vim /etc/sysctl.conf

#對於一個新建連線,核心要傳送多少個 SYN 連線請求才決定放棄,不應該大於255,預設值是5,對應於180秒左右時間   
net.ipv4.tcp_syn_retries=2  
#net.ipv4.tcp_synack_retries=2  
#表示當keepalive起用的時候,TCP傳送keepalive訊息的頻度。預設是2小時,改為300秒  
net.ipv4.tcp_keepalive_time=1200  
net.ipv4.tcp_orphan_retries=3  
#表示如果套接字由本端要求關閉,這個引數決定了它保持在FIN-WAIT-2狀態的時間  
net.ipv4.tcp_fin_timeout=30    
#表示SYN佇列的長度,預設為1024,加大佇列長度為8192,可以容納更多等待連線的網路連線數。  
net.ipv4.tcp_max_syn_backlog = 4096  
#表示開啟SYN Cookies。當出現SYN等待佇列溢位時,啟用cookies來處理,可防範少量SYN攻擊,預設為0,表示關閉  
net.ipv4.tcp_syncookies = 1  
  
#表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線,預設為0,表示關閉  
net.ipv4.tcp_tw_reuse = 1  
#表示開啟TCP連線中TIME-WAIT sockets的快速回收,預設為0,表示關閉  
net.ipv4.tcp_tw_recycle = 1  
  
##減少超時前的探測次數   
net.ipv4.tcp_keepalive_probes=5   
##優化網路裝置接收佇列   
net.core.netdev_max_backlog=3000

修改後使用/sbin/sysctl -p讓引數生效

如果伺服器存在大量的close_wait。則表示程式碼沒有呼叫close來被動關閉連線。請檢查程式碼是否有對這種情況進行處理。。