TCP連接的狀態(tài)
首先介紹一下TCP連接建立與關(guān)閉過程中的狀態(tài)。TCP連接過程是狀態(tài)的轉(zhuǎn)換,促使?fàn)顟B(tài)發(fā)生轉(zhuǎn)換的因素包括用戶調(diào)用、特定數(shù)據(jù)包以及超時(shí)等,具體狀態(tài)如下所示:
CLOSED:初始狀態(tài),表示沒有任何連接。
LISTEN:Server端的某個(gè)Socket正在監(jiān)聽來自遠(yuǎn)方的TCP端口的連接請(qǐng)求。
SYN_SENT:發(fā)送連接請(qǐng)求后等待確認(rèn)信息。當(dāng)客戶端Socket進(jìn)行Connect連接時(shí),會(huì)首先發(fā)送SYN包,隨即進(jìn)入SYN_SENT狀態(tài),然后等待Server端發(fā)送三次握手中的第2個(gè)包。
SYN_RECEIVED:收到一個(gè)連接請(qǐng)求后回送確認(rèn)信息和對(duì)等的連接請(qǐng)求,然后等待確認(rèn)信息。通常是建立TCP連接的三次握手過程中的一個(gè)中間狀態(tài),表示Server端的Socket接收到來自Client的SYN包,并作出回應(yīng)。
ESTABLISHED:表示連接已經(jīng)建立,可以進(jìn)行數(shù)據(jù)傳輸。
FIN_WAIT_1:主動(dòng)關(guān)閉連接的一方等待對(duì)方返回ACK包。若Socket在ESTABLISHED狀態(tài)下主動(dòng)關(guān)閉連接并向?qū)Ψ桨l(fā)送FIN包(表示己方不再有數(shù)據(jù)需要發(fā)送),則進(jìn)入FIN_WAIT_1狀態(tài),等待對(duì)方返回ACK包,此后還能讀取數(shù)據(jù),但不能發(fā)送數(shù)據(jù)。在正常情況下,無論對(duì)方處于何種狀態(tài),都應(yīng)該馬上返回ACK包,所以FIN_WAIT_1狀態(tài)一般很難見到。
FIN_WAIT_2:主動(dòng)關(guān)閉連接的一方收到對(duì)方返回的ACK包后,等待對(duì)方發(fā)送FIN包。處于FIN_WAIT_1狀態(tài)下的Socket收到了對(duì)方返回的ACK包后,便進(jìn)入FIN_WAIT_2狀態(tài)。由于FIN_WAIT_2狀態(tài)下的Socket需要等待對(duì)方發(fā)送的FIN包,所有常常可以看到。若在FIN_WAIT_1狀態(tài)下收到對(duì)方發(fā)送的同時(shí)帶有FIN和ACK的包時(shí),則直接進(jìn)入TIME_WAIT狀態(tài),無須經(jīng)過FIN_WAIT_2狀態(tài)。
TIME_WAIT:主動(dòng)關(guān)閉連接的一方收到對(duì)方發(fā)送的FIN包后返回ACK包(表示對(duì)方也不再有數(shù)據(jù)需要發(fā)送,此后不能再讀取或發(fā)送數(shù)據(jù)),然后等待足夠長(zhǎng)的時(shí)間(2MSL)以確保對(duì)方接收到ACK包(考慮到丟失ACK包的可能和迷路重復(fù)數(shù)據(jù)包的影響),最后回到CLOSED狀態(tài),釋放網(wǎng)絡(luò)資源。
CLOSE_WAIT:表示被動(dòng)關(guān)閉連接的一方在等待關(guān)閉連接。當(dāng)收到對(duì)方發(fā)送的FIN包后(表示對(duì)方不再有數(shù)據(jù)需要發(fā)送),相應(yīng)的返回ACK包,然后進(jìn)入CLOSE_WAIT狀態(tài)。在該狀態(tài)下,若己方還有數(shù)據(jù)未發(fā)送,則可以繼續(xù)向?qū)Ψ竭M(jìn)行發(fā)送,但不能再讀取數(shù)據(jù),直到數(shù)據(jù)發(fā)送完畢。
LAST_ACK:被動(dòng)關(guān)閉連接的一方在CLOSE_WAIT狀態(tài)下完成數(shù)據(jù)的發(fā)送后便可向?qū)Ψ桨l(fā)送FIN包(表示己方不再有數(shù)據(jù)需要發(fā)送),然后等待對(duì)方返回ACK包。收到ACK包后便回到CLOSED狀態(tài),釋放網(wǎng)絡(luò)資源。
CLOSING:比較罕見的例外狀態(tài)。正常情況下,發(fā)送FIN包后應(yīng)該先收到(或同時(shí)收到)對(duì)方的ACK包,再收到對(duì)方的FIN包,而CLOSING狀態(tài)表示發(fā)送FIN包后并沒有收到對(duì)方的ACK包,卻已收到了對(duì)方的FIN包。有兩種情況可能導(dǎo)致這種狀態(tài):其一,如果雙方幾乎在同時(shí)關(guān)閉連接,那么就可能出現(xiàn)雙方同時(shí)發(fā)送FIN包的情況;其二,如果ACK包丟失而對(duì)方的FIN包很快發(fā)出,也會(huì)出現(xiàn)FIN先于ACK到達(dá)。
圖解三次握手
實(shí)線代表服務(wù)器 的狀態(tài)轉(zhuǎn)換、虛線代表客戶端 的轉(zhuǎn)態(tài)轉(zhuǎn)換;
文字講解三次握手:
最開始客戶端和服務(wù)器,都處于 CLOSED 狀態(tài),二者之間沒有任何聯(lián)系;
客戶端向服務(wù)器發(fā)起連接請(qǐng)求,具體操作是發(fā)送一個(gè) SYN 給服務(wù)器,客戶端狀態(tài)轉(zhuǎn)而變?yōu)?SYN-SENT ,這是第一次握手 ;
由于客戶端向服務(wù)器發(fā)送請(qǐng)求了,服務(wù)器被動(dòng)的進(jìn)入 LISTEN 狀態(tài);
如果服務(wù)器沒有想要客戶端的請(qǐng)求,則客戶端得不到服務(wù)器的回應(yīng),請(qǐng)求就會(huì)超時(shí),一旦請(qǐng)求超時(shí),客戶端的狀態(tài),就會(huì)再次進(jìn)入 CLOSED ;
如果服務(wù)器響應(yīng)了這個(gè)請(qǐng)求,具體操作是:接收到了客戶端發(fā)送的 SYN,同時(shí)向客戶端發(fā)送回應(yīng) SYN+ACK,表示我收到你的請(qǐng)求了;然后服務(wù)器的狀態(tài),進(jìn)入 SYN-RECEIVED ; 這是第二次握手 ;
客戶端收到服務(wù)器的回應(yīng) SYN+ACK,然后再次向服務(wù)器發(fā)送一個(gè) ACK,表示我收到你的回應(yīng)了 ;客戶端的狀態(tài)進(jìn)入 ESTABLISHED ;這是第三次握手 ;
服務(wù)器收到客戶端的響應(yīng) ACK 以后,狀態(tài)進(jìn)入 ESTABLISHED ;
至此,三次握手完成,客戶端與服務(wù)器建立了可靠的連接,可以進(jìn)行數(shù)據(jù)傳輸了 ;
上面有個(gè)特殊的地方:
假如客戶端發(fā)送請(qǐng)求報(bào)文SYN的時(shí)候,服務(wù)器也發(fā)送了請(qǐng)求報(bào)文SYN ,則客戶端進(jìn)入 SYN-RECEIVED 狀態(tài),角色變?yōu)榉?wù)器; 一般這種情況是,沒有絕對(duì)的客戶端、服務(wù)器;通信的兩臺(tái)機(jī)器,可以在客戶端和服務(wù)器之間進(jìn)行角色的切換 ;
圖解四次揮手
上面
實(shí)線代表主動(dòng)關(guān)閉方 的狀態(tài)轉(zhuǎn)換、虛線代表被動(dòng)關(guān)閉方 的轉(zhuǎn)態(tài)轉(zhuǎn)換;
文字講解四次揮手:
主動(dòng)關(guān)閉方,發(fā)送 FIN 給被動(dòng)關(guān)閉方,然后進(jìn)入 FIN-WAIT-1 狀態(tài) ;
被動(dòng)關(guān)閉方,收到主動(dòng)關(guān)閉方的 FIN,會(huì)送一個(gè)回應(yīng) ACK 給主動(dòng)發(fā)送方,然后進(jìn)入 CLOSE-WAIT ;
主動(dòng)方收到被動(dòng)方回應(yīng)的ACK,并且也回應(yīng)一個(gè)ACK,然后進(jìn)入 FIN-WAIT-2 ;
既然主動(dòng)方要關(guān)閉連接,那么被動(dòng)方也不能死皮賴臉的不關(guān)啊,它也就向主動(dòng)方發(fā)送一個(gè)請(qǐng)求FIN,然后進(jìn)入 LAST-ACK ;
被動(dòng)方收到主動(dòng)方的回應(yīng)ACK,進(jìn)入 CLOSED
主動(dòng)方收到被動(dòng)方發(fā)送的 FIN 之后,發(fā)送回應(yīng) ACK(主動(dòng)方在接收到被動(dòng)方發(fā)送的FIN,將不再接受任何信息,但是可以發(fā)送信息),進(jìn)入 TIME-WAIT
上面是有一方先于對(duì)方,發(fā)起關(guān)閉請(qǐng)求 ,下面說下,雙方同時(shí)發(fā)起關(guān)閉請(qǐng)求的情況;
當(dāng)雙方同時(shí)發(fā)起關(guān)閉請(qǐng)求的時(shí)候,雙方在發(fā)送完FIN 以后,都進(jìn)入 FIN-WAIT-1 狀態(tài);然后如果雙發(fā)又同時(shí)收到對(duì)方的FIN,以及同時(shí)收到對(duì)方回應(yīng)的ACK,則直接進(jìn)入 TIME-WAIT ;
如果雙方同時(shí)收到FIN,但是沒有同時(shí)收到ACK,則先進(jìn)入 CLOSING,然后,在雙方都收到 ACK 以后,再進(jìn)入 TIME-WAIT ;
為什么在進(jìn)入 TIME-WAIT 的狀態(tài)以后,會(huì)等待 2MSL 的時(shí)間,再進(jìn)入 CLOSED?
MSL 翻譯為:報(bào)文最大生存時(shí)間 ;,2MSL則是兩個(gè)報(bào)文最大生存世時(shí)間,等待這個(gè)時(shí)間的原因:是因?yàn)樵诰W(wǎng)絡(luò)不好的時(shí)候,有時(shí)候,需要重新發(fā)送報(bào)文,因此進(jìn)入這里的等待下 ;
對(duì)Server與Client的影響
在詳細(xì)了解TCP連接的狀態(tài)和關(guān)閉方式后,我們會(huì)發(fā)現(xiàn)TIME_WAIT狀態(tài)是一個(gè)坑爹的存在!主動(dòng)關(guān)閉連接的一方在發(fā)送最后一個(gè)ACK包后,無論對(duì)方是否收到都會(huì)進(jìn)入TIME_WAIT狀態(tài),等待2MSL的時(shí)間,然后才能釋放網(wǎng)絡(luò)資源。MSL就是Maximum Segment Lifetime(數(shù)據(jù)包的最大生命周期),是一個(gè)數(shù)據(jù)包能在互聯(lián)網(wǎng)上生存的最長(zhǎng)時(shí)間,若超過這個(gè)時(shí)間則該數(shù)據(jù)包將會(huì)消失在網(wǎng)絡(luò)中。操作系統(tǒng)通常會(huì)將2MSL設(shè)為4分鐘,最低不少于30秒,因而TIME_WAIT狀態(tài)一般維持在30秒至4分鐘。這個(gè)是TCP/IP協(xié)議必不可少的,是TCP/IP設(shè)計(jì)者設(shè)計(jì)的,也就是無法解決的。TIME_WAIT狀態(tài)的存在主要有兩個(gè)原因:
可靠地實(shí)現(xiàn)TCP全雙工連接的終止。在關(guān)TCP閉連接時(shí),最后的ACK包是由主動(dòng)關(guān)閉方發(fā)出的,如果這個(gè)ACK包丟失,則被動(dòng)關(guān)閉方將重發(fā)FIN包,因此主動(dòng)方必須維護(hù)狀態(tài)信息,以允許它重發(fā)這個(gè)ACK包。如果不維持這個(gè)狀態(tài)信息,那么主動(dòng)方將回到CLOSED狀態(tài),并對(duì)被動(dòng)方重發(fā)的FIN包響應(yīng)RST包,而被動(dòng)關(guān)閉方將此包解釋成一個(gè)錯(cuò)誤(在Java中會(huì)拋出connection reset的SocketException)。因而,要實(shí)現(xiàn)TCP全雙工連接的正常終止,必須能夠處理四次握手協(xié)議中任意一個(gè)包丟失的情況,主動(dòng)關(guān)閉方必須維持狀態(tài)信息進(jìn)入TIME_WAIT狀態(tài)。
確保迷路重復(fù)數(shù)據(jù)包在網(wǎng)絡(luò)中消失,防止上一次連接中的包迷路后重新出現(xiàn),影響新連接。TCP數(shù)據(jù)包可能由于路由器異常而迷路,在迷路期間,數(shù)據(jù)包發(fā)送方可能因超時(shí)而重發(fā)這個(gè)包,迷路的數(shù)據(jù)包在路由器恢復(fù)后也會(huì)被送到目的地,這個(gè)迷路的數(shù)據(jù)包就稱為L(zhǎng)ost Duplicate。在關(guān)閉一個(gè)TCP連接后,如果馬上使用相同的IP地址和端口建立新的TCP連接,那么有可能出現(xiàn)前一個(gè)連接的迷路重復(fù)數(shù)據(jù)包在前一個(gè)連接關(guān)閉后再次出現(xiàn),影響新建立的連接。為了避免這一情況,TCP協(xié)議不允許使用處于TIME_WAIT狀態(tài)的連接的IP和端口啟動(dòng)一個(gè)新連接,只有經(jīng)過2MSL的時(shí)間,確保上一次連接中所有的迷路重復(fù)數(shù)據(jù)包都已消失在網(wǎng)絡(luò)中,才能安全地建立新連接。
對(duì)于Client而言,每個(gè)連接都需要占用一個(gè)端口,而系統(tǒng)允許的可用端口數(shù)不足65000個(gè)(這也是在TCP參數(shù)優(yōu)化后才能達(dá)到)。因此,如果Client發(fā)起過多的連接并主動(dòng)關(guān)閉(假設(shè)沒有重用端口或者連接多個(gè)Server),就會(huì)有大量的連接在關(guān)閉后處于TIME_WAIT狀態(tài),等待2MSL的時(shí)間后才能釋放網(wǎng)絡(luò)資源(包括端口),于是Client會(huì)由于缺少可用端口而無法新建連接。
對(duì)Server而言(特別是處理高并發(fā)短連接的Server),Server端與Client建立的連接是使用同一個(gè)端口的,即監(jiān)聽的端口,每個(gè)連接通過一個(gè)五元組區(qū)分,包括源IP地址、源端口、傳輸層協(xié)議號(hào)(協(xié)議類型)、目的IP地址、目的端口,因而在理論上,Server不受系統(tǒng)端口數(shù)的限制。但是,Server對(duì)每個(gè)端口上的連接數(shù)是有限制的,它要使用哈希表記錄端口上的每個(gè)連接,并受到文件描述符的最大打開數(shù)的限制。所以,如果Server主動(dòng)關(guān)閉連接,同樣會(huì)有大量的連接在關(guān)閉后處于TIME_WAIT狀態(tài),等待2MSL的時(shí)間后才能釋放網(wǎng)絡(luò)資源(包括哈希表上的連接記錄和文件描述符),于是Server會(huì)由于達(dá)到哈希表和文件描述符的限制而無法接受新連接,造成性能的急劇下滑,性能曲線會(huì)持續(xù)產(chǎn)生嚴(yán)重的波動(dòng)。對(duì)于這種情況,有三種應(yīng)對(duì)方式:
試圖讓Client主動(dòng)關(guān)閉連接,由于每個(gè)Client的并發(fā)量都比較低,因而不會(huì)產(chǎn)生性能瓶頸。
優(yōu)化Server的系統(tǒng)TCP參數(shù),使其網(wǎng)絡(luò)資源的最大值、消耗速度和恢復(fù)速度達(dá)到平衡。
改寫TCP協(xié)議,重新實(shí)現(xiàn)底層代碼,不過該方式難度很大,而且系統(tǒng)的穩(wěn)定性和安全性可能受到影響。
Windows系統(tǒng)下的TCP參數(shù)優(yōu)化
通常會(huì)采用修改注冊(cè)表的方式改進(jìn)Windows的系統(tǒng)參數(shù)。下面將為大家介紹Windows系統(tǒng)下的TCP參數(shù)優(yōu)化方式,適用于Windows 2003、Windows XP、Windows 7以及Server版。對(duì)于具體的系統(tǒng)環(huán)境與性能需求,優(yōu)化方式會(huì)有所差異,效果也不盡相同,僅是個(gè)人的建議。所有的優(yōu)化操作都通過修改注冊(cè)表實(shí)現(xiàn),需要使用regedit命令進(jìn)入注冊(cè)表并創(chuàng)建或修改參數(shù),修改完成后需要重啟系統(tǒng),以使之生效。以下使用的參數(shù)值均為10進(jìn)制。
1. TCPWindowSize
TCPWindowSize的值表示TCP的窗口大小。TCP Receive Window(TCP數(shù)據(jù)接收緩沖)定義了發(fā)送端在沒有獲得接收端的確認(rèn)信息的狀態(tài)下可以發(fā)送的最大字節(jié)數(shù)。此數(shù)值越大,返回的確認(rèn)信息就越少,相應(yīng)的在發(fā)送端和接收端之間的通信就越好。此數(shù)值較小時(shí)可以降低發(fā)送端在等待接收端返回確認(rèn)信息時(shí)發(fā)生超時(shí)的可能性,但這將增加網(wǎng)絡(luò)流量,降低有效吞吐率。TCP在發(fā)送端和接收端之間動(dòng)態(tài)調(diào)整一個(gè)最大段長(zhǎng)度MSS(Maximum Segment Size)的整數(shù)倍。MSS在連接開始建立時(shí)確定,由于TCP Receive Window被調(diào)整為MSS的整數(shù)倍,在數(shù)據(jù)傳輸中完全長(zhǎng)度的TCP數(shù)據(jù)段的比例增加,故而提高了網(wǎng)絡(luò)吞吐率。
缺省情況下,TCP將試圖根據(jù)MSS來優(yōu)化窗口大小,起始值為16KB,最大值為64KB。TCPWindowSize的最大值通常為65535字節(jié)(64KB),以太網(wǎng)最大段長(zhǎng)度為1460字節(jié),低于64KB的1460的最大整數(shù)倍為62420字節(jié)。
重置(RST)
幾種TCP連接中出現(xiàn)RST的情況
端口未打開
請(qǐng)求超時(shí)
提前關(guān)閉
在一個(gè)已關(guān)閉的socket上收到數(shù)據(jù)
TCP重置是一個(gè)好的事情,如果沒有reset,我們將會(huì)遇到各種各樣的TCP網(wǎng)絡(luò)連接問題。需要注意的是導(dǎo)致reset的原因是多種多樣的,不僅僅是兩端的節(jié)點(diǎn)還有可能是應(yīng)用程序,追查問題時(shí)最重要的是查看包的狀態(tài)以及其重傳。
一個(gè)案例
當(dāng)我們僅僅打開服務(wù)端之后(端口號(hào)為5188),我們來看看所處的狀態(tài)。
打開服務(wù)端:
調(diào)用命令查看所有的網(wǎng)絡(luò)狀態(tài):netstat
然后,我們通過命令:摘取有關(guān)tcp的狀態(tài):netstat -an |grep tcp
緊接著為了刪減出有效的信息,我們只需要tcp協(xié)議,5188這個(gè)端口,我們可以這樣做:
netstat -an|grep tcp|grep 5188
此刻,可以看到,這里的狀態(tài)是處于LISTEN,調(diào)用的accept函數(shù)還是在阻塞著,等待著返回。
這時(shí),再次打開客戶端,繼續(xù)觀察一下狀態(tài):
然后,繼續(xù)調(diào)用之前的命令:
netstat -an|grep tcp|grep 5188
當(dāng)客戶端一打開,那么就完成了TCP的建立,這里,我們可以看到有兩個(gè)是:ESTABLISHED
其中第二行的42555表示的是客戶端所打開的端口,5188是服務(wù)端所打開的端口,客戶端連向了服務(wù)器端。
由于我們上面的測(cè)試是在同一臺(tái)主機(jī)上的,所以會(huì)出現(xiàn)上面的三種信息
而對(duì)于其他的狀態(tài)而言,只是因?yàn)闋顟B(tài)的轉(zhuǎn)化時(shí)間非常短(三次握手,四次揮手完成的特別快),我們不
去探究具體的狀態(tài),下面
1.查找服務(wù)器進(jìn)程:
ps -ef | grep echoserv
分析其pid號(hào),知道了我們此刻打開的是中間的這個(gè)服務(wù)端(21858,21849)
所以,此刻,我們殺死這個(gè)進(jìn)程:
kill -9 21858
到啦這里,我們?cè)俅尾榭匆幌聽顟B(tài):
至于為什么會(huì)產(chǎn)生一個(gè)FIN_WAIT2, 而不是TIME_WAIT狀態(tài)呢?這是因?yàn)椋何覀兂绦蛑惺沁@樣處理的,服務(wù)端關(guān)閉之后,然后客戶端接收到啦這個(gè)分節(jié),并向服務(wù)端發(fā)送了當(dāng)前的分節(jié)確認(rèn),然后自己阻塞在了從鍵盤獲取字符的這個(gè)位置,并不能運(yùn)行到函數(shù)read處去,也就是說,read函數(shù)壓根就不會(huì)返回0,所以客戶端就不會(huì)重新向服務(wù)端重新發(fā)送關(guān)閉連接的分節(jié),也就停留在此刻了,同樣的,
服務(wù)端接受到啦確認(rèn)分節(jié),那么自己的狀態(tài)就變成了FIN_WAIT_2,這樣就解釋的說明問題了
-
IP
+關(guān)注
關(guān)注
5文章
1655瀏覽量
149334 -
服務(wù)器
+關(guān)注
關(guān)注
12文章
9021瀏覽量
85185 -
TCP
+關(guān)注
關(guān)注
8文章
1350瀏覽量
78987
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論