如何解決TIME_WAIT過多的解決辦法(附Socket中的TIME_WAIT狀態詳解)

linux和windows下TIME_WAIT過多的解決辦法

如果使用了nginx代理,那麼系統TIME_WAIT的數量會變得比較多,這是由於nginx代理使用了短鏈接的方式和後端交互的原因,使得nginx和後端的ESTABLISHED變得很少而TIME_WAIT很多。這不但發生在安裝nginx的代理服務器上,而且也會使後端的app服務器上有大量的TIME_WAIT。查閱TIME_WAIT資料,發現這個狀態很多也沒什麼大問題,但可能因爲它佔用了系統過多的端口,導致後續的請求無法獲取端口而造成障礙。

雖然TIME_WAIT會造成一些問題,但是要完全槍斃掉它也是不正當的,雖然看起來這麼做沒什麼錯。具體可看這篇文檔:

http://hi.baidu.com/tim_bi/blog/item/35b005d784ca91d5a044df1d.html

所以目前看來最好的辦法是讓每個TIME_WAIT早點過期。

在linux上可以這麼配置:

#讓TIME_WAIT狀態可以重用,這樣即使TIME_WAIT佔滿了所有端口,也不會拒絕新的請求造成障礙
echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
#讓TIME_WAIT儘快回收,我也不知是多久,觀察大概是一秒鐘
echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle

很多文檔都會建議兩個參數都配置上,但是我發現只用修改tcp_tw_recycle就可以解決問題的了,TIME_WAIT重用TCP協議本身就是不建議打開的。

不能重用端口可能會造成系統的某些服務無法啓動,比如要重啓一個系統監控的軟件,它用了40000端口,而這個端口在軟件重啓過程中剛好被使用了,就可能會重啓失敗的。linux默認考慮到了這個問題,有這麼個設定:

#查看系統本地可用端口極限值
cat /proc/sys/net/ipv4/ip_local_port_range

用這條命令會返回兩個數字,默認是:32768 61000,說明這臺機器本地能向外連接61000-32768=28232個連接,注意是本地向外連接,不是這臺機器的所有連接,不會影響這臺機器的80端口的對外連接數。但這個數字會影響到代理服務器(nginx)對app服務器的最大連接數,因爲nginx對app是用的異步傳輸,所以這個環節的連接速度很快,所以堆積的連接就很少。假如nginx對app服務器之間的帶寬出了問題或是app服務器有問題,那麼可能使連接堆積起來,這時可以通過設定nginx的代理超時時間,來使連接儘快釋放掉,一般來說極少能用到28232個連接。

因爲有軟件使用了40000端口監聽,常常出錯的話,可以通過設定ip_local_port_range的最小值來解決:

echo "40001 61000" > /proc/sys/net/ipv4/ip_local_port_range

但是這麼做很顯然把系統可用端口數減少了,這時可以把ip_local_port_range的最大值往上調,但是好習慣是使用不超過32768的端口來偵聽服務,另外也不必要去修改ip_local_port_range數值成1024 65535之類的,意義不大。

因爲使用了nginx代理,在windows下也會造成大量TIME_WAIT,當然windows也可以調整:

在註冊表(regedit)的HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters上添加一個DWORD類型的值TcpTimedWaitDelay,值就是秒數,即可。

windows默認是重用TIME_WAIT,我現在還不知道怎麼改成不重用的,本地端口也沒查到是什麼值,但這些都關係不大,都可以按系統默認運作。

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

TIME_WAIT狀態

根據TCP協議,主動發起關閉的一方,會進入TIME_WAIT狀態,持續2*MSL(Max Segment Lifetime),缺省爲240秒,在這個post中簡潔的介紹了爲什麼需要這個狀態。

值得一說的是,對於基於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要上漲不要下降。

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

Socket中的TIME_WAIT狀態

