TCP握手與揮手

技術(shù)革命

格雷厄姆在《黑客與畫(huà)家》中提到一個(gè)觀點(diǎn),歷史上財(cái)富的積累無(wú)非兩種方式---偷竊和搶奪。新技術(shù)革命的出現(xiàn),財(cái)富的積累的新方式則是技術(shù)進(jìn)步。

技術(shù)進(jìn)步創(chuàng)造了無(wú)數(shù)的財(cái)富,而這個(gè)技術(shù)指的就是網(wǎng)絡(luò)技術(shù)。相對(duì)于人類的文明,網(wǎng)絡(luò)的歷史如同曇花一現(xiàn),恰恰是這么短的時(shí)間內(nèi),所創(chuàng)造的文明和財(cái)富卻是前所未有。相對(duì)于網(wǎng)絡(luò)自身的發(fā)展,其實(shí)已經(jīng)算是歷史悠久了。然而自從網(wǎng)絡(luò)誕生到現(xiàn)在,網(wǎng)絡(luò)的基礎(chǔ)架構(gòu)理論,基本的通信協(xié)議改變也不是很大。

我們所熟知的Internet,把全世界的人連接起來(lái),Inernet構(gòu)建于TCP/IP模型基礎(chǔ)之上。TCP/IP模型有著很多協(xié)議,其中與網(wǎng)絡(luò)(web)應(yīng)用開(kāi)發(fā)者息息相關(guān)的莫過(guò)于TCP協(xié)議。想要了解TCP協(xié)議的本質(zhì),起始于TCP的三次握手和四次揮手。

TCP 握手連接

連接是一個(gè)通信的行為。建立連接,就能使用連接進(jìn)行通信。連接作用于兩個(gè)節(jié)點(diǎn)。TCP是有狀態(tài)的協(xié)議,因此兩個(gè)節(jié)點(diǎn)之間想要通過(guò)tcp發(fā)送數(shù)據(jù),必須先建立可靠有效的連接。tcp是雙通道的通信模式,因此tcp的連接(邏輯連接)其實(shí)是兩條物理連接,即客戶端-->連接服務(wù)端--->客戶端的連接。

Handshake.png

上圖即是經(jīng)典的TCP三次握手。握手前,服務(wù)端創(chuàng)建socket對(duì)象,綁定地址,開(kāi)啟監(jiān)聽(tīng);客戶端創(chuàng)建socket,準(zhǔn)備連接。然后進(jìn)行三次握手連接:

  1. clinet向server端發(fā)送一個(gè)[SYN]包,seq=x。
  2. server收到[SYN]包之后,向clinet發(fā)送[SYN ACK] seq=y,ack=x+1
  3. clinet收到server的syn和ack之后,再向server端發(fā)送[ACK] ack=y+1包,至此三次握手完成。

創(chuàng)建一個(gè)tcp連接,通過(guò)三次握手即可。其實(shí)三次握手的clinet和server端是在不同的變化狀態(tài)。下面詳細(xì)討論下三次握手中的兩端的狀態(tài)變化。

TCP 握手c/s端狀態(tài)

針對(duì)三次握手的詳細(xì)過(guò)程,有如下過(guò)程:

  1. 發(fā)起握手的時(shí)候,clinet發(fā)送[SYN]包之后,自身馬上變成SYN_SENT狀態(tài),server則是進(jìn)行了listen,自身的狀態(tài)則變成LISTEN。接受到[SYN]包之后,自身變成SYN_RCVD狀態(tài)。這個(gè)過(guò)程主要含義是clinet向server建立一條發(fā)送數(shù)據(jù)的連接。

  2. server端收到了client的SYN之后,馬上會(huì)發(fā)送一個(gè)[SYN ACK]包,這里一共有兩個(gè)作用。ACK用于應(yīng)答client,表示client->server的連接已經(jīng)建立,同時(shí)server也想向client建立一條發(fā)送數(shù)據(jù)的連接,因此也需要發(fā)送一個(gè)[SYN]包。

  3. client收到了server的[SYN ACK]。通過(guò)server發(fā)的ACK確定了client->server這條連接建立。自身狀態(tài)變成了ESTABLISHED,表示可以正常的發(fā)送數(shù)據(jù)給server了。同時(shí)為了響應(yīng)server發(fā)送的建立連接的SYN請(qǐng)求,再次給server做一次ACK的應(yīng)答,一旦server收到clinet的ACK應(yīng)答,server的狀態(tài)也會(huì)變成ESTABLISHED

