Linux 查看tcp 請求 中 的各個 狀態 數據 ,如timewait

netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'

會得到類似下面的結果,具體數字會有所不同:

LAST_ACK 1
SYN_RECV 14
ESTABLISHED 79
FIN_WAIT1 28
FIN_WAIT2 3
CLOSING 5
TIME_WAIT 1669

狀態:描述
CLOSED:無連接是活動的或正在進行
LISTEN:服務器在等待進入呼叫
SYN_RECV:一個連接請求已經到達,等待確認
SYN_SENT:應用已經開始,打開一個連接
ESTABLISHED:正常數據傳輸狀態
FIN_WAIT1:應用說它已經完成
FIN_WAIT2:另一邊已同意釋放
ITMED_WAIT:等待所有分組死掉
CLOSING:兩邊同時嘗試關閉
TIME_WAIT:另一邊已初始化一個釋放
LAST_ACK:等待所有分組死掉

也就是說,這條命令可以把當前系統的網絡連接狀態分類彙總。

下面解釋一下爲啥要這樣寫:

一個簡單的管道符連接了netstat和awk命令。

------------------------------------------------------------------

每個TCP報文在網絡內的最長時間,就稱爲MSL(Maximum Segment Lifetime),它的作用和IP數據包的TTL類似。

RFC793指出,MSL的值是2分鐘,但是在實際的實現中,常用的值有以下三種:30秒,1分鐘,2分鐘。

注意一個問題,進入TIME_WAIT狀態的一般情況下是客戶端,大多數服務器端一般執行被動關閉,不會進入TIME_WAIT狀態,當在服務

器端關閉某個服務再重新啓動時,它是會進入TIME_WAIT狀態的。

舉例:
1.客戶端連接服務器的80服務,這時客戶端會啓用一個本地的端口訪問服務器的80,訪問完成後關閉此連接,立刻再次訪問服務器的

80,這時客戶端會啓用另一個本地的端口,而不是剛纔使用的那個本地端口。原因就是剛纔的那個連接還處於TIME_WAIT狀態。
2.客戶端連接服務器的80服務,這時服務器關閉80端口,立即再次重啓80端口的服務,這時可能不會成功啓動,原因也是服務器的連

接還處於TIME_WAIT狀態。


檢查net.ipv4.tcp_tw當前值,將當前的值更改爲1分鐘:
[root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_tw_recycle = 0
[root@aaa1 ~]#

vi /etc/sysctl
增加或修改net.ipv4.tcp_tw值:
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

使內核參數生效:
[root@aaa1 ~]# sysctl -p

[root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

用netstat再觀察正常


這裏解決問題的關鍵是如何能夠重複利用time_wait的值,我們可以設置時檢查一下time和wait的值
#sysctl -a | grep time | grep wait
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait = 120

問一下TIME_WAIT有什麼問題,是閒置而且內存不回收嗎?

是的,這樣的現象實際是正常的,有時和訪問量大有關,設置這兩個參數: reuse是表示是否允許重新應用處於TIME-WAIT狀態的

socket用於新的TCP連接; recyse是加速TIME-WAIT sockets回收

 

Q: 我正在寫一個unix server程序,不是daemon,經常需要在命令行上重啓它,絕大 
多數時候工作正常,但是某些時候會報告"bind: address in use",於是重啓失 
敗。 

A: Andrew Gierth 
server程序總是應該在調用bind()之前設置SO_REUSEADDR套接字選項。至於 
TIME_WAIT狀態,你無法避免,那是TCP協議的一部分。 

Q: 如何避免等待60秒之後才能重啓服務 

A: Erik Max Francis 

使用setsockopt,比如 

-------------------------------------------------------------------------- 
int option = 1; 

if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option, 
sizeof( option ) ) < 0 ) 

die( "setsockopt" ); 

-------------------------------------------------------------------------- 

Q: 編寫 TCP/SOCK_STREAM 服務程序時,SO_REUSEADDR到底什麼意思? 

A: 這個套接字選項通知內核,如果端口忙,但TCP狀態位於 TIME_WAIT ,可以重用 
端口。如果端口忙,而TCP狀態位於其他狀態,重用端口時依舊得到一個錯誤信息, 
指明"地址已經使用中"。如果你的服務程序停止後想立即重啓,而新套接字依舊 
使用同一端口,此時 SO_REUSEADDR 選項非常有用。必須意識到,此時任何非期 
望數據到達,都可能導致服務程序反應混亂,不過這只是一種可能,事實上很不 
可能。 

