三次握手
三次握手形象版,快速理解
deepseek 的象形比喻:三次握手建立連接就像打電話一樣:
(1) A 打給 B,“喂, 你能聽到我說話嗎?”
(2) B 回答 A,“嗯,可以聽到,你能聽到我說話嗎?”
(3) A 回答 B,“沒問題,可以聽到,我們開始聊天吧!”
客戶端請求服務器的過程,可以將 A 當作客戶端,B 當作服務器,它們進行“握手”的目的是證明自己的接收能力和發送能力都是正常的。
第一次握手,客戶端向服務器發送消息,服務器收到消息后,服務器知道了:客戶端的發送能力和服務器的接收能力沒問題
第二次握手,服務器向客戶端發送消息,客戶端收到消息后,客戶端知道了:客戶端的發送能力、服務器的接收能力、服務器的發送能力、客戶端的接收能力都是沒問題的。但此時服務器不知道自己的發送能力和客戶端的接收能力是否正常
第三次握手,客戶端向服務器發送消息,服務器收到消息后,服務器就知道:自己的發送能力和客戶端的接收能力也沒問題(在第一次握手的時候,服務器已經知道了自己的接收能力和客戶端的發送能力沒問題)
三次握手結束,客戶端和服務器建立連接成功!
三次握手詳細過程
在 Tcp 報文頭部,有下面幾個比較關鍵的字段:
SYN:同步序列號,是一個控制位,當它為 1 時,表示這是一個用于建立連接
的報文
ACK:也是一個控制位,當它位 1 時,表示報文中的ack
字段是有效的,除了第一個 SYN 包,后續所有 ACK 位都必須是 1
seq:序列號,標識本報文所發送數據的第一個字節的編號(TCP 是可靠傳輸,每個字節都有編號,確保數據有效不丟失)
ack:確認號,期望收到對方下一個報文段的第一個數據字節的序列號,同時表示ack-1
之前的所有數據都已正確收到
第一步,客戶端發送 SYN: 設置 SYN 標識位為1,并隨機生成一個初始序列號 x,放在 seq 字段里(服務器你好,我想和你建立連接,我數據的初始序列號為 x)
第二步,服務器回復 SYN-ACK: 服務器收到報文后,同意建立連接,于是回復一個報文,SYN-ACK 包,這個包將 TCP 標志位的 SYN 和 ACK 都設置為 1,同時,服務器也隨機生成自己的初始序列號 y,放在 seq 字段里,同時需要確認客戶端的 SYN 包。確認方法:將客戶端發來的序列號 x 加 1 作為 ack 的值發回去(客戶端你好,我同意建立連接,我數據的初始序列號是 y,并且我已經收到你的序列號 x,接下來期望從你的第 x+1 個字節的數據開始接收)
第三步,客戶端發送 ACK: 客戶端收到服務器的 SYN-ACK 包后,連接基本已經建立,客戶端需要發送最后一個確認包,將 ACK 標志位設置為1
,seq 字段此時變為了 x+1,因為第一步的 SYN 包會消耗一個字節,同時,客戶端需要確認服務器的消息,因此,將 ack 字段設置為 y+1 發送回去(服務器,我已收到你的確認,接下來建立連接吧!接下來,客戶端發送的數據將從 x+1 開始,并且期望服務器從第 y+1 個字節的序列開始接收)
注意:x 和 y 都是隨機生成的,因此 x+1 和 y+1 只是一個象征性的編號,意思是將客戶端發送數據的起始位置命名為編號 x+1,服務器要發送的數據起始位置命名為編號 y+1。
三次握手之后,客戶端和服務端都會進入 ESTABLISHED 狀態
四次揮手
四次揮手象形理解
A 和 B 打電話,要禮貌的結束通話
(1) A 對 B 說,“我沒什么要說的了,我要掛了”(客戶端發送消息通知服務器,我的消息發送完了,準備斷開連接了)
(2) B 回答 A,“ok 我知道你說完了,但是稍等,我還有話要說”(服務器收到客戶端要結束連接的請求,但服務器的消息還沒有發送完,此時客戶端——>服務器的通道關閉,客戶端不能再給服務器發消息,但是服務器依然可以給客戶端發)
(3) B 對 A 說,“好了,我說完了,我要掛了!”(服務器消息發送完畢)
(4) A 對 B 說,“好的,我們掛了吧。”(雙方通道全部關閉)
四次揮手詳細過程
第一步,客戶端發送 FIN: 將 FIN 標志位設置位 1,這是連接結束的信號,報文中的 seq 字段為 u,u 表示客戶端已發送的數據最后一個字節的序列號加 1,客戶端進入 FIN-WAIT-1
狀態(客戶端對服務器說,我沒數據給你了,我要關閉我到你那邊的發送通道了)
第二步,服務器回復 ACK: 服務器收到 FIN 包后,發送一個確認包,ACK 的標志位設為 1,將客戶端發來的序列號加1,即將 ack 的值設置為 u+1,并發送回去,此時,客戶端進入 CLOSE-WAIT
狀態,因為客戶端->服務器關閉,但服務器->客戶端未關閉,此時,服務器進入半關閉Half-Colse
狀態 (服務器確認客戶端即將關閉連接通道,進入半關閉狀態)
第三步,服務器發送 FIN: 服務器向客戶端發送完消息了,也準備關閉連接。將 FIN 和 ACK 字段都設置為 1,seq 字段設置為 w,w 是服務器之前已經最后一個字節的序列號 + 1,ack 字段依然是 u+1,服務器進入LAST-ACK
狀態(服務器發送完消息,通知客戶端,即將關閉發送通道)
第四步,客戶端回復 ACK: 客戶端收到服務器 FIN 包后,知道服務器發送完了,于是向服務器發送最后一個確認包,ACK 標志位設為1,將 ack 設置為服務器發來的序列號 w 加1,即 ack = w+1,客戶端進入 TIME-WAIT
狀態,時間為2MSL(兩倍的最大報文段生存時間)當 TIME-WAIT 狀態結束后,變為 CLOSED
,服務器收到這個 ACK 包后,釋放資源,狀態變為 CLOSED
。如果服務器沒有收到 ACK,它就會再次發送 FIN,此時處于 TIME-WAIT 狀態的客戶端還能再次發送 ACK,這就是 TIME-WAIT 狀態存在的意義!
為什么不是三次揮手?
因為TCP是全雙工的,允許在兩個方向上獨立傳輸,所以 TCP 可以半關閉,需要耗費一個來回