不懂 TCP 三次握手、四次揮手?面試官:回去等通知吧!
?? 開篇:你以為你懂 TCP?其實……
來,問幾個簡單的問題:
1?? 為什么是"三次"握手?兩次不行嗎?四次多余嗎?
2?? 為什么是"四次"揮手?兩次不行嗎?
3?? 你知道 TCP 握手和揮手的整個流程嗎?每一步到底是在干嘛嗎?
如果這些問題你還不能完全確定,那你一定要繼續(xù)往下看。今天用最直白、最好玩的方式,帶你徹底搞懂 TCP 的握手與揮手!
?? 場景:夜店搭訕 vs 網(wǎng)絡(luò)連接
TCP 連接就像夜店搭訕(建立連接)和禮貌告別(斷開連接)。搞不好,分分鐘變成社死現(xiàn)場!讓我們跟著小明的故事,一步步理解這個過程。
?? 第一幕:三次握手 - "搭訕的藝術(shù)"
小明在夜店看到小紅,但他可不會傻傻地直接喊"做我女朋友!"(除非想被當成神經(jīng)?。K枰粋€穩(wěn)妥的三步走計劃:
[小明](客戶端) [小紅](服務(wù)器)
|----SYN----->| 小明:"嗨,可以認識一下嗎?"
|<--SYN-ACK---| 小紅:"可以呀,你是誰?"
|----ACK----->| 小明:"我是小明,程序員,喜歡貓..."
?? 完整的三次握手過程是這樣的:
圖片
來看看TCP三次握手是怎么玩的:
客戶端和服務(wù)器開始時都是"關(guān)門"狀態(tài)(CLOSED)。這就像兩個人要開始聊天:
- 客戶端先發(fā)個招呼(SYN=1, seq=x),說"嘿,能聊聊嗎?",然后等在那里(SYN_SENT狀態(tài))。服務(wù)器這時候是準備聽的狀態(tài)(LISTEN 狀態(tài))。
- 服務(wù)器收到后回應(yīng)說(SYN=1, ACK=1, seq=y, ack=x+1)"好啊,我聽見你了",自己也進入等待狀態(tài)(SYN_RCVD狀態(tài))。
- 客戶端最后確認一下(ACK=1, ack=y+1),相當于說"太好了,那我們開始聊吧"。這時候雙方都進入了可以正常聊天的狀態(tài)(ESTABLISHED狀態(tài))。
從技術(shù)角度看,這個過程不僅確認了雙方都能收發(fā)數(shù)據(jù),還完成了初始序列號的同步,建立了可靠的通信基礎(chǔ)。
?? 等等!為啥非要三次?兩次不行嗎?
?? 原因一:建立"雙向通信"(能收能發(fā))
想象你在KTV包廂點歌:
第一次:
你:(按麥克風按鈕)"喂喂?"
(確認你的麥克風能發(fā)聲)
第二次:
前臺:(通過音響)"聽到了,你要點什么歌?"
(確認音響能放聲,你能聽到)
第三次:
你:(通過麥克風)"我要點《野狼disco》!"
(確認你能聽到音響聲,整個通信鏈路都通了)
如果只有兩次對話:
- 你按了麥克風
- 前臺回應(yīng)了
- 但你可能沒聽到前臺的回應(yīng)
- 結(jié)果你在那自顧自地點歌
- 前臺:???到底要點啥
?? 在TCP協(xié)議中,這個雙向通信確認過程是這樣的:
第一次: SYN=1, seq=x
客戶端 ------------------> 服務(wù)器
(我能發(fā)消息,我的序號是x)
第二次: SYN=1, ACK=1, seq=y, ack=x+1
客戶端 <------------------ 服務(wù)器
(我能收能發(fā),收到你的x號消息)
第三次: ACK=1, ack=y+1
客戶端 ------------------> 服務(wù)器
(我也能收能發(fā),收到你的y號消息)
這就是TCP中的"全雙工通信"建立過程:
- 第一次握手:客戶端證明自己能發(fā)數(shù)據(jù)
- 第二次握手:服務(wù)器證明自己能收能發(fā)數(shù)據(jù)
- 第三次握手:客戶端證明自己能收數(shù)據(jù)
就像KTV包廂的設(shè)備測試:
- "喂喂?"(證明麥克風能用)
- "聽到了,你能聽到嗎?"(證明音響能放,工作人員能聽)
- "能聽到!"(證明整個系統(tǒng)都OK)
只有這樣才能確保:
- 客戶端和服務(wù)器都能發(fā)送數(shù)據(jù)
- 雙方都能接收對方的數(shù)據(jù)
- 通信鏈路是完全雙向通暢的 ?? 等等!為啥非要三次握手?兩次不行嗎?
?? 原因二:同步雙方的序號(確認彼此的初始序列號)
如果只有兩次握手:
客戶端 服務(wù)器
| |
|--------SYN seq=100-------->| 給你我的序號100
| |
|<--SYN+ACK seq=200,ack=101--| 好的,我用200
| |
問題:服務(wù)器不知道客戶端是否真的收到了它的200
- 兩次握手只能保證服務(wù)器收到了客戶端的初始序號100
- 但無法保證客戶端收到了服務(wù)器的初始序號200
- 雙方的初始序號必須都能被對方確認,兩次握手做不到這一點
所以必須要第三次握手:
客戶端 服務(wù)器
| |
|--SYN seq=100-------------->| 給你我的序號100
|<--SYN+ACK seq=200,ack=101--| 好的,我用200
|--ACK ack=201-------------->| 我知道你用200了
就像兩個人約定暗號:
- 小明:我用暗號100
- 小紅:收到你的100,我用暗號200
- 小明:好,我也收到你的200了
通過三次握手:
- 服務(wù)器確認收到了客戶端的序號
- 客戶端也確認收到了服務(wù)器的序號
- 雙方都知道對方的初始序號,可以開始可靠通信
這就是為什么必須是三次握手,因為只有這樣才能確保雙方的初始序號都被對方成功確認!
?? 原因三:防止過期消息(解決穿越)
[星期五晚上]
小明用手機打開交友APP:
> 9:00 - 發(fā)送好友申請,但信號不好沒發(fā)出去
> 9:05 - 重新發(fā)送申請,這次成功了!開始愉快聊天
> 9:30 - 突然!之前卡住的申請終于發(fā)出去了...
如果沒有第三次確認,會怎樣?
- 服務(wù)器收到 9:00 的遲到申請
- 回應(yīng)說:"好啊,我們開始聊天吧"
- 但這時用戶都聊了半小時了!
- 服務(wù)器還傻傻地為這個舊請求準備資源
- 結(jié)果:浪費服務(wù)器資源,還可能打斷正在進行的聊天。
有了第三次握手,就不會出現(xiàn)這種情況:
[9:30] 遲到的申請終于到達服務(wù)器
服務(wù)器:"收到你的申請啦!"
客戶端:(發(fā)現(xiàn)是半小時前的舊消息)不回應(yīng)
服務(wù)器:(沒收到回應(yīng),知道是舊消息,直接忽略)
?? 在TCP協(xié)議中,這個過程實際是這樣的:
[過期的SYN請求]
客戶端 ----遲到的SYN(seq=x)---------> 服務(wù)器
客戶端 <---SYN+ACK(seq=y,ack=x+1)--- 服務(wù)器
客戶端 (發(fā)現(xiàn)是舊請求,不回應(yīng)ACK)
服務(wù)器 (等待超時,關(guān)閉半連接)
這就涉及到TCP的幾個重要機制:
1.半連接隊列(SYN Queue)
- 服務(wù)器收到SYN后,會創(chuàng)建一個半連接
- 如果沒收到第三次ACK,這個連接就會被丟棄
- 避免了服務(wù)器資源被舊請求占用
2.連接超時機制
- 服務(wù)器會設(shè)置一個超時時間(通常是幾十秒)
- 在這個時間內(nèi)沒收到ACK就清理掉半連接
- 防止半連接隊列被占滿
3.序列號驗證
- 客戶端收到 SYN+ACK 時會驗證序列號
- 如果是過期請求,就不會發(fā)送ACK
- 服務(wù)器因此能識別出過期連接
這就是為什么TCP需要三次握手,而不是兩次的原因:
- 防止歷史連接的意外建立
- 保護服務(wù)器資源不被浪費
- 確保建立的都是有效連接
再想想上面的例子:
- SYN就像小明的好友申請
- SYN+ACK是服務(wù)器的回應(yīng)
- 沒有第三次ACK,就相當于小明沒確認
- 服務(wù)器就知道這是舊的申請,可以安全地忽略它
這個設(shè)計巧妙地解決了網(wǎng)絡(luò)中的"延遲消息"問題,是TCP協(xié)議最精華的部分之一!
?? 還有,為啥非要三次?四次不行嗎?
讓我們繼續(xù)用KTV點歌的例子:
[三次握手]
你:喂喂?(能發(fā)聲)
前臺:聽到了!(能收能發(fā))
你:好的?。ㄕ麄€鏈路通了)
[如果是四次]
你:喂喂?(能發(fā)聲)
前臺:聽到了?。苁盏剑?前臺:你要點歌嗎?(能發(fā)聲)
你:好的?。ù_認)
這樣看出問題了嗎?
第二次和第三次握手其實可以合并
前臺說"聽到了"的同時就能問"要點歌嗎"
沒必要分開說,那樣反而浪費時間
技術(shù)角度來說:
服務(wù)器收到 SYN 后:
- 已經(jīng)知道客戶端能發(fā)送數(shù)據(jù)
- 自己能收到數(shù)據(jù)
- 可以直接在 ACK 中帶上自己的 SYN
- 不需要分兩次發(fā)送
簡單說:
- 三次剛剛好:確認雙方都能收發(fā)
- 兩次不夠:無法確認客戶端能收
- 四次多余:把能合并的分開了
記?。篢CP設(shè)計的原則是既要保證可靠性,又要追求效率!
?? 小結(jié)
三次握手不是為了復(fù)雜,而是為了:
- 建立"雙向通信"(雙方都能收能發(fā))
- 同步雙方的序列號(保證通信順序)
- 防止歷史連接的意外建立(避免"延遲消息")
- 確保雙方都準備好了才開始通信
記?。好恳淮挝帐侄加兴嬖诘牡览?,不是技術(shù)人員閑著沒事干設(shè)計的,而是為了解決實際的網(wǎng)絡(luò)通信問題!
?? 第二幕:四次揮手 - "告別的藝術(shù)"
?? 繼續(xù)我們的夜店故事
還記得小明和小紅嗎?經(jīng)過三次握手,他們已經(jīng)聊得很開心了。但是夜店總要散場,到了說再見的時候??墒?,體面人的告別可不是說走就走!
?? 四次揮手是怎么回事?
分別的是時候,小明不能突然消失,得體面地說再見。我們看看這個過程:
[小明] [小紅]
| |
|--"我該走了"(FIN)-----> | 第一次:表達想走的意思
|<--"等等,我說完"(ACK)-- | 第二次:先別急,還有話說
|<--"好了說完了"(FIN)--- | 第三次:我也說完了,再見
|--"好,拜拜"(ACK)-----> | 第四次:最后的告別
?? 這在TCP協(xié)議中實際是這樣的:
客戶端 服務(wù)器
| 1. FIN=1, seq=x |
| ------------------------------>| 第一次:客戶端表示要關(guān)閉連接
| 2. ACK=1,seq=y,ack=x+1 |
| <-----------------------------| 第二次:服務(wù)器確認,但自己還有數(shù)據(jù)要發(fā)
| |
| (服務(wù)器發(fā)送剩余數(shù)據(jù)) |
| 3. FIN=1,ACK=1,seq=z,ack=x+1 |
| <-----------------------------| 第三次:服務(wù)器發(fā)完數(shù)據(jù),也準備關(guān)閉了
| 4. ACK=1,seq=x+1,ack=z+1 |
| -----------------------------> | 第四次:客戶端最后確認
| TIME_WAIT (2MSL等待...) |
?? 為什么要四次?兩次不行嗎?
想象一下這個尷尬的場景:
[如果只有兩次揮手]
小明:時間不早了,我要走了!
小紅:好的再見!
(雙方立馬分別)
問題是:小紅正要給小明微信號,結(jié)果小明已經(jīng)離開了... // 一個本該成功的搭訕,因為太急著說再見而失敗
為什么這樣不行?
- 小明說"我要走了"只是表示他不發(fā)消息了
- 但小紅可能還有話要說(比如發(fā)微信號)
- 如果立即分別,小紅的話就來不及說了。
技術(shù)角度來說:
[兩次揮手的問題]
客戶端 服務(wù)器
|---我不發(fā)消息了---------------->|
|<--好的再見,連接關(guān)閉-----------|
| | 服務(wù)器的數(shù)據(jù)發(fā)不出去了!
所以必須要四次:
[優(yōu)雅的告別]
小明:我要走了,不說話了
小紅:等等,我還有話要說
(把微信號給小明)
小紅:好了,我說完了
小明:收到,拜拜!
這就是為什么需要四次揮手:
- 讓雙方都能說完該說的話
- 避免重要信息發(fā)不出去
- 確保完整優(yōu)雅地斷開連接
?? 深入理解四次揮手
先來看看 TCP 四次揮手的詳細圖解:
圖片
1?? 第一次揮手:主動說再見
小明:"時間不早了,我該走了"
技術(shù)視角:
第一次揮手:FIN=1, seq=x
客戶端 -----------------> 服務(wù)器
(客戶端的數(shù)據(jù)發(fā)完了,準備關(guān)閉)
- 客戶端發(fā)送 FIN 包,序號為x
- 進入FIN_WAIT_1狀態(tài)
- 表示客戶端沒有數(shù)據(jù)要發(fā)了
2?? 第二次揮手:先別急
小紅:"等等,我還有話說..."
技術(shù)視角:
第二次揮手:ACK=1,seq=y,ack=x+1
客戶端 <----------------------- 服務(wù)器(服務(wù)器確認,好的,我知道了,但我還有數(shù)據(jù)要發(fā))
- 服務(wù)器發(fā)送ACK,序號為y,確認號為x+1
- 進入 CLOSE_WAIT 狀態(tài),表示服務(wù)器可能還有數(shù)據(jù)要發(fā)送
- 當客戶端收到 ACK 包時,客戶端進入 FIN_WAIT_2 狀態(tài)
3?? 第三次揮手:處理完了
小紅:"好了,聯(lián)系方式給你了,我也該說再見了"
技術(shù)視角:
第三次揮手:FIN=1, seq=z, ack=x+1
客戶端 <------------------------- 服務(wù)器(我的數(shù)據(jù)也發(fā)完了,準備關(guān)閉)
- 服務(wù)器發(fā)送FIN包,序號為 z,確認號是x+1
- 進入LAST_ACK狀態(tài)
- 表示服務(wù)器的數(shù)據(jù)也發(fā)送完畢
4?? 第四次揮手:最后道別
小明:"好的,拜拜!"(但還會在門口等一會兒)
技術(shù)視角:
第四次揮手:ACK=1,seq=x+1,ack=z+1
客戶端 --------------------------> 服務(wù)器
(好的,我知道了,可以關(guān)閉了)
- 客戶端發(fā)送ACK,序列號為x+1,確認號為z+1
- 進入 TIME_WAIT 狀態(tài)
- 等待 2MSL 后才真正關(guān)閉
最后,雙方都進入 CLOSED 狀態(tài)。
?? 為什么要等待2MSL?
想象這個場景:
小明說完"拜拜"后,沒有立即離開
而是在夜店門口等了一會兒,因為:
1. 萬一小紅沒聽清他說的"拜拜"
技術(shù)角度解釋:
客戶端 服務(wù)器
...
| 4. ACK=1,seq=x+1,ack=z+1 ---------------> | 第四次:客戶端最后確認
| TIME_WAIT (2MSL等待...) |
- MSL是數(shù)據(jù)包的最大生存時間(一般是幾十秒)
- 等待 2MSL 是為了:
- 確保最后的ACK能到達服務(wù)器
- 等待可能的FIN重傳
- 防止舊連接的數(shù)據(jù)包影響新連接
舊連接的數(shù)據(jù)包影響新連接是什么意思?
讓我通過一個具體的例子來解釋"防止舊連接的數(shù)據(jù)包影響新連接"。
假設(shè)沒有2MSL等待時間:
客戶端A 服務(wù)器
| 1. 斷開連接 |
| 2. 馬上重新建立新連接 |
| 迷路的數(shù)據(jù)包 ------> |
| (來自剛才的舊連接) |
| 結(jié)果:服務(wù)器會把舊數(shù)據(jù)包當成新連接的數(shù)據(jù)! |
# 假設(shè)客戶端A和服務(wù)器的 ip 地址 和 端口 保持不變,即:四元組保持不變
就像這樣的場景:
[夜店場景]
21:00 - 小明和小紅說再見,立刻離開
21:01 - 小明又和小紅相約夜店,可能有重要的事...
21:02 - 突然!服務(wù)員送來小明之前給小紅點的酒...
小紅:???(搞不清這是之前的單,還是新的單)
有了2MSL等待:
客戶端A 服務(wù)器
| 1. 斷開連接 |
| 2. 等待2MSL... |
| (足夠讓之前連接的所有數(shù)據(jù)包都消失) |
| 3. 這時候建立新連接 |
| (不會有舊數(shù)據(jù)包來搗亂) |
簡單說:
- 2MSL的等待就是讓之前的所有互動(數(shù)據(jù)包)都完全結(jié)束,不會讓舊數(shù)據(jù)包來影響新連接。
?? 四次揮手的精髓
在生活中:
- 雙方都要把話說完
- 確保對方聽到了告別
- 給對方充足的處理時間
- 不能突然消失
在TCP中:
- 確保數(shù)據(jù)完整傳輸
- 雙方都能正常關(guān)閉
- 防止數(shù)據(jù)丟失
- 資源能被正確釋放
記住:四次揮手的每一步都是必要的,是為了讓網(wǎng)絡(luò)通信更可靠!
總結(jié):
通過這篇文章,TCP的三次握手和四次揮手的來龍去脈應(yīng)該都清楚了。TCP 之所以這樣設(shè)計,是為了實現(xiàn)可靠的網(wǎng)絡(luò)通信:三次握手確保雙向通信可靠、序列號同步以及避免歷史連接;四次揮手則確保數(shù)據(jù)傳輸完整、連接優(yōu)雅關(guān)閉,以及防止舊連接影響新連接。看似繁瑣的每一次握手和揮手,其實都是為了解決具體的網(wǎng)絡(luò)通信問題,體現(xiàn)了 TCP 協(xié)議的可靠性和嚴謹性。一旦理解了這些設(shè)計背后的原因,也就理解了 TCP 協(xié)議的精髓。