關於 TCP(Transmission Control Protocol) 三次握手這個問題,基本上就是面試必問,是一個非常熱門的問題。先當初我找實習的時候也被問到了,當那個時候只能大概說一下過程,一些標記位什麼的還是不懂。
今天我們就大概講一下,應付面試應該可以了。
標記位
我們這裡先說要一下標記位和標記位的含義,這有助於後面理解整個握手的過程。
我們先來看一下 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 通道傳輸數據了。為了方便大家理解,我從網上找了一個圖。
怎麼樣?聽起來不會很複雜吧。
如果還不是很理解,我們就再場景化一下,想當年我實習面試的時候就是這麼回答的,哈哈哈。
先模擬一個場景,A 要通過 微信 向 B 發一個文件。因為不知道對面有沒有看微信,所以就要先問。
1.主機A 向 主機B 說,“嘿,我等一下要給你發個東西,你看到了沒有?”。
2.主機B 看到之後,就回答說,“哦,好的,我知道了。你要發前先跟我說一聲,我等著呢。“
3.主機A 看到了說,”OK,我這就發了“。
這樣就不會那麼抽象了吧,哈哈哈。可惜不會做圖,不然就做兩個圖,以後再學學做圖。
三次握手引申出的問題
為什麼要三次握手,為什麼不能是一次或者兩次?
這個問題也是非常常見的問題。三次握手是確認雙方都在線、都準備好發送和接受數據,而且連接性能最優的方式。
如果是一次握手根本就不可能,因為根本不可能確認對方在線,不斷重發只會浪費性能。
如果是兩次握手,那服務器收到客戶端一個連接請求就會建立連接。但是如果服務器給客戶端的響應丟失了,那客戶端就根本不知道服務器接收到這個數據沒有,就會一直等待,另一方面,服務器也在等客戶端發送數據,現在兩邊就都在等待對方,那建立的這條 TCP 通道就白白浪費了。
同樣的,如果客戶端一開始發送的連接請求由於網絡延遲的問題,一直沒有到服務器,客戶端就會廢棄第一次的請求,重新發送一個新的連接請求,服務器接收到之後就會建立連接。這樣看似沒什麼問題,但要是第一次後面又到達服務器了呢?由於只要兩次握手,服務器接收到第一次請求之後,回一個消息就可以建立 TCP 連接了,但是客戶端判定第一次的請求已經廢棄了,也不會跟服務器建立 TCP 連接,也更不會發數據了,服務器就也白白浪費了這個 TCP 連接。
如果在第三次握手的時候,數據丟失、服務器沒有收到怎麼辦?
如果在第三次握手的時候,數據丟失,服務器沒有收到,但客戶端認為 TCP 連接已經建立了,開始發數據會有問題嗎?
有問題,這個時候客戶端就會收到服務器的 RST 應答,客戶端就知道出現問題了,需要重新連接。
三次握手的核心
其實三次握手的主要核心就是確認通信雙方都在線能夠,而且不會出現互相等待的情況。
如果只有一次握手,根本不能確認對方在線。
如果是兩次握手,在理想情況下是能夠確立的,但是如果網絡出現問題,就會導致死鎖的情況。浪費性能。
四次握手
既然三次握手都講完了,我們就加個餐,說一下四次握手吧。
四次握手是用來斷開 TCP 連接的。
為什麼建立連接的時候只要三次,斷開卻要四次?
我們同樣先來講一下用到的一個關鍵標記信息。
FIN(Finish)
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 狀態。
我也在網上找了一個圖,方便大家理解。
為什麼最後還要等待 2MSL(兩倍最長報文壽命) 之後再關閉連接呢?
還是網絡的問題,因為最後一次握手可能丟失,被動方(服務器)沒收到響應,可能會重發第三次握手信息(FIN標記)來關閉,之所以要等待 2MSL(兩倍最長報文壽命)就是為了可以在被動方(服務器)重發第三次握手信息(FIN標記)的時候進行響應。
挖坑
其實 TCP 連接中還有一個重要概念沒講,就是 窗口,以及與其相關的 窗口滑動 概念,我們下次有空再講吧。
整個流程大概就是這樣了,如果覺得還不錯,麻煩點個贊再走吧。
也可以轉發給那些不太瞭解這塊的同學,可以好好嘲諷一下學習一下,嘿嘿。
如果發現有問題,麻煩各位大佬在評論區指出。
閱讀更多 豬哥閒聊 的文章
關鍵字: 跳槽那些事兒 Transmission 搞定