一個套接字由相關五元組構成,協議、本地地址、本地端口、遠程地址、遠程端 
口。SO_REUSEADDR 僅僅表示可以重用本地本地地址、本地端口,整個相關五元組 
還是唯一確定的。所以,重啓後的服務程序有可能收到非期望數據。必須慎重使 
用 SO_REUSEADDR 選項。 

Q: 在客戶機/服務器編程中(TCP/SOCK_STREAM),如何理解TCP自動機 TIME_WAIT 狀 
態? 

A:   MSL(最大分段生存期)指明TCP報文在Internet上最長生存時間,每個具體的TCP實現 
都必須選擇一個確定的MSL值。RFC 1122建議是2分鐘,但BSD傳統實現採用了30秒。 

TIME_WAIT 狀態最大保持時間是2 * MSL,也就是1-4分鐘。 

IP頭部有一個TTL,最大值255。儘管TTL的單位不是秒(根本和時間無關),我們仍需 
假設,TTL爲255的TCP報文在Internet上生存時間不能超過MSL。 

TCP報文在傳送過程中可能因爲路由故障被迫緩衝延遲、選擇非最優路徑等等,結果 
發送方TCP機制開始超時重傳。前一個TCP報文可以稱爲"漫遊TCP重複報文",後一個 
TCP報文可以稱爲"超時重傳TCP重複報文",作爲面向連接的可靠協議,TCP實現必須 
正確處理這種重複報文,因爲二者可能最終都到達。 

一個通常的TCP連接終止可以用圖描述如下: 

client server 
FIN M 
close -----------------> (被動關閉) 
ACK M+1 
<----------------- 
FIN N 
<----------------- close 
ACK N+1 
-----------------> 

爲什麼需要 TIME_WAIT 狀態? 

假設最終的ACK丟失,server將重發FIN,client必須維護TCP狀態信息以便可以重發 
最終的ACK,否則會發送RST,結果server認爲發生錯誤。TCP實現必須可靠地終止連 
接的兩個方向(全雙工關閉),client必須進入 TIME_WAIT 狀態,因爲client可能面 
臨重發最終ACK的情形。 


scz 2001-08-31 13:28 

先調用close()的一方會進入TIME_WAIT狀態 


此外,考慮一種情況,TCP實現可能面臨先後兩個同樣的相關五元組。如果前一個連 
接處在 TIME_WAIT 狀態,而允許另一個擁有相同相關五元組的連接出現,可能處理 
TCP報文時,兩個連接互相干擾。使用 SO_REUSEADDR 選項就需要考慮這種情況。 

爲什麼 TIME_WAIT 狀態需要保持 2MSL 這麼長的時間? 

如果 TIME_WAIT 狀態保持時間不足夠長(比如小於2MSL),第一個連接就正常終止了。 
第二個擁有相同相關五元組的連接出現,而第一個連接的重複報文到達,干擾了第二 
個連接。TCP實現必須防止某個連接的重複報文在連接終止後出現,所以讓TIME_WAIT 
狀態保持時間足夠長(2MSL),連接相應方向上的TCP報文要麼完全響應完畢,要麼被 
丟棄。建立第二個連接的時候,不會混淆。 

A: 小四 

在Solaris 7下有內核參數對應 TIME_WAIT 狀態保持時間 

# ndd -get /dev/tcp tcp_time_wait_interval 
240000 
# ndd -set /dev/tcp tcp_time_wait_interval 1000 

缺省設置是240000ms,也就是4分鐘。如果用ndd修改這個值,最小隻能設置到1000ms, 
也就是1秒。顯然內核做了限制,需要Kernel Hacking。 

# echo "tcp_param_arr/W 0t0" | adb -kw /dev/ksyms /dev/mem 
physmem 3b72 
tcp_param_arr: 0x3e8 = 0x0 
# ndd -set /dev/tcp tcp_time_wait_interval 0 

我不知道這樣做有什麼災難性後果,參看<>的聲明。 

Q: TIME_WAIT 狀態保持時間爲0會有什麼災難性後果?在普遍的現實應用中,好象也 
就是服務器不穩定點,不見得有什麼災難性後果吧? 

D: [email protected] 

Linux 內核源碼 /usr/src/linux/include/net/tcp.h 中 

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully 
* close the socket, about 60 seconds */ 

