TCP ,丫的終于來了!
之前的文章一直在聊各種網(wǎng)絡(luò)協(xié)議,那么從這篇文章開始,我就會(huì)和你聊一聊關(guān)于 TCP 協(xié)議的種種特征,比如 TCP 連接管理(也是這篇文章主要討論的)、TCP 超時(shí)和重傳、TCP 擁塞控制、TCP 數(shù)據(jù)流和窗口管理。
TCP 是一種面向連接的單播協(xié)議,在 TCP 中,并不存在多播、廣播的這種行為,因?yàn)?TCP 報(bào)文段中能明確發(fā)送方和接受方的 IP 地址。
在發(fā)送數(shù)據(jù)前,相互通信的雙方(即發(fā)送方和接受方)需要建立一條連接,在發(fā)送數(shù)據(jù)后,通信雙方需要斷開連接,這就是 TCP 連接的建立和終止。
TCP 連接的建立和終止
如果你看過我之前寫的關(guān)于網(wǎng)絡(luò)層的一篇文章,你應(yīng)該知道 TCP 的基本元素有四個(gè):即發(fā)送方的 IP 地址、發(fā)送方的端口號(hào)、接收方的 IP 地址、接收方的端口號(hào)。而每一方的 IP + 端口號(hào)都可以看作是一個(gè)套接字,套接字能夠被唯一標(biāo)示。套接字就相當(dāng)于是門,出了這個(gè)門,就要進(jìn)行數(shù)據(jù)傳輸了。
TCP 的連接建立 -> 終止總共分為三個(gè)階段
下面我們所討論的重點(diǎn)也是集中在這三個(gè)層面。
下圖是一個(gè)非常典型的 TCP 連接的建立和關(guān)閉過程,其中不包括數(shù)據(jù)傳輸?shù)牟糠帧?/p>
TCP 建立連接 - 三次握手
服務(wù)端進(jìn)程準(zhǔn)備好接收來自外部的 TCP 連接,一般情況下是調(diào)用 bind、listen、socket 三個(gè)函數(shù)完成。這種打開方式被認(rèn)為是 被動(dòng)打開(passive open)。然后服務(wù)端進(jìn)程處于 LISTEN 狀態(tài),等待客戶端連接請(qǐng)求。
客戶端通過 connect 發(fā)起主動(dòng)打開(active open),向服務(wù)器發(fā)出連接請(qǐng)求,請(qǐng)求中首部同步位 SYN = 1,同時(shí)選擇一個(gè)初始序號(hào) sequence ,簡寫 seq = x。SYN 報(bào)文段不允許攜帶數(shù)據(jù),只消耗一個(gè)序號(hào)。此時(shí),客戶端進(jìn)入 SYN-SEND 狀態(tài)。
服務(wù)器收到客戶端連接后,,需要確認(rèn)客戶端的報(bào)文段。在確認(rèn)報(bào)文段中,把 SYN 和 ACK 位都置為 1 。確認(rèn)號(hào)是 ack = x + 1,同時(shí)也為自己選擇一個(gè)初始序號(hào) seq = y。這個(gè)報(bào)文段也不能攜帶數(shù)據(jù),但同樣要消耗掉一個(gè)序號(hào)。此時(shí),TCP 服務(wù)器進(jìn)入 SYN-RECEIVED(同步收到) 狀態(tài)。
客戶端在收到服務(wù)器發(fā)出的響應(yīng)后,還需要給出確認(rèn)連接。確認(rèn)連接中的 ACK 置為 1 ,序號(hào)為 seq = x + 1,確認(rèn)號(hào)為 ack = y + 1。TCP 規(guī)定,這個(gè)報(bào)文段可以攜帶數(shù)據(jù)也可以不攜帶數(shù)據(jù),如果不攜帶數(shù)據(jù),那么下一個(gè)數(shù)據(jù)報(bào)文段的序號(hào)仍是 seq = x + 1。這時(shí),客戶端進(jìn)入 ESTABLISHED (已連接) 狀態(tài)
服務(wù)器收到客戶的確認(rèn)后,也進(jìn)入 ESTABLISHED 狀態(tài)。
這是一個(gè)典型的三次握手過程,通過上面 3 個(gè)報(bào)文段就能夠完成一個(gè) TCP 連接的建立。三次握手的的目的不僅僅在于讓通信雙方知曉正在建立一個(gè)連接,也在于利用數(shù)據(jù)包中的選項(xiàng)字段來交換一些特殊信息,交換初始序列號(hào)。
一般首個(gè)發(fā)送 SYN 報(bào)文的一方被認(rèn)為是主動(dòng)打開一個(gè)連接,而這一方通常也被稱為客戶端。而 SYN 的接收方通常被稱為服務(wù)端,它用于接收這個(gè) SYN,并發(fā)送下面的 SYN,因此這種打開方式是被動(dòng)打開。
TCP 建立一個(gè)連接需要三個(gè)報(bào)文段,釋放一個(gè)連接卻需要四個(gè)報(bào)文段。
TCP 斷開連接 - 四次揮手?jǐn)?shù)據(jù)
傳輸結(jié)束后,通信的雙方可以釋放連接。數(shù)據(jù)傳輸結(jié)束后的客戶端主機(jī)和服務(wù)端主機(jī)都處于 ESTABLISHED 狀態(tài),然后進(jìn)入釋放連接的過程。
TCP 斷開連接需要?dú)v經(jīng)的過程如下
客戶端應(yīng)用程序發(fā)出釋放連接的報(bào)文段,并停止發(fā)送數(shù)據(jù),主動(dòng)關(guān)閉 TCP 連接。客戶端主機(jī)發(fā)送釋放連接的報(bào)文段,報(bào)文段中首部 FIN 位置為 1 ,不包含數(shù)據(jù),序列號(hào)位 seq = u,此時(shí)客戶端主機(jī)進(jìn)入 FIN-WAIT-1(終止等待 1) 階段。
服務(wù)器主機(jī)接受到客戶端發(fā)出的報(bào)文段后,即發(fā)出確認(rèn)應(yīng)答報(bào)文,確認(rèn)應(yīng)答報(bào)文中 ACK = 1,生成自己的序號(hào)位 seq = v,ack = u + 1,然后服務(wù)器主機(jī)就進(jìn)入 CLOSE-WAIT(關(guān)閉等待) 狀態(tài)。
客戶端主機(jī)收到服務(wù)端主機(jī)的確認(rèn)應(yīng)答后,即進(jìn)入 FIN-WAIT-2(終止等待2) 的狀態(tài)。等待客戶端發(fā)出連接釋放的報(bào)文段。
這時(shí)服務(wù)端主機(jī)會(huì)發(fā)出斷開連接的報(bào)文段,報(bào)文段中 ACK = 1,序列號(hào) seq = v,ack = u + 1,在發(fā)送完斷開請(qǐng)求的報(bào)文后,服務(wù)端主機(jī)就進(jìn)入了 LAST-ACK(最后確認(rèn))的階段。
客戶端收到服務(wù)端的斷開連接請(qǐng)求后,客戶端需要作出響應(yīng),客戶端發(fā)出斷開連接的報(bào)文段,在報(bào)文段中,ACK = 1, 序列號(hào) seq = u + 1,因?yàn)榭蛻舳藦倪B接開始斷開后就沒有再發(fā)送數(shù)據(jù),ack = v + 1,然后進(jìn)入到 TIME-WAIT(時(shí)間等待) 狀態(tài),請(qǐng)注意,這個(gè)時(shí)候 TCP 連接還沒有釋放。必須經(jīng)過時(shí)間等待的設(shè)置,也就是 2MSL 后,客戶端才會(huì)進(jìn)入 CLOSED 狀態(tài),時(shí)間 MSL 叫做最長報(bào)文段壽命(Maximum Segment Lifetime)。
服務(wù)端主要收到了客戶端的斷開連接確認(rèn)后,就會(huì)進(jìn)入 CLOSED 狀態(tài)。因?yàn)榉?wù)端結(jié)束 TCP 連接時(shí)間要比客戶端早,而整個(gè)連接斷開過程需要發(fā)送四個(gè)報(bào)文段,因此釋放連接的過程也被稱為四次揮手。
TCP 連接的任意一方都可以發(fā)起關(guān)閉操作,只不過通常情況下發(fā)起關(guān)閉連接操作一般都是客戶端。然而,一些服務(wù)器比如 Web 服務(wù)器在對(duì)請(qǐng)求作出相應(yīng)后也會(huì)發(fā)起關(guān)閉連接的操作。TCP 協(xié)議規(guī)定通過發(fā)送一個(gè) FIN 報(bào)文來發(fā)起關(guān)閉操作。
所以綜上所述,建立一個(gè) TCP 連接需要三個(gè)報(bào)文段,而關(guān)閉一個(gè) TCP 連接需要四個(gè)報(bào)文段。TCP 協(xié)議還支持一種半開啟(half-open) 狀態(tài),雖然這種情況并不多見。
TCP 半開啟
TCP 連接處于半開啟的這種狀態(tài)是因?yàn)檫B接的一方關(guān)閉或者終止了這個(gè) TCP 連接卻沒有通知另一方,也就是說兩個(gè)人正在微信聊天,cxuan 你下線了你不告訴我,我還在跟你侃八卦呢。此時(shí)就認(rèn)為這條連接處于半開啟狀態(tài)。這種情況發(fā)生在通信中的一方處于主機(jī)崩潰的情況下,你 xxx 的,我電腦死機(jī)了我咋告訴你?只要處于半連接狀態(tài)的一方不傳輸數(shù)據(jù)的話,那么是無法檢測(cè)出來對(duì)方主機(jī)已經(jīng)下線的。
另外一種處于半開啟狀態(tài)的原因是通信的一方關(guān)閉了主機(jī)電源 而不是正常關(guān)機(jī)。這種情況下會(huì)導(dǎo)致服務(wù)器上有很多半開啟的 TCP 連接。
TCP 半關(guān)閉
既然 TCP 支持半開啟操作,那么我們可以設(shè)想 TCP 也支持半關(guān)閉操作。同樣的,TCP 半關(guān)閉也并不常見。TCP 的半關(guān)閉操作是指僅僅關(guān)閉數(shù)據(jù)流的一個(gè)傳輸方向。兩個(gè)半關(guān)閉操作合在一起就能夠關(guān)閉整個(gè)連接。在一般情況下,通信雙方會(huì)通過應(yīng)用程序互相發(fā)送 FIN 報(bào)文段來結(jié)束連接,但是在 TCP 半關(guān)閉的情況下,應(yīng)用程序會(huì)表明自己的想法:"我已經(jīng)完成了數(shù)據(jù)的發(fā)送發(fā)送,并發(fā)送了一個(gè) FIN 報(bào)文段給對(duì)方,但是我依然希望接收來自對(duì)方的數(shù)據(jù)直到它發(fā)送一個(gè) FIN 報(bào)文段給我"。下面是一個(gè) TCP 半關(guān)閉的示意圖。
解釋一下這個(gè)過程:
首先客戶端主機(jī)和服務(wù)器主機(jī)一直在進(jìn)行數(shù)據(jù)傳輸,一段時(shí)間后,客戶端發(fā)起了 FIN 報(bào)文,要求主動(dòng)斷開連接,服務(wù)器收到 FIN 后,回應(yīng) ACK ,由于此時(shí)發(fā)起半關(guān)閉的一方也就是客戶端仍然希望服務(wù)器發(fā)送數(shù)據(jù),所以服務(wù)器會(huì)繼續(xù)發(fā)送數(shù)據(jù),一段時(shí)間后服務(wù)器發(fā)送另外一條 FIN 報(bào)文,在客戶端收到 FIN 報(bào)文回應(yīng) ACK 給服務(wù)器后,斷開連接。
TCP 的半關(guān)閉操作中,連接的一個(gè)方向被關(guān)閉,而另一個(gè)方向仍在傳輸數(shù)據(jù)直到它被關(guān)閉為止。只不過很少有應(yīng)用程序使用這一特性。
同時(shí)打開和同時(shí)關(guān)閉
還有一種比較非常規(guī)的操作,這就是兩個(gè)應(yīng)用程序同時(shí)主動(dòng)打開連接。雖然這種情況看起來不太可能,但是在特定的安排下卻是有可能發(fā)生的。我們主要講述這個(gè)過程。
通信雙方在接收到來自對(duì)方的 SYN 之前會(huì)首先發(fā)送一個(gè) SYN,這個(gè)場(chǎng)景還要求通信雙方都知道對(duì)方的 IP 地址 + 端口號(hào)。
比如戀愛中的一對(duì)男女,他倆都同時(shí)說出了我愛你這個(gè)神圣的誓言,然后他倆對(duì)彼此的響應(yīng)進(jìn)行么么噠,這就是同時(shí)打開。
下面是同時(shí)打開的例子
如上圖所示,通信雙方都在收到對(duì)方報(bào)文前主動(dòng)發(fā)送了 SYN 報(bào)文,都在收到彼此的報(bào)文后回復(fù)了一個(gè) ACK 報(bào)文。
一個(gè)同時(shí)打開過程需要交換四個(gè)報(bào)文段,比普通的三次握手增加了一個(gè),由于同時(shí)打開沒有客戶端和服務(wù)器一說,所以這里我用了通信雙方來稱呼。
像同時(shí)打開一樣,同時(shí)關(guān)閉也是通信雙方同時(shí)提出主動(dòng)關(guān)閉請(qǐng)求,發(fā)送 FIN 報(bào)文,下圖顯示了一個(gè)同時(shí)關(guān)閉的過程。
同時(shí)關(guān)閉過程中需要交換和正常關(guān)閉相同數(shù)量的報(bào)文段,只不過同時(shí)關(guān)閉不像四次揮手那樣順序進(jìn)行,而是交叉進(jìn)行的。
聊一聊初始序列號(hào)
也許是我上面圖示或者文字描述的不專業(yè),初始序列號(hào)它是有專業(yè)術(shù)語表示的,初始序列號(hào)的英文名稱是Initial sequence numbers (ISN),所以我們上面表示的 seq = v,其實(shí)就表示的 ISN。
在發(fā)送 SYN 之前,通信雙方會(huì)選擇一個(gè)初始序列號(hào)。初始序列號(hào)是隨機(jī)生成的,每一個(gè) TCP 連接都會(huì)有一個(gè)不同的初始序列號(hào)。RFC 文檔指出初始序列號(hào)是一個(gè) 32 位的計(jì)數(shù)器,每 4 us(微秒) + 1。因?yàn)槊總€(gè) TCP 連接都是一個(gè)不同的實(shí)例,這么安排的目的就是為了防止出現(xiàn)序列號(hào)重疊的情況。
當(dāng)一個(gè) TCP 連接建立的過程中,只有正確的 TCP 四元組和正確的序列號(hào)才會(huì)被對(duì)方接收。這也反應(yīng)了 TCP 報(bào)文段容易被偽造 的脆弱性,因?yàn)橹灰覀卧炝艘粋€(gè)相同的四元組和初始序列號(hào)就能夠偽造 TCP 連接,從而打斷 TCP 的正常連接,所以抵御這種攻擊的一種方式就是使用初始序列號(hào),另外一種方法就是加密序列號(hào)。
TCP 狀態(tài)轉(zhuǎn)換
我們上面聊到了三次握手和四次揮手,提到了一些關(guān)于 TCP 連接之間的狀態(tài)轉(zhuǎn)換,那么下面我就從頭開始和你好好梳理一下這些狀態(tài)之間的轉(zhuǎn)換。
首先第一步,剛開始時(shí)服務(wù)器和客戶端都處于 CLOSED 狀態(tài),這時(shí)需要判斷是主動(dòng)打開還是被動(dòng)打開,如果是主動(dòng)打開,那么客戶端向服務(wù)器發(fā)送 SYN 報(bào)文,此時(shí)客戶端處于 SYN-SEND 狀態(tài),SYN-SEND 表示發(fā)送連接請(qǐng)求后等待匹配的連接請(qǐng)求,服務(wù)器被動(dòng)打開會(huì)處于 LISTEN 狀態(tài),用于監(jiān)聽 SYN 報(bào)文。如果客戶端調(diào)用了 close 方法或者經(jīng)過一段時(shí)間沒有操作,就會(huì)重新變?yōu)?CLOSED 狀態(tài),這一步轉(zhuǎn)換圖如下
這里有個(gè)疑問,為什么處于 LISTEN 狀態(tài)下的客戶端還會(huì)發(fā)送 SYN 變?yōu)?SYN_SENT 狀態(tài)呢?
知乎看到了車小胖大佬的回答,這種情況可能出現(xiàn)在 FTP 中,LISTEN -> SYN_SENT 是因?yàn)檫@個(gè)連接可能是由于服務(wù)器端的應(yīng)用有數(shù)據(jù)發(fā)送給客戶端所觸發(fā)的,客戶端被動(dòng)接受連接,連接建立后,開始傳輸文件。也就是說,處于 LISTEN 狀態(tài)的服務(wù)器也是有可能發(fā)送 SYN 報(bào)文的,只不過這種情況非常少見。
處于 SYN_SEND 狀態(tài)的服務(wù)器會(huì)接收 SYN 并發(fā)送 SYN 和 ACK 轉(zhuǎn)換成為 SYN_RCVD 狀態(tài),同樣的,處于 LISTEN 狀態(tài)的客戶端也會(huì)接收 SYN 并發(fā)送 SYN 和 ACK 轉(zhuǎn)換為 SYN_RCVD 狀態(tài)。如果處于 SYN_RCVD 狀態(tài)的客戶端收到 RST 就會(huì)變?yōu)? LISTEN 狀態(tài)。
這兩張圖一起看會(huì)比較好一些。
這里需要解釋下什么是 RST
這里有一種情況是當(dāng)主機(jī)收到 TCP 報(bào)文段后,其 IP 和端口號(hào)不匹配的情況。假設(shè)客戶端主機(jī)發(fā)送一個(gè)請(qǐng)求,而服務(wù)器主機(jī)經(jīng)過 IP 和端口號(hào)的判斷后發(fā)現(xiàn)不是給這個(gè)服務(wù)器的,那么服務(wù)器就會(huì)發(fā)出一個(gè) RST 特殊報(bào)文段給客戶端。
因此,當(dāng)服務(wù)端發(fā)送一個(gè) RST 特殊報(bào)文段給客戶端的時(shí)候,它就會(huì)告訴客戶端沒有匹配的套接字連接,請(qǐng)不要再繼續(xù)發(fā)送了。
RST:(Reset the connection)用于復(fù)位因某種原因引起出現(xiàn)的錯(cuò)誤連接,也用來拒絕非法數(shù)據(jù)和請(qǐng)求。如果接收到 RST 位時(shí)候,通常發(fā)生了某些錯(cuò)誤。
上面沒有識(shí)別正確的 IP 端口是一種導(dǎo)致 RST 出現(xiàn)的情況,除此之外,RST 還可能由于請(qǐng)求超時(shí)、取消一個(gè)已存在的連接等出現(xiàn)。
位于 SYN_RCVD 的服務(wù)器會(huì)接收 ACK 報(bào)文,SYN_SEND 的客戶端會(huì)接收 SYN 和 ACK 報(bào)文,并發(fā)送 ACK 報(bào)文,由此,客戶端和服務(wù)器之間的連接就建立了。
這里還要注意一點(diǎn),同時(shí)打開的狀態(tài)我在上面沒有刻意表示出來,實(shí)際上,在同時(shí)打開的情況下,它的狀態(tài)變化是這樣的。
為什么會(huì)是這樣呢?因?yàn)槟阆耄谕瑫r(shí)打開的情況下,兩端主機(jī)都發(fā)起 SYN 報(bào)文,而主動(dòng)發(fā)起 SYN 的主機(jī)會(huì)處于 SYN-SEND 狀態(tài),發(fā)送完成后,會(huì)等待接收 SYN 和 ACK , 在雙方主機(jī)都發(fā)送了 SYN + ACK 后,雙方都處于 SYN-RECEIVED(SYN-RCVD) 狀態(tài),然后等待 SYN + ACK 的報(bào)文到達(dá)后,雙方就會(huì)處于 ESTABLISHED 狀態(tài),開始傳輸數(shù)據(jù)。
好了,到現(xiàn)在為止,我給你敘述了一下 TCP 連接建立過程中的狀態(tài)轉(zhuǎn)換,現(xiàn)在你可以泡一壺茶喝點(diǎn)水,等著數(shù)據(jù)傳輸了。
好了,現(xiàn)在水喝夠了,這時(shí)候數(shù)據(jù)也傳輸完成了,數(shù)據(jù)傳輸完成后,這條 TCP 連接就可以斷開了。
現(xiàn)在我們把時(shí)鐘往前撥一下,調(diào)整到服務(wù)端處于 SYN_RCVD 狀態(tài)的時(shí)刻,因?yàn)閯偸盏搅?SYN 包并發(fā)送了 SYN + ACK 包,此時(shí)服務(wù)端很開心,但是這時(shí),服務(wù)端應(yīng)用進(jìn)程關(guān)閉了,然后應(yīng)用進(jìn)程發(fā)了一個(gè) FIN 包,就會(huì)讓服務(wù)器從 SYN_RCVD -> FIN_WAIT_1 狀態(tài)。
然后把時(shí)鐘調(diào)到現(xiàn)在,客戶端和服務(wù)器現(xiàn)在已經(jīng)傳輸完數(shù)據(jù)了 ,此時(shí)客戶端發(fā)送了一條 FIN 報(bào)文希望斷開連接,此時(shí)客戶端也會(huì)變?yōu)?FIN_WAIT_1 狀態(tài),對(duì)于服務(wù)器來說,它接收到了 FIN 報(bào)文段并回復(fù)了 ACK 報(bào)文,就會(huì)從 ESTABLISHED -> CLOSE_WAIT 狀態(tài)。
位于 CLOSE_WAIT 狀態(tài)的服務(wù)端會(huì)發(fā)送 FIN 報(bào)文,然后把自己置于 LAST_ACK 狀態(tài)。處于 FIN_WAIT_1 的客戶端接收 ACK 消息就會(huì)變?yōu)?FIN_WAIT_2 狀態(tài)。
這里需要先解釋一下 CLOSING 這個(gè)狀態(tài),F(xiàn)IN_WAIT_1 -> CLOSING 的轉(zhuǎn)換比較特殊
CLOSING 這種狀態(tài)比較特殊,實(shí)際情況中應(yīng)該是很少見,屬于一種比較罕見的例外狀態(tài)。正常情況下,當(dāng)你發(fā)送FIN 報(bào)文后,按理來說是應(yīng)該先收到(或同時(shí)收到)對(duì)方的 ACK 報(bào)文,再收到對(duì)方的 FIN 報(bào)文。但是 CLOSING 狀態(tài)表示你發(fā)送 FIN 報(bào)文后,并沒有收到對(duì)方的 ACK 報(bào)文,反而卻也收到了對(duì)方的 FIN 報(bào)文。
什么情況下會(huì)出現(xiàn)此種情況呢?其實(shí)細(xì)想一下,也不難得出結(jié)論:那就是如果雙方在同時(shí)關(guān)閉一個(gè)鏈接的話,那么就出現(xiàn)了同時(shí)發(fā)送 FIN 報(bào)文的情況,也即會(huì)出現(xiàn) CLOSING 狀態(tài),表示雙方都正在關(guān)閉連接。
FIN_WAIT_2 狀態(tài)的客戶端接收服務(wù)端主機(jī)發(fā)送的 FIN + ACK 消息,并發(fā)送 ACK 響應(yīng)后,會(huì)變?yōu)?TIME_WAIT 狀態(tài)。處于 CLOSE_WAIT 的客戶端發(fā)送 FIN 會(huì)處于 LAST_ACK 狀態(tài)。
這里不少圖和博客雖然在圖上畫的是 FIN + ACK 報(bào)文后才會(huì)處于 LAST_ACK 狀態(tài),但是描述的時(shí)候,一般通常只對(duì)于 FIN 進(jìn)行描述。也就是說 CLOSE_WAIT 發(fā)送 FIN 才會(huì)處于 LAST_ACK 狀態(tài)。
所以這里 FIN_WAIT_1 -> TIME_WAIT 的狀態(tài)也就是接收 FIN 和 ACK 并發(fā)送 ACK 之后,客戶端處于的狀態(tài)。
然后位于 CLOSINIG 狀態(tài)的客戶端這時(shí)候還有 ACK 接收的話,會(huì)繼續(xù)處于 TIME_WAIT 狀態(tài),可以看到,TIME_WAIT 狀態(tài)相當(dāng)于是客戶端在關(guān)閉前的最后一個(gè)狀態(tài),它是一種主動(dòng)關(guān)閉的狀態(tài);而 LAST_ACK 是服務(wù)端在關(guān)閉前的最后一個(gè)狀態(tài),它是一種被動(dòng)打開的狀態(tài)。
上面有幾個(gè)狀態(tài)比較特殊,這里我們向西解釋下。
TIME_WAIT 狀態(tài)
通信雙方建立 TCP 連接后,主動(dòng)關(guān)閉連接的一方就會(huì)進(jìn)入 TIME_WAIT 狀態(tài)。TIME_WAIT 狀態(tài)也稱為 2MSL 的等待狀態(tài)。在這個(gè)狀態(tài)下,TCP 將會(huì)等待最大段生存期(Maximum Segment Lifetime, MSL) 時(shí)間的兩倍。
這里需要解釋下 MSL
MSL 是 TCP 段期望的最大生存時(shí)間,也就是在網(wǎng)絡(luò)中存在的最長時(shí)間。這個(gè)時(shí)間是有限制的,因?yàn)槲覀冎?TCP 是依靠 IP 數(shù)據(jù)段來進(jìn)行傳輸?shù)?,IP 數(shù)據(jù)報(bào)中有 TTL 和跳數(shù)的字段,這兩個(gè)字段決定了 IP 的生存時(shí)間,一般情況下,TCP 的最大生存時(shí)間是 2 分鐘,不過這個(gè)數(shù)值是可以修改的,根據(jù)不同操作系統(tǒng)可以修改此值。
基于此,我們來探討 TIME_WAIT 的狀態(tài)。
當(dāng) TCP 執(zhí)行一個(gè)主動(dòng)關(guān)閉并發(fā)送最終的 ACK 時(shí),TIME_WAIT 應(yīng)該以 2 * 最大生存時(shí)間存在,這樣就能夠讓 TCP 重新發(fā)送最終的 ACK 以避免出現(xiàn)丟失的情況。重新發(fā)送最終的 ACK 并不是因?yàn)?TCP 重傳了 ACK,而是因?yàn)橥ㄐ帕硪环街貍髁?FIN,客戶端經(jīng)常回發(fā)送 FIN,因?yàn)樗枰?ACK 的響應(yīng)才能夠關(guān)閉連接,如果生存時(shí)間超過了 2MSL 的話,客戶端就會(huì)發(fā)送 RST,使服務(wù)端出錯(cuò)。