TCP/IP TIME_WAIT状态原理
TIME_WAIT狀態(tài)原理
----------------------------
通信雙方建立TCP連接后,主動關閉連接的一方就會進入TIME_WAIT狀態(tài)。
客戶端主動關閉連接時,會發(fā)送最后一個ack后,然后會進入TIME_WAIT狀態(tài),再停留2個MSL時間(后有MSL的解釋),進入CLOSED狀態(tài)。
下圖是以客戶端主動關閉連接為例,說明這一過程的。
?
?
?
TIME_WAIT狀態(tài)存在的理由
----------------------------
TCP/IP協(xié)議就是這樣設計的,是不可避免的。主要有兩個原因:
1)可靠地實現(xiàn)TCP全雙工連接的終止
TCP協(xié)議在關閉連接的四次握手過程中,最終的ACK是由主動關閉連接的一端(后面統(tǒng)稱A端)發(fā)出的,如果這個ACK丟失,對方(后面統(tǒng)稱B端)將重發(fā)出最終的FIN,因此A端必須維護狀態(tài)信息(TIME_WAIT)允許它重發(fā)最終的ACK。如果A端不維持TIME_WAIT狀態(tài),而是處于CLOSED 狀態(tài),那么A端將響應RST分節(jié),B端收到后將此分節(jié)解釋成一個錯誤(在java中會拋出connection reset的SocketException)。
因而,要實現(xiàn)TCP全雙工連接的正常終止,必須處理終止過程中四個分節(jié)任何一個分節(jié)的丟失情況,主動關閉連接的A端必須維持TIME_WAIT狀態(tài) 。
?
2)允許老的重復分節(jié)在網(wǎng)絡中消逝?
TCP分節(jié)可能由于路由器異常而“迷途”,在迷途期間,TCP發(fā)送端可能因確認超時而重發(fā)這個分節(jié),迷途的分節(jié)在路由器修復后也會被送到最終目的地,這個遲到的迷途分節(jié)到達時可能會引起問題。在關閉“前一個連接”之后,馬上又重新建立起一個相同的IP和端口之間的“新連接”,“前一個連接”的迷途重復分組在“前一個連接”終止后到達,而被“新連接”收到了。為了避免這個情況,TCP協(xié)議不允許處于TIME_WAIT狀態(tài)的連接啟動一個新的可用連接,因為TIME_WAIT狀態(tài)持續(xù)2MSL,就可以保證當成功建立一個新TCP連接的時候,來自舊連接重復分組已經(jīng)在網(wǎng)絡中消逝。
?
?
?
?
MSL時間
----------------------------
MSL就是maximum segment lifetime(最大分節(jié)生命期),這是一個IP數(shù)據(jù)包能在互聯(lián)網(wǎng)上生存的最長時間,超過這個時間IP數(shù)據(jù)包將在網(wǎng)絡中消失 。MSL在RFC 1122上建議是2分鐘,而源自berkeley的TCP實現(xiàn)傳統(tǒng)上使用30秒。
?
TIME_WAIT狀態(tài)維持時間
----------------------------
TIME_WAIT狀態(tài)維持時間是兩個MSL時間長度,也就是在1-4分鐘。Windows操作系統(tǒng)就是4分鐘。
?
?
?
?
用于統(tǒng)計當前各種狀態(tài)的連接的數(shù)量的命令
---------------------------
#netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
?
返回結果如下:
LAST_ACK 14
SYN_RECV 348
ESTABLISHED 70
FIN_WAIT1 229
FIN_WAIT2 30
CLOSING 33
TIME_WAIT 18122
?
對上述結果的解釋:
CLOSED:無連接是活動的或正在進行
LISTEN:服務器在等待進入呼叫
SYN_RECV:一個連接請求已經(jīng)到達,等待確認
SYN_SENT:應用已經(jīng)開始,打開一個連接
ESTABLISHED:正常數(shù)據(jù)傳輸狀態(tài)
FIN_WAIT1:應用說它已經(jīng)完成
FIN_WAIT2:另一邊已同意釋放
ITMED_WAIT:等待所有分組死掉
CLOSING:兩邊同時嘗試關閉
TIME_WAIT:另一邊已初始化一個釋放
LAST_ACK:等待所有分組死掉
?
?
進一步論述這個問題:
===============================
?
?
--------------客戶端主動關閉連接-----------------------
注意一個問題,進入TIME_WAIT狀態(tài)的一般情況下是客戶端。
大多數(shù)服務器端一般執(zhí)行被動關閉,服務器不會進入TIME_WAIT狀態(tài)。
當在服務器端關閉某個服務再重新啟動時,服務器是會進入TIME_WAIT狀態(tài)的。
舉例:
1.客戶端連接服務器的80服務,這時客戶端會啟用一個本地的端口訪問服務器的80,訪問完成后關閉此連接,立刻再次訪問服務器的
80,這時客戶端會啟用另一個本地的端口,而不是剛才使用的那個本地端口。原因就是剛才的那個連接還處于TIME_WAIT狀態(tài)。
2.客戶端連接服務器的80服務,這時服務器關閉80端口,立即再次重啟80端口的服務,這時可能不會成功啟動,原因也是服務器的連
接還處于TIME_WAIT狀態(tài)。
?
服務端提供服務時,一般監(jiān)聽一個端口就夠了。例如Apach監(jiān)聽80端口。
客戶端則是使用一個本地的空閑端口(大于1024),與服務端的Apache的80端口建立連接。
當通信時使用短連接,并由客戶端主動關閉連接時,主動關閉連接的客戶端會產(chǎn)生TIME_WAIT狀態(tài)的連接,一個TIME_WAIT狀態(tài)的連接就占用了一個本地端口。這樣在TIME_WAIT狀態(tài)結束之前,本地最多就能承受6萬個TIME_WAIT狀態(tài)的連接,就無端口可用了。
客戶端與服務端進行短連接的TCP通信,如果在同一臺機器上進行壓力測試模擬上萬的客戶請求,并且循環(huán)與服務端進行短連接通信,那么這臺機器將產(chǎn)生4000個左右的TIME_WAIT socket,后續(xù)的短連接就會產(chǎn)生address already in use : connect的異常。
?
關閉的時候使用RST的方式,不進入 TIME_WAIT狀態(tài),是否可行?
?
--------------服務端主動關閉連接------------------------------
服務端提供在服務時,一般監(jiān)聽一個端口就夠了。例如Apach監(jiān)聽80端口。
客戶端則是使用一個本地的空閑端口(大于1024),與服務端的Apache的80端口建立連接。
當通信時使用短連接,并由服務端主動關閉連接時,主動關閉連接的服務端會產(chǎn)生TIME_WAIT狀態(tài)的連接。
由于都連接到服務端80端口,服務端的TIME_WAIT狀態(tài)的連接會有很多個。
假如server一秒鐘處理1000個請求,那么就會積壓240秒*1000=24萬個TIME_WAIT的記錄,服務有能力維護這24萬個記錄。
?
大多數(shù)服務器端一般執(zhí)行被動關閉,服務器不會進入TIME_WAIT狀態(tài)。
服務端為了解決這個TIME_WAIT問題,可選擇的方式有三種:
? ? ? ?保證由客戶端主動發(fā)起關閉(即做為B端)
? ? ? ?關閉的時候使用RST的方式
? ? ? ?對處于TIME_WAIT狀態(tài)的TCP允許重用
?
一般Apache的配置是:
Timeout 30 ?
KeepAlive On ? #表示服務器端不會主動關閉鏈接 ?
MaxKeepAliveRequests 100 ?
KeepAliveTimeout 180 ?
表示:Apache不會主動關閉鏈接,
兩種情況下Apache會主動關閉連接:
1、Apache收到了http協(xié)議頭中有客戶端要求Apache關閉連接信息,如setRequestHeader("Connection", "close"); ?
2、連接保持時間達到了180秒的超時時間,將關閉。
?
如果配置如下:
KeepAlive Off ? #表示服務器端會響應完數(shù)據(jù)后主動關閉鏈接 ?
?
?
--------------有代理時------------------------------
nginx代理使用了短鏈接的方式和后端交互,如果使用了nginx代理,那么系統(tǒng)TIME_WAIT的數(shù)量會變得比較多,這是由于nginx代理使用了短鏈接的方式和后端交互的原因,使得nginx和后端的ESTABLISHED變得很少而TIME_WAIT很多。這不但發(fā)生在安裝nginx的代理服務器上,而且也會使后端的app服務器上有大量的TIME_WAIT。查閱TIME_WAIT資料,發(fā)現(xiàn)這個狀態(tài)很多也沒什么大問題,但可能因為它占用了系統(tǒng)過多的端口,導致后續(xù)的請求無法獲取端口而造成障礙。
?
對于大型的服務,一臺server搞不定,需要一個LB(Load Balancer)把流量分配到若干后端服務器上,如果這個LB是以NAT方式工作的話,可能會帶來問題。假如所有從LB到后端Server的IP包的source address都是一樣的(LB的對內地址),那么LB到后端Server的TCP連接會受限制,因為頻繁的TCP連接建立和關閉,會在server上留下TIME_WAIT狀態(tài),而且這些狀態(tài)對應的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的連接數(shù),但是實驗表明沒這回事,LB后面的一臺Windows Server 2003每秒處理請求數(shù)照樣達到了600個,難道TIME_WAIT狀態(tài)沒起作用?用Net Monitor和netstat觀察后發(fā)現(xiàn),Server和LB的XXXX端口之間的連接進入TIME_WAIT狀態(tài)后,再來一個LB的XXXX端口的SYN包,Server照樣接收處理了,而是想像的那樣被drop掉了。翻書,從書堆里面找出覆滿塵土的大學時代買的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》,中間提到一句,對于BSD-derived實現(xiàn),只要SYN的sequence number比上一次關閉時的最大sequence number還要大,那么TIME_WAIT狀態(tài)一樣接受這個SYN,難不成Windows也算BSD-derived?有了這點線索和關鍵字(BSD),找到這個post,在NT4.0的時候,還是和BSD-derived不一樣的,不過Windows Server 2003已經(jīng)是NT5.2了,也許有點差別了。
做個試驗,用Socket API編一個Client端,每次都Bind到本地一個端口比如2345,重復的建立TCP連接往一個Server發(fā)送Keep-Alive=false的HTTP請求,Windows的實現(xiàn)讓sequence number不斷的增長,所以雖然Server對于Client的2345端口連接保持TIME_WAIT狀態(tài),但是總是能夠接受新的請求,不會拒絕。那如果SYN的Sequence Number變小會怎么樣呢?同樣用Socket API,不過這次用Raw IP,發(fā)送一個小sequence number的SYN包過去,Net Monitor里面看到,這個SYN被Server接收后如泥牛如海,一點反應沒有,被drop掉了。
按照書上的說法,BSD-derived和Windows Server 2003的做法有安全隱患,不過至少這樣至少不會出現(xiàn)TIME_WAIT阻止TCP請求的問題,當然,客戶端要配合,保證不同TCP連接的sequence number要上漲不要下降。
?
-------------------------------------------
Q: 我正在寫一個unix server程序,不是daemon,經(jīng)常需要在命令行上重啟它,絕大?
多數(shù)時候工作正常,但是某些時候會報告"bind: address in use",于是重啟失?
敗。?
?
A: Andrew Gierth?
server程序總是應該在調用bind()之前設置SO_REUSEADDR套接字選項。至于?
TIME_WAIT狀態(tài),你無法避免,那是TCP協(xié)議的一部分。
?
?
Q: 編寫 TCP/SOCK_STREAM 服務程序時,SO_REUSEADDR到底什么意思??
?
A: 這個套接字選項通知內核,如果端口忙,但TCP狀態(tài)位于 TIME_WAIT ,可以重用?
端口。如果端口忙,而TCP狀態(tài)位于其他狀態(tài),重用端口時依舊得到一個錯誤信息,?
指明"地址已經(jīng)使用中"。如果你的服務程序停止后想立即重啟,而新套接字依舊?
使用同一端口,此時 SO_REUSEADDR 選項非常有用。必須意識到,此時任何非期?
望數(shù)據(jù)到達,都可能導致服務程序反應混亂,不過這只是一種可能,事實上很不?
可能。?
?
一個套接字由相關五元組構成,協(xié)議、本地地址、本地端口、遠程地址、遠程端?
口。SO_REUSEADDR 僅僅表示可以重用本地本地地址、本地端口,整個相關五元組?
還是唯一確定的。所以,重啟后的服務程序有可能收到非期望數(shù)據(jù)。必須慎重使?
用 SO_REUSEADDR 選項。?
總結
以上是生活随笔為你收集整理的TCP/IP TIME_WAIT状态原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sscanf,sscanf_s及其相关用
- 下一篇: 关于TCP下SOCKET的一些测试