傳輸控制協(xié)議(TCP,Transmission Control Protocol)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,由 IETF 的 RFC 793 定義,是為了在不可靠的互聯(lián)網(wǎng)絡(luò)上提供可靠的端到端字節(jié)流而專門設(shè)計(jì)的一個(gè)傳輸協(xié)議。
互聯(lián)網(wǎng)絡(luò)與單個(gè)網(wǎng)絡(luò)有很大的不同,因?yàn)榛ヂ?lián)網(wǎng)絡(luò)的不同部分可能有截然不同的拓?fù)浣Y(jié)構(gòu)、帶寬、延遲、數(shù)據(jù)包大小和其他參數(shù)。TCP 的設(shè)計(jì)目標(biāo)是能夠動(dòng)態(tài)地適應(yīng)互聯(lián)網(wǎng)絡(luò)的這些特性,而且具備面對(duì)各種故障時(shí)的健壯性。
應(yīng)用層向 TCP 層發(fā)送用于網(wǎng)間傳輸?shù)摹⒂?8 位字節(jié)表示的數(shù)據(jù)流,然后 TCP 把數(shù)據(jù)流分區(qū)成適當(dāng)長度的報(bào)文段(通常受該計(jì)算機(jī)連接的網(wǎng)絡(luò)的數(shù)據(jù)鏈路層的最大傳輸單元(MTU)的限制)。之后 TCP 把結(jié)果包傳給 IP 層,由它來通過網(wǎng)絡(luò)將包傳送給接收端實(shí)體的 TCP 層。TCP 為了保證不發(fā)生丟包,就給每個(gè)包一個(gè)序號(hào),同時(shí)序號(hào)也保證了傳送到接收端實(shí)體的包的按序接收。然后接收端實(shí)體對(duì)已成功收到的包發(fā)回一個(gè)相應(yīng)的確認(rèn)(ACK);如果發(fā)送端實(shí)體在合理的往返時(shí)延(RTT)內(nèi)未收到確認(rèn),那么對(duì)應(yīng)的數(shù)據(jù)包就被假設(shè)為已丟失將會(huì)被進(jìn)行重傳。TCP 用一個(gè)校驗(yàn)和函數(shù)來檢驗(yàn)數(shù)據(jù)是否有錯(cuò)誤;在發(fā)送和接收時(shí)都要計(jì)算校驗(yàn)和。
TCP 消息頭
下面對(duì)每個(gè)字段含義進(jìn)行解釋
- 源、目標(biāo)端口號(hào)字段:占 16 比特 (2 字節(jié))。TCP 協(xié)議通過使用”端口”來標(biāo)識(shí)源端和目標(biāo)端的應(yīng)用進(jìn)程。端口號(hào)可以使用0到65535之間的任何數(shù)字。在收到服務(wù)請(qǐng)求時(shí),操作系統(tǒng)動(dòng)態(tài)地為客戶端的應(yīng)用程序分配端口號(hào)。在服務(wù)器端,每種服務(wù)在”眾所周知的端口”(Well-Know Port)為用戶提供服務(wù)。
- 順序號(hào)字段:占 32 比特。用來標(biāo)識(shí)從 TCP 源端向 TCP 目標(biāo)端發(fā)送的數(shù)據(jù)字節(jié)流,它表示在這個(gè)報(bào)文段中的第一個(gè)數(shù)據(jù)字節(jié)。在TCP傳送的流中,每一個(gè)字節(jié)一個(gè)順序號(hào)。e.g.如果一個(gè) TCP 報(bào)文段的序號(hào)為 301,它攜帶了 100 字節(jié)的數(shù)據(jù),就表示這 100 個(gè)字節(jié)的數(shù)據(jù)的字節(jié)序號(hào)范圍是 [301, 400],該報(bào)文段攜帶的第一個(gè)字節(jié)序號(hào)是 301,最后一個(gè)字節(jié)序號(hào)是 400。所以順序號(hào)號(hào)確保了TCP傳輸?shù)挠行蛐浴?/li>
- 確認(rèn)號(hào)字段:占 32 比特。只有 ACK 標(biāo)志為1時(shí),確認(rèn)號(hào)字段才有效。它包含目標(biāo)端所期望收到源端的下一個(gè)數(shù)據(jù)字節(jié)。比如建立連接時(shí),SYN 報(bào)文的 ACK 標(biāo)志位為 0。
- 頭部長度字段:占 4 比特。給出頭部占 32 比特的數(shù)目。沒有任何選項(xiàng)字段的 TCP 頭部長度為 20 字節(jié);最多可以有 60 字節(jié)的 TCP 頭部。4位首部長度字段所能表示的最大值為1111,轉(zhuǎn)化為10進(jìn)制為15,15*32/8 = 60,故報(bào)頭最大長度為60字節(jié)
- 標(biāo)志位字段(U、A、P、R、S、F):占 6 比特。各比特的含義如下:窗口大小字段:占 16 比特。此字段用來進(jìn)行流量控制。單位為字節(jié)數(shù),這個(gè)值是本機(jī)期望一次接收的字節(jié)數(shù)。
- URG:緊急指針標(biāo)志,為 1 時(shí)表示緊急指針有效,為 0 則忽略緊急指針。ACK:確認(rèn)序號(hào)標(biāo)志,為 1 時(shí)表示確認(rèn)號(hào)有效,為 0 表示報(bào)文中不含確認(rèn)信息,忽略確認(rèn)號(hào)字段。PSH:push 標(biāo)志,為 1 表示是帶有 push 標(biāo)志的數(shù)據(jù),指示接收方在接收到該報(bào)文段以后,應(yīng)盡快將這個(gè)報(bào)文段交給應(yīng)用程序,而不是在緩沖區(qū)排隊(duì)。RST:重置連接標(biāo)志,用于重置由于主機(jī)崩潰或其他原因而出現(xiàn)錯(cuò)誤的連接。或者用于拒絕非法的報(bào)文段和拒絕連接請(qǐng)求。SYN:同步序號(hào),用于建立連接過程,在連接請(qǐng)求中,SYN = 1 和 ACK = 0 表示該數(shù)據(jù)段沒有使用捎帶的確認(rèn)域,而連接應(yīng)答捎帶一個(gè)確認(rèn),即 SYN = 1 和 ACK = 1。FIN:finish 標(biāo)志,用于釋放連接,為1時(shí)表示發(fā)送方已經(jīng)沒有數(shù)據(jù)發(fā)送了,即關(guān)閉本方數(shù)據(jù)流。
- TCP 校驗(yàn)和字段:占 16 比特。對(duì)整個(gè) TCP 報(bào)文段,即 TCP 頭部和 TCP 數(shù)據(jù)進(jìn)行校驗(yàn)和計(jì)算,并由目標(biāo)端進(jìn)行驗(yàn)證。
- 緊急指針字段:占 16 比特。它是一個(gè)偏移量,和序號(hào)字段中的值相加表示緊急數(shù)據(jù)最后一個(gè)字節(jié)的序號(hào)。
- 選項(xiàng)和填充:最常見的可選字段是最長報(bào)文大小,又稱為 MSS(Maximum Segment Size),每個(gè)連接方通常都在通信的第一個(gè)報(bào)文段(為建立連接而設(shè)置SYN標(biāo)志為1的那個(gè)段)中指明這個(gè)選項(xiàng),它表示本端所能接受的最大報(bào)文段的長度。選項(xiàng)長度不一定是32位的整數(shù)倍,所以要加填充位,即在這個(gè)字段中加入額外的零,以保證TCP頭是32的整數(shù)倍。
- 數(shù)據(jù)部分: TCP 報(bào)文段中的數(shù)據(jù)部分是可選的。在一個(gè)連接建立和一個(gè)連接終止時(shí),雙方交換的報(bào)文段僅有 TCP 首部。如果一方?jīng)]有數(shù)據(jù)要發(fā)送,也使用沒有任何數(shù)據(jù)的首部來確認(rèn)收到的數(shù)據(jù)。在處理超時(shí)的許多情況中,也會(huì)發(fā)送不帶任何數(shù)據(jù)的報(bào)文段。
TCP 三次握手
TCP是一個(gè)面向連接的協(xié)議,無論哪一方向另一方發(fā)送數(shù)據(jù)之前,都必須先在雙方之間建立一條連接,建立一條連接有以下過程。
- 第一次握手:建立連接時(shí),客戶端發(fā)送 syn 包(syn=x)到服務(wù)器,并進(jìn)入SYN_SENT狀態(tài),等待服務(wù)器確認(rèn);SYN:同步序列編號(hào)(Synchronize Sequence Numbers)。
- 第二次握手:服務(wù)器收到 syn 包,必須確認(rèn)客戶的 SYN(ack=x+1),同時(shí)自己也發(fā)送一個(gè) SYN 包(syn=y),即 SYN+ACK 包,此時(shí)服務(wù)器進(jìn)入 SYN_RECV 狀態(tài);
- 第三次握手:客戶端收到服務(wù)器的 SYN + ACK 包,向服務(wù)器發(fā)送確認(rèn)包 ACK(ack=y+1),此包發(fā)送完畢,客戶端和服務(wù)器進(jìn)入 ESTABLISHED(TCP連接成功)狀態(tài),完成三次握手。
這三個(gè)報(bào)文段完成連接的建立,這個(gè)過程成為三次握手。
SYN 攻擊
在三次握手過程中,Server 發(fā)送 SYN-ACK 之后,收到 Client 的 ACK 之前的 TCP 連接稱為半連接(half-open connect),此時(shí) Server 處于 SYN_RCVD 狀態(tài),當(dāng)收到 ACK 后,Server 轉(zhuǎn)入 ESTABLISHED 狀態(tài)。
SYN 攻擊就是 Client 在短時(shí)間內(nèi)偽造大量不存在的 IP 地址,并向 Server 不斷地發(fā)送 SYN 包,Server 回復(fù)確認(rèn)包,并等待 Client 的確認(rèn),由于源地址是不存在的,因此,Server 需要不斷重發(fā)直至超時(shí),這些偽造的 SYN 包將長時(shí)間占用未連接隊(duì)列,導(dǎo)致正常的 SYN 請(qǐng)求因?yàn)殛?duì)列滿而被丟棄,從而引起網(wǎng)絡(luò)堵塞甚至系統(tǒng)癱瘓。
SYN 攻擊時(shí)一種典型的 DDoS 攻擊,檢測 SYN 攻擊的方式非常簡單,即當(dāng) Server 上有大量半連接狀態(tài)且源 IP 地址是隨機(jī)的,則可以斷定遭到 SYN 攻擊了。
解決方案:
- 1、無效連接監(jiān)視釋放
- 2、延緩TCB分配方法
當(dāng)開放了一個(gè) TCP 端口后,該端口就處于 Listening 狀態(tài),不停地監(jiān)視發(fā)到該端口的 Syn 報(bào)文,
一旦接收到 Client 發(fā)來的 Syn 報(bào)文,就需要為該請(qǐng)求分配一個(gè) TCB(Transmission Control Block),并返回一個(gè) SYN ACK 命令,立即轉(zhuǎn)為 SYN-RECEIVED 即半開連接狀態(tài)。
通常一個(gè) TCB 至少需要 280 個(gè)字節(jié),在某些操作系統(tǒng)中 TCB 甚至需要 1300 個(gè)字節(jié),而某些操作系統(tǒng)在 SOCK 的實(shí)現(xiàn)上最多可開啟 512 個(gè)半開連接。
消耗服務(wù)器資源主要是因?yàn)楫?dāng) SYN 數(shù)據(jù)報(bào)文一到達(dá),系統(tǒng)立即分配 TCB,從而占用了資源。
解決:
收到 SYN 數(shù)據(jù)報(bào)文時(shí)不急于去分配TCB,而是先回應(yīng)一個(gè)SYN ACK報(bào)文,并在一個(gè)專用HASH表(Cache)中保存這種半開連接信息,直到收到正確的回應(yīng)ACK報(bào)文再分配TCB。
TCP終止連接過程(四次揮手)
- 客戶端進(jìn)程發(fā)出連接釋放報(bào)文,并且停止發(fā)送數(shù)據(jù)。釋放數(shù)據(jù)報(bào)文首部,F(xiàn)IN=1,其序列號(hào)為 seq=u(等于前面已經(jīng)傳送過來的數(shù)據(jù)的最后一個(gè)字節(jié)的序號(hào)加 1),此時(shí),客戶端進(jìn)入 FIN-WAIT-1(終止等待1)狀態(tài)。 TCP 規(guī)定,F(xiàn)IN報(bào)文段即使不攜帶數(shù)據(jù),也要消耗一個(gè)序號(hào)。
- 服務(wù)器收到連接釋放報(bào)文,發(fā)出確認(rèn)報(bào)文,ACK=1,ack=u+1,并且?guī)献约旱男蛄刑?hào) seq=v,此時(shí),服務(wù)端就進(jìn)入了CLOSE-WAIT(關(guān)閉等待)狀態(tài)。TCP服務(wù)器通知高層的應(yīng)用進(jìn)程,客戶端向服務(wù)器的方向就釋放了,這時(shí)候處于半關(guān)閉狀態(tài),即客戶端已經(jīng)沒有數(shù)據(jù)要發(fā)送了,但是服務(wù)器若發(fā)送數(shù)據(jù),客戶端依然要接受。這個(gè)狀態(tài)還要持續(xù)一段時(shí)間,也就是整個(gè) CLOSE-WAIT 狀態(tài)持續(xù)的時(shí)間。
- 客戶端收到服務(wù)器的確認(rèn)請(qǐng)求后,此時(shí),客戶端就進(jìn)入 FIN-WAIT-2(終止等待2)狀態(tài),等待服務(wù)器發(fā)送連接釋放報(bào)文(在這之前還需要接受服務(wù)器發(fā)送的最后的數(shù)據(jù))。
- 服務(wù)器將最后的數(shù)據(jù)發(fā)送完畢后,就向客戶端發(fā)送連接釋放報(bào)文,F(xiàn)IN=1,ack=u+1,由于在半關(guān)閉狀態(tài),服務(wù)器很可能又發(fā)送了一些數(shù)據(jù),假定此時(shí)的序列號(hào)為 seq=w,此時(shí),服務(wù)器就進(jìn)入了 LAST-ACK(最后確認(rèn))狀態(tài),等待客戶端的確認(rèn)。
- 客戶端收到服務(wù)器的連接釋放報(bào)文后,必須發(fā)出確認(rèn),ACK=1,ack=w+1,而自己的序列號(hào)是 seq=u+1,此時(shí),客戶端就進(jìn)入了 TIME-WAIT(時(shí)間等待)狀態(tài)。注意此時(shí) TCP連接還沒有釋放,必須經(jīng)過 2∗∗MSL(最長報(bào)文段壽命)的時(shí)間后,當(dāng)客戶端撤銷相應(yīng)的TCB后,才進(jìn)入 CLOSED 狀態(tài)。
- 服務(wù)器只要收到了客戶端發(fā)出的確認(rèn),立即進(jìn)入 CLOSED 狀態(tài)。同樣,撤銷 TCB 后,就結(jié)束了這次的 TCP 連接??梢钥吹剑?wù)器結(jié)束 TCP 連接的時(shí)間要比客戶端早一些。
TCP 狀態(tài)機(jī)
上圖是 TCP 的狀態(tài)機(jī)。
- CLOSED:狀態(tài)時(shí)初始狀態(tài)。
- LISTEN:被動(dòng)打開,服務(wù)器端的 狀態(tài)變?yōu)?LISTEN (監(jiān)聽)。被動(dòng)打開的概念:連接的一端的應(yīng)用程序通知操作系統(tǒng),希望建立一個(gè)傳入的連接。這時(shí)候操作系統(tǒng)為連接的這一端建立一個(gè)連接。與之對(duì)應(yīng)的是主動(dòng)連接:應(yīng)用程序通過主動(dòng)打開請(qǐng)求來告訴操作系統(tǒng)建立一個(gè)連接。
- SYNRECVD:服務(wù)器端收到 SYN 后,狀態(tài)為 SYN;發(fā)送 SYN ACK;
- SYN_SENTY:應(yīng)用程序發(fā)送 SYN 后,狀態(tài)為 SYN_SENT;
- ESTABLISHED:SYNRECVD 收到 ACK 后,狀態(tài)為 ESTABLISHED; SYN_SENT 在收到 SYN ACK,發(fā)送 ACK,狀態(tài)為 ESTABLISHED;
- CLOSE_WAIT:服務(wù)器端在收到 FIN 后,發(fā)送 ACK,狀態(tài)為 CLOSE_WAIT;如果此時(shí)服務(wù)器端還有數(shù)據(jù)需要發(fā)送,那么就發(fā)送,直到數(shù)據(jù)發(fā)送完畢;此時(shí),服務(wù)器端發(fā)送FIN,狀態(tài)變?yōu)?LAST_ACK;
- FIN_WAIT_1:應(yīng)用程序端發(fā)送 FIN,準(zhǔn)備斷開 TCP 連接;狀態(tài)從 ESTABLISHED -> FIN_WAIT_1;
- FIN_WAIT_2:應(yīng)用程序端只收到服務(wù)器端得 ACK 信號(hào),并沒有收到FIN信號(hào);說明服務(wù)器端還有數(shù)據(jù)傳輸,那么此時(shí)為半連接;
- TIME_WAIT:有兩種方式進(jìn)入該狀態(tài):FIN_WAIT_1進(jìn)入:此時(shí)應(yīng)用程序端口收到 FIN+ACK(而不是像 FIN_WAIT_2 那樣只收到 ACK,說明數(shù)據(jù)已經(jīng)發(fā)送完畢)并向服務(wù)器端口發(fā)送 ACK;FIN_WAIT_2進(jìn)入:此時(shí)應(yīng)用程序端口收到了 FIN,然后向服務(wù)器端發(fā)送 ACK;TIME_WAIT 是為了實(shí)現(xiàn) TCP 全雙工連接的可靠性關(guān)閉,用來重發(fā)可能丟失的 ACK 報(bào)文;需要持續(xù) 2 個(gè) MSL (最大報(bào)文生存時(shí)間):假設(shè)應(yīng)用程序端口在進(jìn)入 TIME_WAIT后,2 個(gè) MSL 時(shí)間內(nèi)并沒有收到FIN,說明應(yīng)用程序最后發(fā)出的 ACK 已經(jīng)收到了;否則,會(huì)在 2 個(gè) MSL 內(nèi)在此收到ACK報(bào)文;
客戶端應(yīng)用程序的狀態(tài)遷移圖
客戶端的狀態(tài)可以用如下的流程來表示:
CLOSED -> SYN_SENT -> ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED
以上流程是在程序正常的情況下應(yīng)該有的流程,從書中的圖中可以看到,在建立連接時(shí),當(dāng)客戶端收到 SYN 報(bào)文的 ACK 以后,客戶端就打開了數(shù)據(jù)交互地連接。而結(jié)束連接則通常是客戶端主動(dòng)結(jié)束的,客戶端結(jié)束應(yīng)用程序以后,需要經(jīng)歷 FIN_WAIT_1,F(xiàn)IN_WAIT_2 等狀態(tài),這些狀態(tài)的遷移就是前面提到的結(jié)束連接的四次握手。
服務(wù)器的狀態(tài)遷移圖
服務(wù)器的狀態(tài)可以用如下的流程來表示:
CLOSED -> LISTEN -> SYN 收到 -> ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED
在建立連接的時(shí)候,服務(wù)器端是在第三次握手之后才進(jìn)入數(shù)據(jù)交互狀態(tài),而關(guān)閉連接則是在關(guān)閉連接的第二次握手以后(注意不是第四次)。而關(guān)閉以后還要等待客戶端給出最后的ACK 包才能進(jìn)入初始的狀態(tài)。
其他狀態(tài)遷移
書中的圖還有一些其他的狀態(tài)遷移,這些狀態(tài)遷移針對(duì)服務(wù)器和客戶端兩方面的總結(jié)如下
- LISTEN -> SYN_SENT,對(duì)于這個(gè)解釋就很簡單了,服務(wù)器有時(shí)候也要打開連接的嘛。
- SYN_SENT -> SYN 收到,服務(wù)器和客戶端在 SYN_SENT 狀態(tài)下如果收到 SYN 數(shù)據(jù)報(bào),則都需要發(fā)送 SYN 的 ACK 數(shù)據(jù)報(bào)并把自己的狀態(tài)調(diào)整到 SYN 收到狀態(tài),準(zhǔn)備進(jìn)入ESTABLISHED
- SYN_SENT -> CLOSED,在發(fā)送超時(shí)的情況下,會(huì)返回到 CLOSED 狀態(tài)。
- SYN_收到 -> LISTEN,如果受到 RST 包,會(huì)返回到 LISTEN 狀態(tài)。
- SYN_收到 -> FIN_WAIT_1,這個(gè)遷移是說,可以不用到 ESTABLISHED 狀態(tài),而可以直接跳轉(zhuǎn)到 FIN_WAIT_1 狀態(tài)并等待關(guān)閉。
2 MSL 等待狀態(tài)
書中給的圖里面,有一個(gè) TIME_WAIT 等待狀態(tài),這個(gè)狀態(tài)又叫做 2 MSL 狀態(tài),說的是在 TIME_WAIT2 發(fā)送了最后一個(gè) ACK 數(shù)據(jù)報(bào)以后,要進(jìn)入 TIME_WAIT 狀態(tài),這個(gè)狀態(tài)是防止最后一次握手的數(shù)據(jù)報(bào)沒有傳送到對(duì)方那里而準(zhǔn)備的(注意這不是四次握手,這是第四次握手的保險(xiǎn)狀態(tài))。這個(gè)狀態(tài)在很大程度上保證了雙方都可以正常結(jié)束,但是,問題也來了。
由于插口的 2MSL 狀態(tài)(插口是IP和端口對(duì)的意思,socket),使得應(yīng)用程序在 2 MSL 時(shí)間內(nèi)是無法再次使用同一個(gè)插口的,對(duì)于客戶程序還好一些,但是對(duì)于服務(wù)程序,例如httpd,它總是要使用同一個(gè)端口來進(jìn)行服務(wù),而在 2 MSL 時(shí)間內(nèi),啟動(dòng) httpd 就會(huì)出現(xiàn)錯(cuò)誤(插口被使用)。為了避免這個(gè)錯(cuò)誤,服務(wù)器給出了一個(gè)平靜時(shí)間的概念,這是說在 2 MSL 時(shí)間內(nèi),雖然可以重新啟動(dòng)服務(wù)器,但是這個(gè)服務(wù)器還是要平靜的等待 2 MSL 時(shí)間的過去才能進(jìn)行下一次連接。
FIN_WAIT_2 狀態(tài)
這就是著名的半關(guān)閉的狀態(tài)了,這是在關(guān)閉連接時(shí),客戶端和服務(wù)器兩次握手之后的狀態(tài)。在這個(gè)狀態(tài)下,應(yīng)用程序還有接受數(shù)據(jù)的能力,但是已經(jīng)無法發(fā)送數(shù)據(jù),但是也有一種可能是,客戶端一直處于 FIN_WAIT_2 狀態(tài),而服務(wù)器則一直處于 WAIT_CLOSE 狀態(tài),而直到應(yīng)用層來決定關(guān)閉這個(gè)狀態(tài)。
RST,同時(shí)打開和同時(shí)關(guān)閉
RST 是另一種關(guān)閉連接的方式,應(yīng)用程序應(yīng)該可以判斷 RST 包的真實(shí)性,即是否為異常中止。而同時(shí)打開和同時(shí)關(guān)閉則是兩種特殊的 TCP 狀態(tài),發(fā)生的概率很小。
常見面試題
【問題1】為什么連接的時(shí)候是三次握手,關(guān)閉的時(shí)候卻是四次握手?
答:因?yàn)楫?dāng) Server 端收到 Client 端的 SYN 連接請(qǐng)求報(bào)文后,可以直接發(fā)送 SYN+ACK 報(bào)文。其中 ACK 報(bào)文是用來應(yīng)答的,SYN 報(bào)文是用來同步的。但是關(guān)閉連接時(shí),當(dāng) Server 端收到 FIN 報(bào)文時(shí),很可能并不會(huì)立即關(guān)閉 SOCKET,所以只能先回復(fù)一個(gè) ACK 報(bào)文,告訴 Client 端,"你發(fā)的 FIN 報(bào)文我收到了"。只有等到我 Server 端所有的報(bào)文都發(fā)送完了,我才能發(fā)送 FIN 報(bào)文,因此不能一起發(fā)送。故需要四步握手。
【問題2】為什么TIME_WAIT狀態(tài)需要經(jīng)過2MSL(最大報(bào)文段生存時(shí)間)才能返回到CLOSE狀態(tài)?
雖然按道理,四個(gè)報(bào)文都發(fā)送完畢,我們可以直接進(jìn)入 CLOSE 狀態(tài)了,但是我們必須假象網(wǎng)絡(luò)是不可靠的,有可以最后一個(gè) ACK 丟失。所以TIME_WAIT狀態(tài)就是用來重發(fā)可能丟失的 ACK 報(bào)文。在 Client 發(fā)送出最后的 ACK 回復(fù),但該 ACK 可能丟失。Server 如果沒有收到 ACK,將不斷重復(fù)發(fā)送 FIN 片段。所以 Client 不能立即關(guān)閉,它必須確認(rèn) Server 接收到了該ACK。Client 會(huì)在發(fā)送出ACK 之后進(jìn)入到 TIME_WAIT 狀態(tài)。Client 會(huì)設(shè)置一個(gè)計(jì)時(shí)器,等待 2MSL 的時(shí)間。如果在該時(shí)間內(nèi)再次收到FIN,那么 Client 會(huì)重發(fā) ACK 并再次等待2 MSL。所謂的 2 MSL 是兩倍的 MSL(Maximum Segment Lifetime)。MSL 指一個(gè)片段在網(wǎng)絡(luò)中最大的存活時(shí)間,2MSL 就是一個(gè)發(fā)送和一個(gè)回復(fù)所需的最大時(shí)間。如果直到 2 MSL,Client 都沒有再次收到 FIN,那么 Client 推斷 ACK 已經(jīng)被成功接收,則結(jié)束 TCP 連接。
【問題3】為什么不能用兩次握手進(jìn)行連接?
3 次握手完成兩個(gè)重要的功能,既要雙方做好發(fā)送數(shù)據(jù)的準(zhǔn)備工作(雙方都知道彼此已準(zhǔn)備好),也要允許雙方就初始序列號(hào)進(jìn)行協(xié)商,這個(gè)序列號(hào)在握手過程中被發(fā)送和確認(rèn)。
現(xiàn)在把三次握手改成僅需要兩次握手,死鎖是可能發(fā)生的。作為例子,考慮計(jì)算機(jī) S 和 C 之間的通信,假定 C 給 S 發(fā)送一個(gè)連接請(qǐng)求分組,S 收到了這個(gè)分組,并發(fā) 送了確認(rèn)應(yīng)答分組。按照兩次握手的協(xié)定,S 認(rèn)為連接已經(jīng)成功地建立了,可以開始發(fā)送數(shù)據(jù)分組。可是,C 在 S 的應(yīng)答分組在傳輸中被丟失的情況下,將不知道 S 是否已準(zhǔn)備好,不知道 S 建立什么樣的序列號(hào),C 甚至懷疑 S 是否收到自己的連接請(qǐng)求分組。在這種情況下,C 認(rèn)為連接還未建立成功,將忽略 S 發(fā)來的任何數(shù)據(jù)分組,只等待連接確認(rèn)應(yīng)答分組。而 S 在發(fā)出的分組超時(shí)后,重復(fù)發(fā)送同樣的分組。這樣就形成了死鎖。
【問題4】如果已經(jīng)建立了連接,但是客戶端突然出現(xiàn)故障了怎么辦?
TCP 還設(shè)有一個(gè)?;钣?jì)時(shí)器,顯然,客戶端如果出現(xiàn)故障,服務(wù)器不能一直等下去,白白浪費(fèi)資源。服務(wù)器每收到一次客戶端的請(qǐng)求后都會(huì)重新復(fù)位這個(gè)計(jì)時(shí)器,時(shí)間通常是設(shè)置為 2 小時(shí),若兩小時(shí)還沒有收到客戶端的任何數(shù)據(jù),服務(wù)器就會(huì)發(fā)送一個(gè)探測報(bào)文段,以后每隔 75 秒鐘發(fā)送一次。若一連發(fā)送 10 個(gè)探測報(bào)文仍然沒反應(yīng),服務(wù)器就認(rèn)為客戶端出了故障,接著就關(guān)閉連接
樹林美麗、幽暗而深邃,但我有諾言尚待實(shí)現(xiàn),還要奔行百里方可沉睡。 -- 羅伯特·弗羅斯特
來源:https://www.cnblogs.com/huansky/p/13951567.html