上述的過(guò)程中涉及到幾個(gè)狀態(tài),其中SYN_SENTSYN_RCVD非常短暫,使用nc等工具也很難看到這種狀態(tài)的。syn包表示希望建立一個(gè)連接,ack包表示應(yīng)答。連接的本質(zhì)是:

    client  ----- 發(fā)送syn ------>  server  希望創(chuàng)建連接
    client  <----- 發(fā)送ack ------  server  確定創(chuàng)建連接

之所以是三次握手,而不是第四次握手,是因?yàn)榈诙挝帐值臅r(shí)候,server把 [SYN] 和 [ACK]一起發(fā)送了。

客戶端 服務(wù)端
開(kāi)始狀態(tài) 過(guò)程 結(jié)束狀態(tài) 開(kāi)始狀態(tài) 過(guò)程 結(jié)束狀態(tài)
CLOSED client創(chuàng)建socket CLOSED CLOSED server創(chuàng)建socket,綁定地址,打開(kāi)監(jiān)聽(tīng) LISTEN
CLOSED [第一次握手]client向server發(fā)送SYN包 SYS-SENT LISTEN 等待client發(fā)送的SYN LISTEN
SYS-SENT 發(fā)送syn之后等待server發(fā)送ack SYS-SENT LISTEN [第二次握手]接受client的syn,同時(shí)向client發(fā)送[SYN ACK] SYN-RECEIVED
SYN-SENT 接受server的SYN+ACK,通過(guò)ack確定client->server連接創(chuàng)立,[第三次握手]同時(shí)針對(duì)SYN發(fā)送ACK ESTABLISHED SYN-RECEIVED 等待clinet發(fā)送的ACK確認(rèn) SYN-RECEIVED
ESTABLISHED 等待server完成對(duì)ack的響應(yīng),等待完成server->clinet的連接 ESTABLISHED SYN-RECEIVED 接受client的ACK確定,完成server->clinet的連接 ESTABLISHED
ESTABLISHED 發(fā)送接受數(shù)據(jù) ESTABLISHED ESTABLISHED 發(fā)送接收數(shù)據(jù) ESTABLISHED

參考

TCP 揮手?jǐn)噙B

了解了tcp的握手方式,那么揮手方式就很容易理解啦。與創(chuàng)建連接syn不一樣,想要端口連接需要發(fā)送的是fin包。同樣為了確定斷開(kāi)連接,需要發(fā)送ack應(yīng)答確認(rèn)包。大概方式入下圖:

Termination.png

斷開(kāi)tcp需要四步,斷開(kāi)連接既可以是client主動(dòng),server被動(dòng),也可以server主動(dòng)斷開(kāi)。兩種的狀態(tài)變化也是相對(duì)而言。下面以client主動(dòng)斷開(kāi)為例:

  1. client發(fā)送[FIN]包,表示要斷開(kāi)clinet->server的連接。
  2. server收到[FIN]之后,發(fā)送一個(gè)[ACK]包表示確定斷開(kāi)連接啦。
  3. 同時(shí)server也會(huì)向clinet再發(fā)一個(gè)[FIN]包,表示也想斷開(kāi)server->clinet的連接。
  4. clinet收到server的[ACK]包,確定了clinet->server的連接的斷開(kāi),該連接將不會(huì)發(fā)送數(shù)據(jù)啦。由于client也會(huì)收到server的[FIN]包,因此也要為斷開(kāi)server->clinet的連接發(fā)送[ACK]給server確定。

至此,四次揮手完成。下面詳細(xì)四次交互過(guò)程兩個(gè)端的狀態(tài)。

client和server端傳送數(shù)據(jù)的時(shí)候,雙方的狀態(tài)都是ESTABLISHED。一旦clinet發(fā)送了fin之后,自身變成FIN-WAIT-1的狀態(tài),意思是等待server端的ack確定。此時(shí),該通道不再向server發(fā)送數(shù)據(jù),但是仍然可以接收server數(shù)據(jù)了。

server收到了clinet發(fā)送的ack之后,自身的狀態(tài)由ESTABLISHED變成CLOSE_WAIT。client收到ack后,自身由FIN-WAIT-1變成FIN-WAIT-2,斷開(kāi)了連接。此時(shí)client在等待server端的fin信號(hào)。

server 發(fā)送 ack之后,自身就由CLOSE_WAIT變成LAST-ACK狀態(tài),即等待client的最后的ack確定。client收到server的fin包之后,自身就由 FIN-WAIT-2 變成 TIME_WAIT狀態(tài),等待一個(gè)2MSL時(shí)間之后,就變成了CLOSED狀態(tài)。

最后server收到client的ack確定之后,自身也變成CLOSED狀態(tài)。

客戶端 服務(wù)端
開(kāi)始狀態(tài) 過(guò)程 結(jié)束狀態(tài) 開(kāi)始狀態(tài) 過(guò)程 結(jié)束狀態(tài)
ESTABLISHED 向server發(fā)送FIN,表示想關(guān)閉clinet->server的連接 FIN-WAIT-1 ESTABLISHED 正常服務(wù),直到收到FIN ESTABLISHED
FIN-WAIT-1 發(fā)送完畢FIN,等待server的確認(rèn)應(yīng)答,此時(shí)將不會(huì)再發(fā)送數(shù)據(jù)給server FIN-WAIT-1 ESTABLISHED 接受FIN,發(fā)送ACK確認(rèn)斷開(kāi)連接 CLOSE-WAIT
FIN-WAIT-1 接收server發(fā)送的ACK,確定client->server連接關(guān)閉 FIN-WAIT-2 CLOSE-WAIT 發(fā)送FIN,表示關(guān)閉server->client的連接 LAST-ACK
FIN-WAIT-2 收到server的FIN包,發(fā)送ACK作為應(yīng)答 TIME-WAIT LAST-ACK 等待 client發(fā)送的ACK LAST-ACK
TIME-WAIT 等待MSL時(shí)間,保證ACK能被對(duì)方收到,等待fin否則重發(fā) TIME-WAIT LAST-ACK 接受ACK應(yīng)達(dá),關(guān)閉server->clinet的連接 CLOSED
TIME-WAIT MSL 時(shí)間過(guò)期 CLOSED CLOSED 連接關(guān)閉 CLOSED

參考

問(wèn)題

三次握手保證了TCP連接的可靠性,假設(shè)沒(méi)有三次握手只有兩次。如果client發(fā)送第一個(gè)syn的時(shí)候延遲很大,導(dǎo)致client發(fā)送了第二個(gè),第二個(gè)syn很快就到達(dá)server端。于是server發(fā)送ack確定連接,并發(fā)送syn。在兩次握手的情況下,服務(wù)器認(rèn)為連接都建立好了。如果此時(shí)第一個(gè)syn又抵達(dá)了服務(wù)器,那么服務(wù)器將會(huì)再次應(yīng)答,向client端發(fā)送連接請(qǐng)求。這樣會(huì)造成無(wú)效的連接,通過(guò)第三次握手,可以在server應(yīng)答無(wú)效連接的時(shí)候提前終止。也就是最后一次握手不再發(fā)送ack,那么server就不會(huì)再創(chuàng)建連接。

在關(guān)閉連接的時(shí)候,雖然兩個(gè)端都統(tǒng)一關(guān)閉連接,并且四次交互也發(fā)送完畢。假設(shè)如果網(wǎng)絡(luò)延遲很大,或者丟包嚴(yán)重,就很難保證client最后一次ack一定能被server收到。如果server收不到,會(huì)重發(fā)fin。為了解決這個(gè)問(wèn)題,通常在client發(fā)送ack之后2MSL時(shí)間,才由TIME_WAIT變成CLOSED狀態(tài),在此期間,client可以針對(duì)server補(bǔ)發(fā)的fin重發(fā)ack。

還有一個(gè)CLOSING狀態(tài),這個(gè)狀態(tài)表示client發(fā)送了FIN之后,并沒(méi)有收到ACK,反而先收到了server的FIN。當(dāng)然這種情況十分罕見(jiàn)的“異常”狀態(tài)。雙方都同時(shí)關(guān)閉連接,有可能在四次交互的時(shí)候,出現(xiàn)某些包延遲。

總結(jié)

TCP 連接和斷開(kāi)的過(guò)程中,都是一問(wèn)一答的方式,創(chuàng)建連接的問(wèn)是syn,回答是ack,同樣斷開(kāi)連接的問(wèn)是fin,回答是ack。有問(wèn)必有答,那么連接就能正常的創(chuàng)建和關(guān)閉。因?yàn)閠cp通信是雙通道的,因此一個(gè)TCP邏輯連接,實(shí)際上是兩條成對(duì)client和server端的物理連接。無(wú)論連接和斷開(kāi),都需要把這兩個(gè)連接都處理完畢才能完成。也因?yàn)檫@兩個(gè)過(guò)程,中間衍生出了很多狀態(tài)。這些狀態(tài)在創(chuàng)建連接的時(shí)候有client和server端的區(qū)分,在斷開(kāi)的連接的時(shí)候就沒(méi)有特別區(qū)別,只有主動(dòng)斷開(kāi)和被動(dòng)斷開(kāi)的差別。

至于tcp三次握手中兩端的“兩個(gè)連接”的通信通道,他們的具體原理和過(guò)程,我們將會(huì)在TCP連接與Python中詳細(xì)討論。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容