在高併發短連接的server端,當server處理完client的請求後立刻closesocket此時會出現time_wait狀態然後如果client再併發2000個連接,此時部分連接就連接不上了,用linger強制關閉可以解決此問題,但是linger會導致數據丟失,linger值爲0時是強制關閉,無論併發多少多能正常連接上,如果非0會發生部分連接不上的情況!(可調用setsockopt設置套接字的linger延時標誌,同時將延時時間設置爲0。
TCP/IP的RFC文檔。TIME_WAIT是TCP連接斷開時必定會出現的狀態。
是無法避免掉的,這是TCP協議實現的一部分。
在WINDOWS下,可以修改註冊表讓這個時間變短一些

time_wait的時間爲2msl,默認爲4min.
你可以通過改變這個變量:
TcpTimedWaitDelay
把它縮短到30s
TCP要保證在所有可能的情況下使得所有的數據都能夠被投遞。當你關閉一個socket時,主動關閉一端的socket將進入TIME_WAIT狀態,而被動關閉一方則轉入CLOSED狀態,這的確能夠保證所有的數據都被傳輸。當一個socket關閉的時候,是通過兩端互發信息的四次握手過程完成的,當一端調用close()時,就說明本端沒有數據再要發送了。這好似看來在握手完成以後,socket就都應該處於關閉CLOSED狀態了。但這有兩個問題,首先,我們沒有任何機制保證最後的一個ACK能夠正常傳輸,第二,網絡上仍然有可能有殘餘的數據包(wandering duplicates),我們也必須能夠正常處理。
通過正確的狀態機,我們知道雙方的關閉過程如下

假設最後一個ACK丟失了,服務器會重發它發送的最後一個FIN,所以客戶端必須維持一個狀態信息,以便能夠重發ACK;如果不維持這種狀態,客戶端在接收到FIN後將會響應一個RST,服務器端接收到RST後會認爲這是一個錯誤。如果TCP協議能夠正常完成必要的操作而終止雙方的數據流傳輸,就必須完全正確的傳輸四次握手的四個節,不能有任何的丟失。這就是爲什麼socket在關閉後,仍然處於 TIME_WAIT狀態,因爲他要等待以便重發ACK。

如果目前連接的通信雙方都已經調用了close(),假定雙方都到達CLOSED狀態,而沒有TIME_WAIT狀態時,就會出現如下的情況。現在有一個新的連接被建立起來,使用的IP地址與端口與先前的完全相同,後建立的連接又稱作是原先連接的一個化身。還假定原先的連接中有數據報殘存於網絡之中,這樣新的連接收到的數據報中有可能是先前連接的數據報。爲了防止這一點,TCP不允許從處於TIME_WAIT狀態的socket建立一個連接。處於TIME_WAIT狀態的socket在等待兩倍的MSL時間以後(之所以是兩倍的MSL,是由於MSL是一個數據報在網絡中單向發出到認定丟失的時間,一個數據報有可能在發送圖中或是其響應過程中成爲殘餘數據報,確認一個數據報及其響應的丟棄的需要兩倍的MSL),將會轉變爲CLOSED狀態。這就意味着,一個成功建立的連接,必然使得先前網絡中殘餘的數據報都丟失了。

由於TIME_WAIT狀態所帶來的相關問題,我們可以通過設置SO_LINGER標誌來避免socket進入TIME_WAIT狀態,這可以通過發送RST而取代正常的TCP四次握手的終止方式。但這並不是一個很好的主意,TIME_WAIT對於我們來說往往是有利的。

 

 

客戶端與服務器端建立TCP/IP連接後關閉SOCKET後,服務器端連接的端口
狀態爲TIME_WAIT
是不是所有執行主動關閉的socket都會進入TIME_WAIT狀態呢?
有沒有什麼情況使主動關閉的socket直接進入CLOSED狀態呢?
主動關閉的一方在發送最後一個 ack 後
就會進入 TIME_WAIT 狀態 停留2MSL(max segment lifetime)時間
這個是TCP/IP必不可少的,也就是“解決”不了的。

也就是TCP/IP設計者本來是這麼設計的
主要有兩個原因
1。防止上一次連接中的包,迷路後重新出現,影響新連接
   (經過2MSL,上一次連接中所有的重複包都會消失)
2。可靠的關閉TCP連接
   在主動關閉方發送的最後一個 ack(fin) ,有可能丟失,這時被動方會重新發
   fin, 如果這時主動方處於 CLOSED 狀態 ,就會響應 rst 而不是 ack。所以
   主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。

TIME_WAIT 並不會佔用很大資源的,除非受到攻擊。

還有,如果一方 send 或 recv 超時,就會直接進入 CLOSED 狀態

 

socket-faq中的這一段講的也很好,摘錄如下:
2.7. Please explain the TIME_WAIT state.

Remember that TCP guarantees all data transmitted will be delivered,
if at all possible. When you close a socket, the server goes into a
TIME_WAIT state, just to be really really sure that all the data has
gone through. When a socket is closed, both sides agree by sending
messages to each other that they will send no more data. This, it
seemed to me was good enough, and after the handshaking is done, the
socket should be closed. The problem is two-fold. First, there is no
way to be sure that the last ack was communicated successfully.
Second, there may be "wandering duplicates" left on the net that must
be dealt with if they are delivered.

Andrew Gierth ([email protected]) helped to explain the
closing sequence in the following usenet posting:

Assume that a connection is in ESTABLISHED state, and the client is
about to do an orderly release. The client's sequence no. is Sc, and
the server's is Ss. Client Server
====== ======
ESTABLISHED ESTABLISHED
(client closes)
ESTABLISHED ESTABLISHED
------->>
FIN_WAIT_1
<<--------
FIN_WAIT_2 CLOSE_WAIT
<<-------- (server closes)
LAST_ACK
, ------->>
TIME_WAIT CLOSED
(2*msl elapses...)
CLOSED

Note: the +1 on the sequence numbers is because the FIN counts as one
byte of data. (The above diagram is equivalent to fig. 13 from RFC
793).

Now consider what happens if the last of those packets is dropped in
the network. The client has done with the connection; it has no more
data or control info to send, and never will have. But the server does
not know whether the client received all the data correctly; that's
what the last ACK segment is for. Now the server may or may not care
whether the client got the data, but that is not an issue for TCP; TCP
is a reliable rotocol, and must distinguish between an orderly
connection close where all data is transferred, and a connection abort
where data may or may not have been lost.

So, if that last packet is dropped, the server will retransmit it (it
is, after all, an unacknowledged segment) and will expect to see a
suitable ACK segment in reply. If the client went straight to CLOSED,
the only possible response to that retransmit would be a RST, which
would indicate to the server that data had been lost, when in fact it
had not been.

(Bear in mind that the server's FIN segment may, additionally, contain
data.)

DISCLAIMER: This is my interpretation of the RFCs (I have read all the
TCP-related ones I could find), but I have not attempted to examine
implementation source code or trace actual connections in order to
verify it. I am satisfied that the logic is correct, though.

More commentarty from Vic:

The second issue was addressed by Richard Stevens ([email protected],
author of "Unix Network Programming", see ``1.5 Where can I get source
code for the book [book title]?''). I have put together quotes from
some of his postings and email which explain this. I have brought
together paragraphs from different postings, and have made as few
changes as possible.

From Richard Stevens ([email protected]):

If the duration of the TIME_WAIT state were just to handle TCP's full-
duplex close, then the time would be much smaller, and it would be
some function of the current RTO (retransmission timeout), not the MSL
(the packet lifetime).

A couple of points about the TIME_WAIT state.

o The end that sends the first FIN goes into the TIME_WAIT state,
because that is the end that sends the final ACK. If the other
end's FIN is lost, or if the final ACK is lost, having the end that
sends the first FIN maintain state about the connection guarantees
that it has enough information to retransmit the final ACK.

o Realize that TCP sequence numbers wrap around after 2**32 bytes
have been transferred. Assume a connection between A.1500 (host A,
port 1500) and B.2000. During the connection one segment is lost
and retransmitted. But the segment is not really lost, it is held
by some intermediate router and then re-injected into the network.
(This is called a "wandering duplicate".) But in the time between
the packet being lost & retransmitted, and then reappearing, the
connection is closed (without any problems) and then another
connection is established between the same host, same port (that
is, A.1500 and B.2000; this is called another "incarnation" of the
connection). But the sequence numbers chosen for the new
incarnation just happen to overlap with the sequence number of the
wandering duplicate that is about to reappear. (This is indeed
possible, given the way sequence numbers are chosen for TCP
connections.) Bingo, you are about to deliver the data from the
wandering duplicate (the previous incarnation of the connection) to
the new incarnation of the connection. To avoid this, you do not
allow the same incarnation of the connection to be reestablished
until the TIME_WAIT state terminates.

Even the TIME_WAIT state doesn't complete solve the second problem,
given what is called TIME_WAIT assassination. RFC 1337 has more
details.

o The reason that the duration of the TIME_WAIT state is 2*MSL is
that the maximum amount of time a packet can wander around a
network is assumed to be MSL seconds. The factor of 2 is for the
round-trip. The recommended value for MSL is 120 seconds, but
Berkeley-derived implementations normally use 30 seconds instead.
This means a TIME_WAIT delay between 1 and 4 minutes. Solaris 2.x
does indeed use the recommended MSL of 120 seconds.

A wandering duplicate is a packet that appeared to be lost and was
retransmitted. But it wasn't really lost ... some router had
problems, held on to the packet for a while (order of seconds, could
be a minute if the TTL is large enough) and then re-injects the packet
back into the network. But by the time it reappears, the application
that sent it originally has already retransmitted the data contained
in that packet.

Because of these potential problems with TIME_WAIT assassinations, one
should not avoid the TIME_WAIT state by setting the SO_LINGER option
to send an RST instead of the normal TCP connection termination
(FIN/ACK/FIN/ACK). The TIME_WAIT state is there for a reason; it's
your friend and it's there to help you :-)

I have a long discussion of just this topic in my just-released
"TCP/IP Illustrated, Volume 3". The TIME_WAIT state is indeed, one of
the most misunderstood features of TCP.

I'm currently rewriting "Unix Network Programming" (see ``1.5 Where
can I get source code for the book [book title]?''). and will include
lots more on this topic, as it is often confusing and misunderstood.

An additional note from Andrew:

Closing a socket: if SO_LINGER has not been called on a socket, then
close() is not supposed to discard data. This is true on SVR4.2 (and,
apparently, on all non-SVR4 systems) but apparently not on SVR4; the
use of either shutdown() or SO_LINGER seems to be required to
guarantee delivery of all data.

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

討厭的 Socket TIME_WAIT 問題

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: W. Richard Stevens <1999年逝世,享年49歲>

下面我來解釋一下 TIME_WAIT 狀態,這些在<>
中2.6節解釋很清楚了。

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中
發佈了4 篇原創文章 · 獲贊 13 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章