骄W(wǎng)絡(luò)工程實(shí)踐)
在面試和日常技術(shù)交流中“TCP為什么是三次握手而不是兩次或四次”幾乎是每個(gè)網(wǎng)絡(luò)工程師和開(kāi)發(fā)者都會(huì)遇到的經(jīng)典問(wèn)題。很多人會(huì)用一個(gè)生活化的比喻來(lái)理解兩個(gè)人握手不就是伸出兩只手一次接觸就完成了嗎為什么TCP這個(gè)“握手”要來(lái)回三次呢這個(gè)看似簡(jiǎn)單的問(wèn)題背后卻蘊(yùn)含著TCP協(xié)議設(shè)計(jì)者對(duì)于網(wǎng)絡(luò)可靠性、效率和安全性的深刻權(quán)衡。本文將徹底拆解TCP三次握手的核心原理從報(bào)文交互細(xì)節(jié)到設(shè)計(jì)哲學(xué)并通過(guò)Wireshark抓包實(shí)戰(zhàn)讓你不僅知其然更能知其所以然。1. TCP連接的本質(zhì)與“握手”的隱喻在深入三次握手之前我們必須先澄清一個(gè)常見(jiàn)的誤解TCP的“握手”是一個(gè)技術(shù)術(shù)語(yǔ)它模擬的是人類建立聯(lián)系前確認(rèn)彼此身份和意愿的過(guò)程但其核心目標(biāo)與人類握手有本質(zhì)區(qū)別。人類握手的主要目的是禮節(jié)性問(wèn)候一次接觸兩次握手足以傳達(dá)“我見(jiàn)到你了我準(zhǔn)備好交流了”的信號(hào)。但TCP連接建立在一個(gè)不可靠的、可能存在延遲、重復(fù)、丟失、亂序的網(wǎng)絡(luò)IP網(wǎng)絡(luò)之上。因此TCP握手的目標(biāo)要復(fù)雜得多確認(rèn)雙方的“存在”與“可達(dá)性”確保對(duì)方主機(jī)在線且指定端口正在監(jiān)聽(tīng)。同步初始序列號(hào)這是TCP實(shí)現(xiàn)可靠傳輸?shù)幕?。序列?hào)用于對(duì)字節(jié)流進(jìn)行編號(hào)保證數(shù)據(jù)按序到達(dá)、檢測(cè)重復(fù)和丟失。交換基礎(chǔ)參數(shù)協(xié)商一些重要的TCP參數(shù)如最大報(bào)文段長(zhǎng)度。為后續(xù)可靠數(shù)據(jù)傳輸分配資源操作系統(tǒng)需要為這個(gè)連接分配內(nèi)存、創(chuàng)建套接字?jǐn)?shù)據(jù)結(jié)構(gòu)等。如果只用兩次握手客戶端發(fā)送SYN服務(wù)器回復(fù)SYN-ACK只能證明從客戶端到服務(wù)器的單向路徑是通的。服務(wù)器無(wú)法確認(rèn)自己發(fā)出的SYN-ACK報(bào)文是否被客戶端成功接收。如果這個(gè)SYN-ACK在半路丟失服務(wù)器會(huì)認(rèn)為連接已建立并等待數(shù)據(jù)而客戶端因未收到確認(rèn)會(huì)認(rèn)為連接失敗。這就導(dǎo)致了服務(wù)器資源的空等和浪費(fèi)在遭受SYN洪泛攻擊時(shí)尤其危險(xiǎn)。因此第三次握手客戶端對(duì)服務(wù)器的SYN進(jìn)行ACK確認(rèn)是必不可少的它向服務(wù)器明確宣告“我收到了你的同步請(qǐng)求并且我準(zhǔn)備好了我們現(xiàn)在可以開(kāi)始雙向可靠通信了。” 這確保了連接的雙向可靠性。2. 三次握手報(bào)文交互全流程詳解讓我們拋開(kāi)比喻直接深入到TCP報(bào)文的比特位層面看看三次握手具體交換了哪些信息。一個(gè)TCP報(bào)文段頭部包含多個(gè)關(guān)鍵字段在握手中最重要的是SYN: 同步序列號(hào)標(biāo)志位。置1表示這是一個(gè)連接請(qǐng)求或連接接受報(bào)文。ACK: 確認(rèn)標(biāo)志位。置1表示確認(rèn)號(hào)字段有效。Sequence Number: 序列號(hào)。本報(bào)文段所發(fā)送數(shù)據(jù)的第一個(gè)字節(jié)的編號(hào)。Acknowledgment Number: 確認(rèn)號(hào)。期望收到對(duì)方下一個(gè)報(bào)文段的第一個(gè)數(shù)據(jù)字節(jié)的編號(hào)。三次握手完整過(guò)程第一次握手 (SYN):客戶端主動(dòng)打開(kāi)方發(fā)送一個(gè)TCP報(bào)文段。將標(biāo)志位SYN置為1。隨機(jī)生成一個(gè)初始序列號(hào)seq J假設(shè)為J。發(fā)送給服務(wù)器。此時(shí)客戶端進(jìn)入SYN_SENT狀態(tài)。這個(gè)報(bào)文不攜帶任何應(yīng)用層數(shù)據(jù)它只傳達(dá)一個(gè)信息“我想和你建立連接我的初始序列號(hào)是J?!钡诙挝帐?(SYN ACK):服務(wù)器被動(dòng)打開(kāi)方收到客戶端的SYN報(bào)文后如果同意連接則回復(fù)一個(gè)報(bào)文段。將標(biāo)志位SYN和ACK都置為1。隨機(jī)生成自己的初始序列號(hào)seq K。將確認(rèn)號(hào)ack設(shè)置為J 1即客戶端的序列號(hào)J加1表示“我收到了你的序列號(hào)為J的SYN我期望你下一個(gè)數(shù)據(jù)字節(jié)的序號(hào)是J1”。此時(shí)服務(wù)器進(jìn)入SYN_RCVD狀態(tài)。這個(gè)報(bào)文同時(shí)完成了兩件事1) 確認(rèn)客戶端的SYN2) 發(fā)起服務(wù)器到客戶端的連接同步。第三次握手 (ACK):客戶端收到服務(wù)器的SYN-ACK報(bào)文后。將標(biāo)志位ACK置為1。序列號(hào)seq J 1因?yàn)榈谝淮挝帐值腟YN消耗了一個(gè)序號(hào)。將確認(rèn)號(hào)ack設(shè)置為K 1即服務(wù)器的序列號(hào)K加1表示“我收到了你的序列號(hào)為K的SYN我期望你下一個(gè)數(shù)據(jù)字節(jié)的序號(hào)是K1”。該報(bào)文可以攜帶應(yīng)用層數(shù)據(jù)如HTTP請(qǐng)求。發(fā)送此報(bào)文后客戶端進(jìn)入ESTABLISHED狀態(tài)。服務(wù)器收到此ACK后也進(jìn)入ESTABLISHED狀態(tài)。至此雙向連接可靠建立雙方可以開(kāi)始全雙工的數(shù)據(jù)傳輸。為什么不是四次握手從信息論角度看服務(wù)器的SYN和ACK合并到了一個(gè)報(bào)文里發(fā)送這已經(jīng)是最精簡(jiǎn)的必需信息交換。如果再拆成四次客戶端SYN - 服務(wù)器ACK - 服務(wù)器SYN - 客戶端ACK只會(huì)增加不必要的網(wǎng)絡(luò)延遲而沒(méi)有帶來(lái)任何額外的可靠性或功能 benefits。TCP協(xié)議的設(shè)計(jì)哲學(xué)之一就是高效。3. 實(shí)戰(zhàn)使用Wireshark抓包分析三次握手理論需要實(shí)踐驗(yàn)證。我們通過(guò)Wireshark這個(gè)強(qiáng)大的網(wǎng)絡(luò)封包分析軟件親眼目睹三次握手的過(guò)程。環(huán)境準(zhǔn)備操作系統(tǒng): Windows 10/11, macOS 或 Linux。工具: Wireshark 請(qǐng)從官網(wǎng)下載。目標(biāo): 捕獲訪問(wèn)www.baidu.com的TCP連接過(guò)程。操作步驟啟動(dòng)Wireshark并選擇網(wǎng)卡打開(kāi)Wireshark在主界面選擇你正在使用的網(wǎng)絡(luò)接口如“Wi-Fi”或“以太網(wǎng)”。設(shè)置捕獲過(guò)濾器可選但推薦在捕獲過(guò)濾器中輸入tcp port 80因?yàn)槲覀冊(cè)L問(wèn)的百度HTTP服務(wù)默認(rèn)使用80端口。這可以過(guò)濾掉大量不相關(guān)的網(wǎng)絡(luò)流量。開(kāi)始捕獲點(diǎn)擊左上角的“鯊魚鰭”按鈕開(kāi)始抓包。觸發(fā)連接迅速打開(kāi)你的瀏覽器訪問(wèn)http://www.baidu.com注意是HTTP不是HTTPS。HTTPS的握手是TLS層的會(huì)干擾觀察。停止捕獲網(wǎng)頁(yè)加載完成后回到Wireshark點(diǎn)擊紅色停止按鈕。分析握手報(bào)文在Packet List面板中尋找與你本機(jī)IP和www.baidu.com的IP之間協(xié)議為TCP的報(bào)文。通常最前面的幾個(gè)報(bào)文就是三次握手。你會(huì)看到類似下面的序列No. Time Source Destination Protocol Length Info 1 0.000000 192.168.1.100 110.242.68.3 TCP 74 59234 → 80 [SYN] Seq0 Win64240 Len0 MSS1460 WS256 SACK_PERM1 2 0.027834 110.242.68.3 192.168.1.100 TCP 74 80 → 59234 [SYN, ACK] Seq0 Ack1 Win8192 Len0 MSS1440 WS256 SACK_PERM1 3 0.027927 192.168.1.100 110.242.68.3 TCP 66 59234 → 80 [ACK] Seq1 Ack1 Win262656 Len0報(bào)文解讀Packet 1: 客戶端(192.168.1.100)的端口59234向服務(wù)器(110.242.68.3)的80端口發(fā)送[SYN]。Seq0是相對(duì)序列號(hào)Wireshark為了便于閱讀而顯示為0實(shí)際在原始報(bào)文中是一個(gè)大隨機(jī)數(shù)比如J。Packet 2: 服務(wù)器回復(fù)[SYN, ACK]。Seq0實(shí)際為KAck1即J1確認(rèn)了客戶端的SYN。Packet 3: 客戶端發(fā)送[ACK]。Seq1即J1Ack1即K1確認(rèn)了服務(wù)器的SYN。至此握手完成。點(diǎn)擊每個(gè)報(bào)文在下方Packet Details面板中可以展開(kāi)TCP層查看所有標(biāo)志位和原始序列號(hào)/確認(rèn)號(hào)的真實(shí)值。這個(gè)實(shí)踐能讓你對(duì)抽象的理論產(chǎn)生最直觀的認(rèn)識(shí)。4. 深入探究?jī)纱挝帐峙c四次握手的假設(shè)場(chǎng)景為了強(qiáng)化理解我們分別設(shè)想兩次握手和四次握手可能帶來(lái)的問(wèn)題。假設(shè)只有兩次握手客戶端發(fā)送SYN序列號(hào)J。服務(wù)器回復(fù)SYN-ACK序列號(hào)K確認(rèn)號(hào)J1。結(jié)束。會(huì)產(chǎn)生什么問(wèn)題歷史連接造成的資源浪費(fèi)與混淆這是最核心的問(wèn)題。假設(shè)客戶端發(fā)出的第一個(gè)SYN序列號(hào)J因?yàn)榫W(wǎng)絡(luò)擁堵延遲了??蛻舳顺瑫r(shí)后重發(fā)了一個(gè)新的SYN序列號(hào)J’。如果兩次握手就建立連接那么當(dāng)延遲的舊SYNJ最終到達(dá)服務(wù)器時(shí)服務(wù)器會(huì)認(rèn)為這是一個(gè)新的連接請(qǐng)求并回復(fù)SYN-ACK進(jìn)入ESTABLISHED狀態(tài)分配資源。然而這個(gè)連接對(duì)客戶端來(lái)說(shuō)是無(wú)效的它已經(jīng)用了新的序列號(hào)J’客戶端不會(huì)發(fā)送數(shù)據(jù)。這就導(dǎo)致服務(wù)器端維護(hù)了一個(gè)“幽靈”連接白白消耗資源半連接隊(duì)列資源。三次握手中的第三次ACK攜帶了確認(rèn)號(hào)服務(wù)器可以通過(guò)確認(rèn)號(hào)來(lái)判斷這個(gè)ACK是對(duì)應(yīng)哪個(gè)SYN的從而拒絕舊的、重復(fù)的SYN報(bào)文避免舊連接干擾。無(wú)法可靠地同步雙方初始序列號(hào)服務(wù)器無(wú)法確認(rèn)客戶端是否收到了自己的SYN序列號(hào)K。如果服務(wù)器的SYN-ACK丟失服務(wù)器認(rèn)為連接已建立而客戶端認(rèn)為未建立。當(dāng)客戶端開(kāi)始發(fā)送數(shù)據(jù)時(shí)其首個(gè)數(shù)據(jù)包會(huì)攜帶ACK標(biāo)志確認(rèn)服務(wù)器的SYN服務(wù)器收到后會(huì)一臉茫然因?yàn)樗诖氖切蛄刑?hào)為K1的數(shù)據(jù)但客戶端發(fā)來(lái)的序列號(hào)是基于J的。這會(huì)導(dǎo)致連接狀態(tài)不一致。假設(shè)需要四次握手客戶端發(fā)送SYNJ。服務(wù)器確認(rèn)客戶端的SYNACK for J。服務(wù)器發(fā)送自己的SYNK。客戶端確認(rèn)服務(wù)器的SYNACK for K。這看起來(lái)更“對(duì)稱”但第2步和第3步完全可以合并成一個(gè)報(bào)文SYN-ACK發(fā)送沒(méi)有任何功能損失。拆成兩步只會(huì)增加一個(gè)額外的網(wǎng)絡(luò)往返延遲RTT降低連接建立的效率。在網(wǎng)絡(luò)協(xié)議設(shè)計(jì)中在保證正確性的前提下盡量減少報(bào)文數(shù)量是重要的優(yōu)化原則。5. 從三次握手看TCP協(xié)議的設(shè)計(jì)哲學(xué)三次握手的設(shè)計(jì)完美體現(xiàn)了TCP協(xié)議的幾個(gè)核心設(shè)計(jì)思想可靠性優(yōu)先在不可靠的IP網(wǎng)絡(luò)上構(gòu)建可靠的字節(jié)流服務(wù)一切設(shè)計(jì)都以確保數(shù)據(jù)正確、有序、不重不漏為最高準(zhǔn)則。初始序列號(hào)的同步是可靠傳輸?shù)钠瘘c(diǎn)必須萬(wàn)無(wú)一失。狀態(tài)機(jī)驅(qū)動(dòng)TCP連接的生命周期被明確定義為一系列狀態(tài)CLOSED,LISTEN,SYN_SENT,SYN_RCVD,ESTABLISHED等。三次握手是驅(qū)動(dòng)狀態(tài)從CLOSED變遷到ESTABLISHED的唯一正規(guī)途徑。這種嚴(yán)謹(jǐn)?shù)臓顟B(tài)機(jī)模型使得協(xié)議行為可預(yù)測(cè)、可分析。資源保護(hù)通過(guò)第三次握手服務(wù)器在收到最終確認(rèn)前不會(huì)完全分配連接所需的全部資源在Linux中SYN_RCVD狀態(tài)的連接存放在“半連接隊(duì)列”而ESTABLISHED狀態(tài)的連接在“全連接隊(duì)列”。這是防御SYN Flood攻擊攻擊者只發(fā)送第一次SYN耗盡服務(wù)器半連接隊(duì)列的一道基礎(chǔ)防線。效率與折衷在可靠性和效率之間取得平衡。三次是建立雙向可靠連接所需的最小次數(shù)。既避免了兩次的不可靠又杜絕了四次的低效。6. 常見(jiàn)問(wèn)題與面試深度問(wèn)答Q1: 初始序列號(hào)為什么是隨機(jī)的A: 主要是為了安全。如果序列號(hào)從一個(gè)固定值開(kāi)始比如每次都是0攻擊者很容易偽造一個(gè)TCP報(bào)文來(lái)劫持會(huì)話。隨機(jī)化的初始序列號(hào)大大增加了猜測(cè)難度。在早期TCP實(shí)現(xiàn)中序列號(hào)基于時(shí)鐘遞增也被證明存在安全隱患現(xiàn)代操作系統(tǒng)都使用更安全的隨機(jī)數(shù)生成算法。Q2: 第三次握手可以攜帶數(shù)據(jù)嗎A:可以。一旦客戶端發(fā)出第三次握手的ACK它就已經(jīng)進(jìn)入了ESTABLISHED狀態(tài)認(rèn)為連接已經(jīng)建立因此可以立即發(fā)送應(yīng)用層數(shù)據(jù)。這些數(shù)據(jù)會(huì)包含在第三個(gè)報(bào)文中一起發(fā)送提高了效率。例如HTTP的GET請(qǐng)求經(jīng)常在第三次握手中發(fā)出。但服務(wù)器端在收到第三次ACK之前必須保持SYN_RCVD狀態(tài)不能處理數(shù)據(jù)。所以服務(wù)器發(fā)送的數(shù)據(jù)必須等到自己進(jìn)入ESTABLISHED狀態(tài)后即收到第三次ACK后才能發(fā)出。Q3: 什么是半連接隊(duì)列和全連接隊(duì)列A: 這是服務(wù)器內(nèi)核維護(hù)的兩個(gè)重要隊(duì)列半連接隊(duì)列SYN Queue服務(wù)器收到客戶端SYN回復(fù)SYN-ACK后連接進(jìn)入SYN_RCVD狀態(tài)被放入此隊(duì)列。全連接隊(duì)列Accept Queue服務(wù)器收到客戶端的第三次ACK后連接進(jìn)入ESTABLISHED狀態(tài)從半連接隊(duì)列移出放入此隊(duì)列等待應(yīng)用程序調(diào)用accept()系統(tǒng)調(diào)用來(lái)取走。 如果半連接隊(duì)列滿了服務(wù)器可能無(wú)法處理新的連接請(qǐng)求如果全連接隊(duì)列滿了即使完成了三次握手新的連接也無(wú)法被應(yīng)用層處理可能導(dǎo)致客戶端連接超時(shí)。Q4: 如何查看和調(diào)整這些隊(duì)列大小A: 在Linux系統(tǒng)中可以通過(guò)系統(tǒng)參數(shù)進(jìn)行調(diào)整半連接隊(duì)列大小受net.ipv4.tcp_max_syn_backlog和somaxconn以及應(yīng)用程序listen()函數(shù)的backlog參數(shù)共同影響關(guān)系復(fù)雜。全連接隊(duì)列大小為min(backlog, somaxconn)其中backlog是listen()調(diào)用時(shí)傳入的值somaxconn是系統(tǒng)參數(shù)net.core.somaxconn。 查看命令# 查看當(dāng)前連接狀態(tài)統(tǒng)計(jì) (包含SYN-RECV, ESTAB等) ss -ant # 查看系統(tǒng)參數(shù) sysctl net.ipv4.tcp_max_syn_backlog sysctl net.core.somaxconnQ5: 除了三次握手TCP連接建立還有別的方式嗎A: 有。TCP還支持同時(shí)打開(kāi)即雙方同時(shí)主動(dòng)發(fā)送SYN給對(duì)方。這種情況很少見(jiàn)但協(xié)議支持。它會(huì)需要四次報(bào)文交換比標(biāo)準(zhǔn)的三次握手多一次。此外通過(guò)TCP Fast Open技術(shù)可以在某些條件下將握手與首次數(shù)據(jù)交換合并減少延遲。7. 最佳實(shí)踐與工程啟示理解三次握手不僅是為了應(yīng)付面試更能指導(dǎo)我們進(jìn)行高性能、高可用的網(wǎng)絡(luò)編程和運(yùn)維。服務(wù)端優(yōu)化調(diào)整隊(duì)列長(zhǎng)度根據(jù)服務(wù)器負(fù)載和內(nèi)存情況適當(dāng)調(diào)整somaxconn和tcp_max_syn_backlog以應(yīng)對(duì)高并發(fā)連接場(chǎng)景防止隊(duì)列溢出導(dǎo)致連接失敗。啟用SYN Cookies在遭受SYN Flood攻擊時(shí)可以設(shè)置net.ipv4.tcp_syncookies 1。該機(jī)制在不使用半連接隊(duì)列的情況下驗(yàn)證連接能有效抵御此類攻擊。縮短超時(shí)時(shí)間對(duì)于SYN_RCVD狀態(tài)的連接可以通過(guò)net.ipv4.tcp_synack_retries減少重試次數(shù)讓無(wú)效連接更快釋放??蛻舳藘?yōu)化連接復(fù)用對(duì)于HTTP等短連接服務(wù)使用連接池如數(shù)據(jù)庫(kù)連接池、HTTP客戶端連接池可以避免頻繁進(jìn)行三次握手和四次揮手極大提升性能。超時(shí)與重試合理設(shè)置連接建立超時(shí)時(shí)間。過(guò)短可能導(dǎo)致在弱網(wǎng)絡(luò)環(huán)境下頻繁失敗過(guò)長(zhǎng)則影響用戶體驗(yàn)。網(wǎng)絡(luò)問(wèn)題診斷當(dāng)遇到“Connection timeout”或“Connection refused”時(shí)結(jié)合telnet、netstat和抓包工具分析問(wèn)題發(fā)生在握手的哪個(gè)階段。SYN_SENT狀態(tài)過(guò)多可能客戶端無(wú)法收到服務(wù)器的SYN-ACK防火墻攔截、服務(wù)器未監(jiān)聽(tīng)端口、路由問(wèn)題。SYN_RCVD狀態(tài)過(guò)多可能服務(wù)器的ACK未能到達(dá)客戶端網(wǎng)絡(luò)不對(duì)稱、客戶端防火墻丟棄或者服務(wù)器遭受SYN攻擊。編程注意事項(xiàng)在編寫Socket程序時(shí)正確處理connect(),listen(),accept()等調(diào)用與TCP狀態(tài)機(jī)的關(guān)系。理解“握手”是內(nèi)核協(xié)議棧完成的應(yīng)用程序在accept()返回時(shí)連接早已在第三次握手完成后就建立了?;氐阶畛跄莻€(gè)有趣的問(wèn)題“TCP握手為什么是三次而正常人握手是兩只手” 現(xiàn)在我們可以清晰地回答人類握手是禮儀一次接觸足矣TCP握手是工程需要在充滿不確定性的網(wǎng)絡(luò)世界中用最少的必要步驟三次為雙向的、可靠的字節(jié)流通信奠定一個(gè)毫無(wú)歧義的堅(jiān)實(shí)基礎(chǔ)。這多出來(lái)的一次“確認(rèn)”正是TCP協(xié)議嚴(yán)謹(jǐn)性與可靠性的精髓所在。