最好不要改爲0,改成1。端口分配是從上一次分配的端口號+1開始分配的,所以一般 
不會有什麼問題。端口分配算法在tcp_ipv4.c中tcp_v4_get_port中。

二、服務器出現TIME_WAIT狀態

到此爲止,都是從內核上修改保持TIME_WAIT狀態的時間參數來保證正確的進行,一般情況下出現TIME_WAIT狀態的都是客戶端,在業務邏輯中儘量讓客戶端主動關閉連接,這樣也就將TIME_WAIT變相的轉載了。但是在有些業務中必須讓服務器主動關閉連接。

       對於基於TCP的HTTP協議,關閉TCP連接的是Server端,這樣,Server端會進入TIME_WAIT狀態,可想而知,對於訪問量大的Web Server,會存在大量的TIME_WAIT狀態,假如server一秒鐘接收1000個請求,那麼就會積壓240*1000=240,000個TIME_WAIT的記錄,維護這些狀態給Server帶來負擔。當然現代操作系統都會用快速的查找算法來管理這些TIME_WAIT,所以對於新的TCP連接請求,判斷是否hit中一個TIME_WAIT不會太費時間,但是有這麼多狀態要維護總是不好。

        HTTP協議1.1版規定default行爲是Keep-Alive,也就是會重用TCP連接傳輸多個request/response,一個主要原因就是發現了這個問題。還有一個方法減緩TIME_WAIT壓力就是把系統的2*MSL時間減少,因爲240秒的時間實在是忒長了點,對於Windows,修改註冊表,在HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\ Tcpip\Parameters上添加一個DWORD類型的值TcpTimedWaitDelay,一般認爲不要少於60,不然可能會有麻煩。

       對於大型的服務,一臺server搞不定,需要一個LB(Load Balancer)把流量分配到若干後端服務器上,如果這個LB是以NAT方式工作的話,可能會帶來問題。假如所有從LB到後端Server的IP包的source address都是一樣的(LB的對內地址),那麼LB到後端Server的TCP連接會受限制,因爲頻繁的TCP連接建立和關閉,會在server上留下TIME_WAIT狀態,而且這些狀態對應的remote address都是LB的,LB的source port撐死也就60000多個(2^16=65536,1~1023是保留端口,還有一些其他端口缺省也不會用),每個LB上的端口一旦進入Server的TIME_WAIT黑名單,就有240秒不能再用來建立和Server的連接,這樣LB和Server最多也就能支持300個左右的連接。如果沒有LB,不會有這個問題,因爲這樣server看到的remote address是internet上廣闊無垠的集合,對每個address,60000多個port實在是夠用了。 

       一開始我覺得用上LB會很大程度上限制TCP的連接數,但是實驗表明沒這回事,LB後面的一臺Windows Server 2003每秒處理請求數照樣達到了600個,難道TIME_WAIT狀態沒起作用?用Net Monitor和netstat觀察後發現,Server和LB的XXXX端口之間的連接進入TIME_WAIT狀態後,再來一個LB的XXXX端口的SYN包,Server照樣接收處理了,而是想像的那樣被drop掉了。翻書,從書堆裏面找出覆滿塵土的大學時代買的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》,中間提到一句,對於BSD-derived實現,只要SYN的sequence number比上一次關閉時的最大sequence number還要大,那麼TIME_WAIT狀態一樣接受這個SYN,難不成Windows也算BSD-derived?有了這點線索和關鍵字(BSD),找到這個post,在NT4.0的時候,還是和BSD-derived不一樣的,不過Windows Server 2003已經是NT5.2了,也許有點差別了。

        做個試驗,用Socket API編一個Client端,每次都Bind到本地一個端口比如2345,重複的建立TCP連接往一個Server發送Keep-Alive=false的HTTP請求,Windows的實現讓sequence number不斷的增長,所以雖然Server對於Client的2345端口連接保持TIME_WAIT狀態,但是總是能夠接受新的請求,不會拒絕。那如果SYN的Sequence Number變小會怎麼樣呢?同樣用Socket API,不過這次用Raw IP,發送一個小sequence number的SYN包過去,Net Monitor裏面看到,這個SYN被Server接收後如泥牛如海,一點反應沒有,被drop掉了。

       按照書上的說法,BSD-derived和Windows Server 2003的做法有安全隱患,不過至少這樣至少不會出現TIME_WAIT阻止TCP請求的問題,當然,客戶端要配合,保證不同TCP連接的sequence number要上漲不要下降。

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