TCP三向交握、四次揮手,在面試這鍋滾油裡,可謂是炸了千百遍的老油條。

三次握手
就是用來建立連接配接的, 四次揮手
就是用來斷開連接配接的。 三次握手
先上圖:
我們來看一下三次握手的過程:
- 一開始,用戶端和服務端都處于
狀态。用戶端主動打開連接配接,服務端被動打卡連接配接,結束CLOSED
z狀态,開始監聽,進入CLOSED
狀态。LISTEN
一次握手
- 用戶端會随機初始化序号(
),将此序号置于 TCP 首部的「序号」字段中,同時把client_isn
标志位置為SYN
,表示1
封包。接着把第一個 SYN 封包發送給服務端,表示向服務端發起連接配接,該封包不包含應用層資料,之後用戶端處于SYN
SYN-SENT
二次握手
- 服務端收到用戶端的
封包後,首先服務端也随機初始化自己的序号(SYN
),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确認應答号」字段填入server_isn
, 接着把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 階段的原因。
這裡同樣有個動态過程的圖示:
好了,我們的文章到這就……
唉,不對,就這麼完了,這會我好像知道了,但過會兒那就說不定了。
沒關系,我苦思冥想,找了兩個大白話的例子,保準你忘不了。
大白話說三次握手
在二十年前的農村,電話沒有普及,手機就更不用說了,是以,通信基本靠吼。
老張和老王是鄰居,這天老張下地了,結果家裡有事,熱心的鄰居老王趕緊跑到村口,開始叫喚老王。
- 老王:老張唉!我是老王,你能聽到嗎?
- 老張一聽,是老王的聲音:老王老王,我是老張,我能聽到,你能聽到嗎?
- 老王一聽,嗯,沒錯,是老張:老張,我聽到了,我有事要跟你說。
“你老婆要生了,趕緊回家吧!”
老張風風火火地趕回家,老婆順利地生了個帶把的大胖小子。
握手的故事充滿了幸福和美滿。
“三次握手,四次揮手”這麼講,保證你忘不了三次握手四次揮手大白話說三次握手大白話說四次揮手 大白話說四次揮手
假如部落客有一個女朋友——隻是“假如”,該死的,這不争氣的眼淚,怎麼止不住地滴在鍵盤上。
由于部落客上班九九六,下班肝部落格,導緻沒有時間陪女朋友,女朋友忍無可忍。
- 女朋友:臭男人,最近你都不理我,你是不是不愛我了?你是不是外面有别的狗子了?我要和你分手?
- 沙雕部落客一愣,怒火攻心:分手就分手,不陪你鬧了,等我把東西收拾收拾。
沙雕部落客小心翼翼地裝起了自己的青軸機械鍵盤。
- 哼,蠢女人,我已經收拾完了,我先滾為敬,再見!
- 女朋友:滾,滾的遠遠的,越遠越好,我一輩子都不想再見到你。
唉,揮手的故事總充滿了悲傷和遺憾!