面試必問!幫你一次搞定 TCP 三次握手

關於 TCP(Transmission Control Protocol) 三次握手這個問題,基本上就是面試必問,是一個非常熱門的問題。先當初我找實習的時候也被問到了,當那個時候只能大概說一下過程,一些標記位什麼的還是不懂。

今天我們就大概講一下,應付面試應該可以了。

標記位

我們這裡先說要一下標記位和標記位的含義,這有助於後面理解整個握手的過程。

我們先來看一下 TCP 報文的格式吧。

面試必問!幫你一次搞定 TCP 三次握手

我們要講 TCP 握手的過程,就要先了解圖中紅色框出來的幾個信息,下面我們一一解釋。

序列號(Sequence number)

佔4個字節,用來標記數據段的順序,TCP 把連接中發送的所有數據字節都編上一個序號,第一個字節的編號由本地隨機產生;給字節編上序號後,就給每一個報文段指派一個序號;序列號seq就是這個報文段中的第一個字節的數據編號。

我們這裡也補充一點網絡的知識吧,我們都知道 TCP 是屬於傳輸層,要保障數據準確完整的傳輸,而在網絡傳輸中,一般都是要分包的,因為如果不分包,要是數據丟失了,要重新傳的代價太大了,我們分成一小段一小段進行傳輸,這樣即使有一小段丟失了,我們就只要重新傳這一小段就可以了,可以節省很多流量。

TCP 在傳輸數據的時候,會順便把數據放到重發隊列裡面,然後啟動計時器,如果後面接收到了這個包的確認信息,就把這個數據包從隊列裡面刪除掉,如果計時器超時了都還沒有收到確認信息,就重新發送這個數據包,因為數據可能丟失了,對面沒有收到。另外因為有序列號的存在,在接收到全部數據之後就可以按照順序重新組裝起來了,保障了數據的準確性和完整性。

說人話,就是有點類似 ID 一般,給這次連接定一個序列號。後面傳輸數據就從這個序號開始。

後面我們就用 seq 來表示 序列號。

ACK (Acknowledgement)

佔1位,僅當 ACK=1 時,確認號字段才有效。ACK=0時,確認號無效。

說人話,ACK 就是相當於一個開關,確認號 就相當於燈泡的顏色,無論你這個燈泡是什麼顏色,如果開關不打開,就什麼都看不到,也就是說無效。ACK=1 時,這個開關就打開了,ACK=0時,這個開關就是關閉了。這樣是不是好理解多了。

確認號(Acknowledge number)

剛才我們說了,確認號受 ACK 控制,只有 ACK=1 時,確認號才有效。

那問題又來了,這個確認號是幹什麼用的呢?

確認號就是用來確認的,比如就好像你微信發了一個文件給我,我回復你一句“收到”是同樣的道理,就是用來跟對方確認已經收到了消息。

後面我們就用 ack 來表示 確認號。

SYN(synchronous)

這個信號也是很關鍵,SYN 的意思就是說請求同步,也就是請求連接的意思。

三次握手

說完了幾個關鍵的標記信息,我們就開始來說 TCP 建立的三次握手。

1.主機A 向 主機B 發送一個信號,【SYN=1,ACK=0,seq=x】,這個信號的意思就是說“主機B,我想跟你建立連接”。主機A 就會進入 SYN-SENT 狀態。

2.主機B 收到 主機A 的請求之後,就會做出判斷,如果同意的話,就會回一個消息 【SYN=1,ACK=1,ack=x+1,seq=y】,意思是說,你的請求我收到了,我也想跟你建立連接。主機B 的狀態由 LISTEN 進入 SYN-RCVD 狀態。

3.主機A 收到 主機B 的響應後,如果要建立連接就會回一個消息 【SYN=0,ACK=1,ack=y+1,seq=x+1】,表示,我知道了,我要發送數據了。主機A 進入 ESTABLISHED 狀態。

到此,TCP 通道就會建立了,後面就通過這個 TCP 通道傳輸數據了。為了方便大家理解,我從網上找了一個圖。

面試必問!幫你一次搞定 TCP 三次握手

怎麼樣?聽起來不會很複雜吧。

如果還不是很理解,我們就再場景化一下,想當年我實習面試的時候就是這麼回答的,哈哈哈。

先模擬一個場景,A 要通過 微信 向 B 發一個文件。因為不知道對面有沒有看微信,所以就要先問。

1.主機A 向 主機B 說,“嘿,我等一下要給你發個東西,你看到了沒有?”。

2.主機B 看到之後,就回答說,“哦,好的,我知道了。你要發前先跟我說一聲,我等著呢。“

3.主機A 看到了說,”OK,我這就發了“。

這樣就不會那麼抽象了吧,哈哈哈。可惜不會做圖,不然就做兩個圖,以後再學學做圖。

