推薦閱讀:
TCP三次握手、四次揮手,在面試這鍋滾油裏,可謂是炸了千百遍的老油條。
我們都知道TCP是面向連接的,三次握手
就是用來建立連接的,四次揮手
就是用來斷開連接的。
三次握手
先上圖:
我們來看一下三次握手的過程:
- 一開始,客戶端和服務端都處於
CLOSED
狀態。客戶端主動打開連接,服務端被動打卡連接,結束CLOSED
z狀態,開始監聽,進入LISTEN
狀態。
一次握手
- 客戶端會隨機初始化序號(
client_isn
),將此序號置於 TCP 首部的「序號」字段中,同時把SYN
標誌位置爲1
,表示SYN
報文。接着把第一個 SYN 報文發送給服務端,表示向服務端發起連接,該報文不包含應用層數據,之後客戶端處於SYN-SENT
狀態。
二次握手
- 服務端收到客戶端的
SYN
報文後,首先服務端也隨機初始化自己的序號(server_isn
),將此序號填入 TCP 首部的「序號」字段中,其次把 TCP 首部的「確認應答號」字段填入client_isn + 1
, 接着把SYN
和ACK
標誌位置爲1
。最後把該報文發給客戶端,該報文也不包含應用層數據,之後服務端處於SYN-RCVD
狀態。
三次握手
- 客戶端收到服務端報文後,還要向服務端迴應最後一個應答報文,首先該應答報文 TCP 首部
ACK
標誌位置爲1
,其次「確認應答號」字段填入server_isn + 1
,最後把報文發送給服務端,這次報文可以攜帶客戶到服務器的數據,之後客戶端處於ESTABLISHED
狀態。
好了,經過三次握手的過程,客戶端和服務端之間的確定連接正常,接下來進入ESTABLISHED
狀態,服務端和客戶端就可以快樂地通信了。
這裏有個動態過程的圖示:
這裏有個小細節,第三次握手是可以攜帶數據的,這是面試常問的點。
那麼爲什麼要三次握手呢?兩次不行嗎?
- 爲了防止服務器端開啓一些無用的連接增加服務器開銷
- 防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤。
由於網絡傳輸是有延時的(要通過網絡光纖和各種中間代理服務器),在傳輸的過程中,比如客戶端發起了 SYN=1 的第一次握手。
如果服務器端就直接創建了這個連接並返回包含 SYN、ACK 和 Seq 等內容的數據包給客戶端,這個數據包因爲網絡傳輸的原因丟失了,丟失之後客戶端就一直沒有接收到服務器返回的數據包。
如果沒有第三次握手告訴服務器端客戶端收的到服務器端傳輸的數據的話,服務器端是不知道客戶端有沒有接收到服務器端返回的信息的。服務端就認爲這個連接是可用的,端口就一直開着,等到客戶端因超時重新發出請求時,服務器就會重新開啓一個端口連接。
這樣一來,就會有很多無效的連接端口白白地開着,導致資源的浪費。
這個過程可理解爲:
還有一種情況是已經失效的客戶端發出的請求信息,由於某種原因傳輸到了服務器端,服務器端以爲是客戶端發出的有效請求,接收後產生錯誤。
所以我們需要“第三次握手”來確認這個過程:
通過第三次握手的數據告訴服務端,客戶端有沒有收到服務器“第二次握手”時傳過去的數據,以及這個連接的序號是不是有效的。若發送的這個數據是“收到且沒有問題
”的信息,接收後服務器就正常建立 TCP 連接,否則建立 TCP 連接失敗,服務器關閉連接端口。由此減少服務器開銷和接收到失效請求發生的錯誤。
四次揮手
還是先上圖:
聚散終有時,TCP 斷開連接是通過四次揮手方式。
雙方
都可以主動斷開連接,斷開連接後主機中的「資源」將被釋放。
上圖是客戶端主動關閉連接 :
一次揮手
- 客戶端打算關閉連接,此時會發送一個 TCP 首部
FIN
標誌位被置爲1
的報文,也即FIN
報文,之後客戶端進入FIN_WAIT_1
狀態。
二次揮手
- 服務端收到該報文後,就向客戶端發送
ACK
應答報文,接着服務端進入CLOSED_WAIT
狀態。
三次揮手
- 客戶端收到服務端的
ACK
應答報文後,之後進入FIN_WAIT_2
狀態。等待服務端處理完數據後,也向客戶端發送FIN
報文,之後服務端進入LAST_ACK
狀態。
四次揮手
- 客戶端收到服務端的
FIN
報文後,回一個ACK
應答報文,之後進入TIME_WAIT
狀態 - 服務器收到了
ACK
應答報文後,就進入了CLOSED
狀態,至此服務端已經完成連接的關閉。 - 客戶端在經過
2MSL
一段時間後,自動進入CLOSED
狀態,至此客戶端也完成連接的關閉。
你可以看到,每個方向都需要一個 FIN 和一個 ACK,因此通常被稱爲四次揮手。
爲什麼要揮手四次?
再來回顧下四次揮手雙方發 FIN
包的過程,就能理解爲什麼需要四次了。
- 關閉連接時,客戶端向服務端發送
FIN
時,僅僅表示客戶端不再發送數據了但是還能接收數據。 - 服務器收到客戶端的
FIN
報文時,先回一個ACK
應答報文,而服務端可能還有數據需要處理和發送,等服務端不再發送數據時,才發送FIN
報文給客戶端來表示同意現在關閉連接。
從上面過程可知,服務端通常需要等待完成數據的發送和處理,所以服務端的 ACK
和 FIN
一般都會分開發送,從而比三次握手導致多了一次。
爲什麼客戶端在TIME-WAIT階段要等2MSL?
爲的是確認服務器端是否收到客戶端發出的 ACK 確認報文,當客戶端發出最後的 ACK 確認報文時,並不能確定服務器端能夠收到該段報文。
所以客戶端在發送完 ACK 確認報文之後,會設置一個時長爲 2MSL 的計時器。
MSL 指的是 Maximum Segment Lifetime:一段 TCP 報文在傳輸過程中的最大生命週期。
2MSL 即是服務器端發出爲 FIN 報文和客戶端發出的 ACK 確認報文所能保持有效的最大時長。
服務器端在 1MSL 內沒有收到客戶端發出的 ACK 確認報文,就會再次向客戶端發出 FIN 報文:
- 如果客戶端在 2MSL 內,再次收到了來自服務器端的 FIN 報文,說明服務器端由於各種原因沒有接收到客戶端發出的 ACK 確認報文。
客戶端再次向服務器端發出 ACK 確認報文,計時器重置,重新開始 2MSL 的計時。
- 否則客戶端在 2MSL 內沒有再次收到來自服務器端的 FIN 報文,說明服務器端正常接收了 ACK 確認報文,客戶端可以進入 CLOSED 階段,完成“四次揮手”。
所以,客戶端要經歷時長爲 2SML 的 TIME-WAIT 階段;這也是爲什麼客戶端比服務器端晚進入 CLOSED 階段的原因。
這裏同樣有個動態過程的圖示:
好了,我們的文章到這就……
唉,不對,就這麼完了,這會我好像知道了,但過會兒那就說不定了。
沒關係,我苦思冥想,找了兩個大白話的例子,保準你忘不了。
大白話說三次握手
在二十年前的農村,電話沒有普及,手機就更不用說了,所以,通信基本靠吼。
老張和老王是鄰居,這天老張下地了,結果家裏有事,熱心的鄰居老王趕緊跑到村口,開始叫喚老王。
- 老王:老張唉!我是老王,你能聽到嗎?
- 老張一聽,是老王的聲音:老王老王,我是老張,我能聽到,你能聽到嗎?
- 老王一聽,嗯,沒錯,是老張:老張,我聽到了,我有事要跟你說。
"你老婆要生了,趕緊回家吧!"
老張風風火火地趕回家,老婆順利地生了個帶把的大胖小子。
握手的故事充滿了幸福和美滿。
大白話說四次揮手
假如博主有一個女朋友——只是“假如”,該死的,這不爭氣的眼淚,怎麼止不住地滴在鍵盤上。
由於博主上班九九六,下班肝博客,導致沒有時間陪女朋友,女朋友忍無可忍。
- 女朋友:臭男人,最近你都不理我,你是不是不愛我了?你是不是外面有別的狗子了?我要和你分手?
- 沙雕博主一愣,怒火攻心:分手就分手,不陪你鬧了,等我把東西收拾收拾。
沙雕博主小心翼翼地裝起了自己的青軸機械鍵盤。
- 哼,蠢女人,我已經收拾完了,我先滾爲敬,再見!
- 女朋友:滾,滾的遠遠的,越遠越好,我一輩子都不想再見到你。
唉,揮手的故事總充滿了悲傷和遺憾!
好了,白話純屬娛樂!看在博主費了不少腦子的份上,點個贊再走唄!!