關(guān)于 TCP(Transmission Control Protocol) 三次握手這個問題,基本上就是面試必問,是一個非常熱門的問題。先當(dāng)初我找實習(xí)的時候也被問到了,當(dāng)那個時候只能大概說一下過程,一些標(biāo)記位什么的還是不懂。
今天我們就大概講一下,應(yīng)付面試應(yīng)該可以了。
標(biāo)記位
我們這里先說要一下標(biāo)記位和標(biāo)記位的含義,這有助于后面理解整個握手的過程。
我們先來看一下 TCP 報文的格式吧。

我們要講 TCP 握手的過程,就要先了解圖中紅色框出來的幾個信息,下面我們一一解釋。
序列號(Sequence number)
占4個字節(jié),用來標(biāo)記數(shù)據(jù)段的順序,TCP 把連接中發(fā)送的所有數(shù)據(jù)字節(jié)都編上一個序號,第一個字節(jié)的編號由本地隨機(jī)產(chǎn)生;給字節(jié)編上序號后,就給每一個報文段指派一個序號;序列號seq就是這個報文段中的第一個字節(jié)的數(shù)據(jù)編號。
我們這里也補(bǔ)充一點(diǎn)網(wǎng)絡(luò)的知識吧,我們都知道 TCP 是屬于傳輸層,要保障數(shù)據(jù)準(zhǔn)確完整的傳輸,而在網(wǎng)絡(luò)傳輸中,一般都是要分包的,因為如果不分包,要是數(shù)據(jù)丟失了,要重新傳的代價太大了,我們分成一小段一小段進(jìn)行傳輸,這樣即使有一小段丟失了,我們就只要重新傳這一小段就可以了,可以節(jié)省很多流量。
TCP 在傳輸數(shù)據(jù)的時候,會順便把數(shù)據(jù)放到重發(fā)隊列里面,然后啟動計時器,如果后面接收到了這個包的確認(rèn)信息,就把這個數(shù)據(jù)包從隊列里面刪除掉,如果計時器超時了都還沒有收到確認(rèn)信息,就重新發(fā)送這個數(shù)據(jù)包,因為數(shù)據(jù)可能丟失了,對面沒有收到。另外因為有序列號的存在,在接受到全部數(shù)據(jù)之后就可以按照順序重新組裝起來了,保障了數(shù)據(jù)的準(zhǔn)確性和完整性。
說人話,就是有點(diǎn)類似 ID 一般,給這次連接定一個序列號。后面?zhèn)鬏敂?shù)據(jù)就從這個序號開始。
后面我們就用 seq 來表示 序列號。
ACK (Acknowledgement)
占1位,僅當(dāng) ACK=1 時,確認(rèn)號字段才有效。ACK=0時,確認(rèn)號無效。
說人話,ACK 就是相當(dāng)于一個開關(guān),確認(rèn)號 就相當(dāng)于燈泡的顏色,無論你這個燈泡是什么顏色,如果開關(guān)不打開,就什么都看不到,也就是說無效。ACK=1 時,這個開關(guān)就打開了,ACK=0時,這個開關(guān)就是關(guān)閉了。這樣是不是好理解多了。
ack(Acknowledge number)
剛才我們說了,確認(rèn)號收 ACK 控制,只有 ACK=1 時,確認(rèn)號才有效。
那問題又來了,這個確認(rèn)號是干什么用的呢?
確認(rèn)號就是用來確認(rèn)的,比如就好像你微信發(fā)了一個文件給我,我回復(fù)你一句“收到”是同樣的道理,就是用來跟對方確認(rèn)已經(jīng)收到了消息。
后面我們就用 ack 來表示 確認(rèn)號。
SYN(synchronous)
這個信號也是很關(guān)鍵,SYN 的意思就是說請求同步,也就是請求連接的意思。
三次握手
說完了幾個關(guān)鍵的標(biāo)記信息,我們就開始來說 TCP 建立的三次握手。
- 主機(jī)A 向 主機(jī)B 發(fā)送一個信號,【SYN=1,ACK=0,seq=x】,這個信號的意思就是說“主機(jī)B,我想跟你建立連接”。主機(jī)A 就會進(jìn)入 SYN-SENT 狀態(tài)。
- 主機(jī)B 收到 主機(jī)A 的請求之后,就會做出判斷,如果同意的話,就會回一個消息 【SYN=1,ACK=1,ack=x+1,seq=y】,意思是說,你的請求我收到了,我也想跟你建立連接。主機(jī)B 的狀態(tài)由 LISTEN 進(jìn)入 SYN-RCVD 狀態(tài)。
- 主機(jī)A 收到 主機(jī)B 的響應(yīng)后,如果要建立連接就會回一個消息 【SYN=0,ACK=1,ack=y+1,seq=x+1】,表示,我知道了,我要發(fā)送數(shù)據(jù)了。主機(jī)A 進(jìn)入 ESTABLISHED 狀態(tài)。
到此,TCP 通道就會建立了,后面就通過這個 TCP 通道傳輸數(shù)據(jù)了。為了方便大家理解,我從網(wǎng)上找了一個圖。

怎么樣?聽起來不會很復(fù)雜吧。
如果還不是很理解,我們就再場景化一下,想當(dāng)年我實習(xí)面試的時候就是這么回答的,哈哈哈。
先模擬一個場景,A 要通過 微信 向 B 發(fā)一個文件。因為不知道對面有沒有看微信,所以就要先問。
- 主機(jī)A 向 主機(jī)B 說,“嘿,我等一下要給你發(fā)個東西,你看到了沒有?”。
- 主機(jī)B 看到之后,就回答說,“哦,好的,我知道了。你要發(fā)前先跟我說一聲,我等著呢?!?/li>
- 主機(jī)A 看到了說,”O(jiān)K,我這就發(fā)了“。
這樣就不會那么抽象了吧,哈哈哈。可惜不會做圖,不然就做兩個圖,以后再學(xué)學(xué)做圖。
三次握手引申出的問題
為什么要三次握手,為什么不能是一次或者兩次?
這個問題也是非常常見的問題。三次握手是確認(rèn)雙方都在線、都準(zhǔn)備好發(fā)送和接受數(shù)據(jù),而且連接性能最優(yōu)的方式。
如果是一次握手根本就不可能,因為根本不可能確認(rèn)對方在線,不斷重發(fā)只會浪費(fèi)性能。
如果是兩次握手,那服務(wù)器收到客戶端一個連接請求就會建立連接。但是如果服務(wù)器給客戶端的響應(yīng)丟失了,那客戶端就根本不知道服務(wù)器接收到這個數(shù)據(jù)沒有,就會一直等待,另一方面,服務(wù)器也在等客戶端發(fā)送數(shù)據(jù),現(xiàn)在兩邊就都在等待對方,那建立的這條 TCP 通道就白白浪費(fèi)了。
同樣的,如果客戶端一開始發(fā)送的連接請求由于網(wǎng)絡(luò)延遲的問題,一直沒有到服務(wù)器,客戶端就會廢棄第一次的請求,重新發(fā)送一個新的連接請求,服務(wù)器接收到之后就會建立連接。這樣看似沒什么問題,但要是第一次后面又到達(dá)服務(wù)器了呢?由于只要兩次握手,服務(wù)器接收到第一次請求之后,回一個消息就可以建立 TCP 連接了,但是客戶端判定第一次的請求已經(jīng)廢棄了,也不會跟服務(wù)器建立 TCP 連接,也更不會發(fā)數(shù)據(jù)了,服務(wù)器就也白白浪費(fèi)了這個 TCP 連接。
如果在第三次握手的時候,數(shù)據(jù)丟失、服務(wù)器沒有收到怎么辦?
如果在第三次握手的時候,數(shù)據(jù)丟失,服務(wù)器沒有收到,但客戶端認(rèn)為 TCP 連接已經(jīng)建立了,開始發(fā)數(shù)據(jù)會有問題嗎?
有問題,這個時候客戶端就會收到服務(wù)器的 RST 應(yīng)答,客戶端就知道出現(xiàn)問題了,需要重新連接。
三次握手的核心
其實三次握手的主要核心就是確認(rèn)通信雙方都在線能夠,而且不會出現(xiàn)互相等待的情況。
如果只有一次握手,根本不能確認(rèn)對方在線。
如果是兩次握手,在理想情況下是能夠確立的,但是如果網(wǎng)絡(luò)出現(xiàn)問題,就會導(dǎo)致死鎖的情況。浪費(fèi)性能。
四次握手
既然三次握手都講完了,我們就加個餐,說一下四次握手吧。
四次握手是用來斷開 TCP 連接的。
為什么建立連接的時候只要三次,斷開卻要四次?
我們同樣先來講一下用到的一個關(guān)鍵標(biāo)記信息。
FIN(Finish)

FIN 標(biāo)記位,表示數(shù)據(jù)發(fā)送完畢(Finish),也就是請求斷開連接。
斷開連接 -- 四次握手
其實跟建立連接時的三次握手不同,主要因為,這個時候連接已經(jīng)建立了,有一方主動請求斷開連接,但是被動方可能還有一些數(shù)據(jù)正在發(fā)送,或者還有些數(shù)據(jù)沒發(fā)送,不能馬上中斷,要等這部分?jǐn)?shù)據(jù)發(fā)送完。
下面說一下四次握手的過程吧。
- 主機(jī)A 向 主機(jī)B 發(fā)送一個關(guān)閉連接請求?!綟IN=1,seq=u】,狀態(tài)由 ESTABLISHED 進(jìn)入 FIN-WAIT-1 。
- 主機(jī)B 接收到關(guān)閉請求,會響應(yīng) 【ACK=1,seq=v,ack=u+1】,告訴 主機(jī)A,你的請求是收到了,但是等一下,我還有東西沒發(fā)完。狀態(tài)由 ESTABLISHED 進(jìn)入 CLOSE-WAIT 。主機(jī)A 收到之后,就會進(jìn)入 FIN-WAIT-2 狀態(tài)。
- 主機(jī)B 等發(fā)送完剩余的數(shù)據(jù)之后,發(fā)送 【FIN=1,ACK=1,ack=u+1,seq=w】,然后就進(jìn)入 LAST-ACK 狀態(tài)。這里 seq 為什么是變了呢?因為剛才把剩余的數(shù)據(jù)發(fā)送出去了,seq 增加了。
- 主機(jī)A 接受到消息之后,回復(fù) 【ACK=1,seq=u+1,ack=w+1】,然后就進(jìn)入 TIME-WAIT 狀態(tài)。等待 兩倍最長報文壽命(2MSL)的時間之后,就進(jìn)入 CLOSE 狀態(tài)。主機(jī)B 接受到之后,就也進(jìn)入 CLOSE 狀態(tài)。
我也在網(wǎng)上找了一個圖,方便大家理解。

整個流程大概就是這樣了,如果覺得還不錯,麻煩點(diǎn)個贊再走吧。
也可以轉(zhuǎn)發(fā)給那些不太了解這塊的同學(xué),可以好好嘲諷一下學(xué)習(xí)一下,嘿嘿。
如果發(fā)現(xiàn)有問題,麻煩各位大佬在評論區(qū)指出。