三次握手引申出的問題

為什麼要三次握手,為什麼不能是一次或者兩次?

這個問題也是非常常見的問題。三次握手是確認雙方都在線、都準備好發送和接受數據,而且連接性能最優的方式。

如果是一次握手根本就不可能,因為根本不可能確認對方在線,不斷重發只會浪費性能。

如果是兩次握手,那服務器收到客戶端一個連接請求就會建立連接。但是如果服務器給客戶端的響應丟失了,那客戶端就根本不知道服務器接收到這個數據沒有,就會一直等待,另一方面,服務器也在等客戶端發送數據,現在兩邊就都在等待對方,那建立的這條 TCP 通道就白白浪費了。

同樣的,如果客戶端一開始發送的連接請求由於網絡延遲的問題,一直沒有到服務器,客戶端就會廢棄第一次的請求,重新發送一個新的連接請求,服務器接收到之後就會建立連接。這樣看似沒什麼問題,但要是第一次後面又到達服務器了呢?由於只要兩次握手,服務器接收到第一次請求之後,回一個消息就可以建立 TCP 連接了,但是客戶端判定第一次的請求已經廢棄了,也不會跟服務器建立 TCP 連接,也更不會發數據了,服務器就也白白浪費了這個 TCP 連接。

如果在第三次握手的時候,數據丟失、服務器沒有收到怎麼辦?

如果在第三次握手的時候,數據丟失,服務器沒有收到,但客戶端認為 TCP 連接已經建立了,開始發數據會有問題嗎?

有問題,這個時候客戶端就會收到服務器的 RST 應答,客戶端就知道出現問題了,需要重新連接。

三次握手的核心

其實三次握手的主要核心就是確認通信雙方都在線能夠,而且不會出現互相等待的情況。

如果只有一次握手,根本不能確認對方在線。

如果是兩次握手,在理想情況下是能夠確立的,但是如果網絡出現問題,就會導致死鎖的情況。浪費性能。

四次握手

既然三次握手都講完了,我們就加個餐,說一下四次握手吧。

四次握手是用來斷開 TCP 連接的。

為什麼建立連接的時候只要三次,斷開卻要四次?

我們同樣先來講一下用到的一個關鍵標記信息。

FIN(Finish)

面試必問!幫你一次搞定 TCP 三次握手

FIN 標記位,表示數據發送完畢(Finish),也就是請求斷開連接。

斷開連接 -- 四次握手

其實跟建立連接時的三次握手不同,主要因為,這個時候連接已經建立了,有一方主動請求斷開連接,但是被動方可能還有一些數據正在發送,或者還有些數據沒發送,不能馬上中斷,要等這部分數據發送完。

下面說一下四次握手的過程吧。

1.主機A 向 主機B 發送一個關閉連接請求。【FIN=1,seq=u】,狀態由 ESTABLISHED 進入 FIN-WAIT-1

2.主機B 接收到關閉請求,會響應 【ACK=1,seq=v,ack=u+1】,告訴 主機A,你的請求是收到了,但是等一下,我還有東西沒發完。狀態由 ESTABLISHED 進入 CLOSE-WAIT 。主機A 收到之後,就會進入 FIN-WAIT-2 狀態。

3.主機B 等發送完剩餘的數據之後,發送 【FIN=1,ACK=1,ack=u+1,seq=w】,然後就進入

LAST-ACK 狀態。這裡 seq 為什麼是變了呢?因為剛才把剩餘的數據發送出去了,seq 增加了。

4.主機A 接受到消息之後,回覆 【ACK=1,seq=u+1,ack=w+1】,然後就進入 TIME-WAIT 狀態。等待 兩倍最長報文壽命(2MSL)的時間之後,就進入 CLOSE 狀態。主機B 接受到之後,就也進入 CLOSE 狀態。

我也在網上找了一個圖,方便大家理解。

面試必問!幫你一次搞定 TCP 三次握手

為什麼最後還要等待 2MSL(兩倍最長報文壽命) 之後再關閉連接呢?

還是網絡的問題,因為最後一次握手可能丟失,被動方(服務器)沒收到響應,可能會重發第三次握手信息(FIN標記)來關閉,之所以要等待 2MSL(兩倍最長報文壽命)就是為了可以在被動方(服務器)重發第三次握手信息(FIN標記)的時候進行響應。

挖坑

其實 TCP 連接中還有一個重要概念沒講,就是 窗口,以及與其相關的 窗口滑動 概念,我們下次有空再講吧。


整個流程大概就是這樣了,如果覺得還不錯,麻煩點個贊再走吧。

也可以轉發給那些不太瞭解這塊的同學,可以好好嘲諷一下學習一下,嘿嘿。

如果發現有問題,麻煩各位大佬在評論區指出。


分享到:


相關文章: