TCP 协议(包含三次握手,四次挥手)
TCP 特性
- 1.確認應答機制 (ACK)
- 2.超時重傳
- 3.1建立連接 - 三次握手 ▲
- 3.2.斷開連接 - 四次揮手 ▲
1.確認應答機制 (ACK)
確認應答是可靠傳輸的最核心機制
接收方反饋一個應答報文(ACK),表示已收到
假設現在 A 想去 B 家里玩游戲,于是 A 給 B 發消息,若消息沒有出現錯誤且順序正確
結果如下所示:
但網絡傳輸比較復雜,可能存在一種情況"后發先至"
由于數據的長度不同或者傳輸網絡不同,先發送的數據不一定先到達,接收方接收到的數據可能是亂序的,如圖:
當 B 回復 A 的消息時,若存在對應關系,那么即使出現了"后發先至"的情況,也能順利的確立應答
上述方法,雖然可以順利的確立應答,但額外的信息很多,占用的帶寬很多
下面如圖,針對發送的請求進行編號,應答的時候也針對編號進行應答,這樣既能保證數據傳輸沒有歧義,也不會浪費太多的空間和帶寬
序號和確定序號,在前面 TCP報文格式中提到過
上述情況不嚴謹,真實的 TCP 還不一樣,TCP 是面向字節流的,此處的編號并不是按照一條兩條來編的,而是按照字節來編號的 (每個字節有一個編號)
確認應答是一種特殊的報文(ACK),所謂的應答報文,本質上就是 ACK 字段為1 的報文,此時報頭中的"確認序號"字段才是生效的
初始序號是隨機的,為了防止網絡攻擊;如果發送多個數據,每個數據都會帶著一個序號
接收方收到數據后,是知道數據所帶著的序號的,根據序號給出確認序號(告訴發送方下次給我發的序號),發送給發送方,發送方就知道接收方收到了哪些數據
2.超時重傳
確認應答是比較理想的情況,但數據在傳輸過程中,可能是會丟包的
仍以上面例子為例,A 給 B 發消息,你在家嘛?等了很久,A 也沒收到 B 的消息,此時,存在以下幾種情況:
① B 不想回 A 的消息
② B 沒收到 A 的消息 (丟包情況1: 發的請求丟失)
③ B 回復了消息,但 A 沒收到 (丟包情況2: 應答的 ACK 丟失)
②③情況:丟包的兩種情況,對于發送方來說無法確定是哪種情況,因此,進行統一處理:當發送了一條數據之后,TCP 內部就會自動啟動一個定時器,達到一定時間也沒收到 ACK,定時器就會自動觸發重傳消息的動作 —— 超時重傳
①情況:
思考:
假設第二次重發沒有成功,那么就存在兩個超時時間 t1,t2 如圖所示:
那么,t1 和 t2 時間一樣長嗎??
在 TCP 中,t2 會比 t1 更長
TCP 抱著一種 “悲觀的態度”,當一次丟包重傳之后,TCP 就覺得大概率后面的重傳也沒用,所以就隔一個更長的時間,節省帶寬
上述丟包有兩種情況,一種是請求丟失 —— 重傳沒有問題;一種是 ACK 丟失,重傳就意味著接收方收到了相同的數據
TCP 會在內部進行數據去重 (以序號為 key 進行去重),保證應用層讀到的數據不是重復數據
確認應答 和 超時重傳是 TCP 可靠性中最核心的機制
3.1建立連接 - 三次握手 ▲
為什么要就建立連接?
1.更好的保證可靠性: 建立連接的過程其實就是讓通信雙方驗證各自的發送能力和接受能力是否正常
2.協商一些重要參數 (如: 序號的初始值)
具體怎樣建立連接?
舉例:A 給 B 打電話,打電話同樣要驗證自己以及對方的話筒和聽筒是否正常工作
第一次握手: 剛開始,A 不知道自己和 B 手機的聽筒和話筒是否正常,所以 A說"喂,你能聽到嗎?"
第二次握手: B 聽到后,說明 A 的話筒和 B 的聽筒正常,但 B 還需進一步檢查自己的話筒和 A 的聽筒是否正常;同時 B 把 A 話筒正常和自己聽筒正常的消息傳遞給 A;于是 B “我能聽到,你呢?”
第三次握手: A 收到 B 的消息后,就證明了 A 聽筒正常,B 話筒正常
以上三次握手就保證了 A、B 的聽筒和話筒都正常,也就保證了通話的正常,這就類似于網絡建立連接時的三次握手
TCP 中真實的建立連接過程: (假設主機 A 主動發起連接)
-
第一次握手: 客戶端向服務器發送 SYN 報文 (SEQ=x,SYN=1),并進入 SYN_SENT 狀態,等待服務器確認
-
第二次握手: 實際上是分兩部分來完成的,即 SYN+ACK (請求和確認) 報文
服務器收到了客戶端的請求,向客戶端回復一個確認信息 (ack=x+1)
服務器再向客戶端發送一個 SYN 包 (SEQ=y)建立連接的請求,此時服務器進入 SYN_RECV 狀態 -
第三次握手: 客戶端收到服務器的回復 (SYN+ACK 報文0);此時,客戶端也要向服務器發送確認包 (ACK);此包發送完畢客戶端和服務器進入 ESTABLISHED 狀態,完成 3 次握手
建立連接的過程,相當于通信雙方各自給對方發送 SYN,在各自給對方發送給 ACK,只不過中間的 ACK 和 SYN 合二為一了,于是最后就是"三次握手"
幾個重要的狀態:
- LISTEN: 正在偵聽來自遠方的 TCP 端口的連接請求,服務端啟動后處于 LISTEN 狀態用于監聽不同客戶端的 TCP 請求并建立連接
- SYN_SEND / SYN_RCVD: 建立連接的中間過程,若連接順利的話(建立連接過程也可能丟包),這兩個狀態就一瞬消失
- ESTABLISHEN: 連接建立完畢 (驗證了通信雙方的發送和接受能力都正常),可以進行數據傳輸
1.兩次握手可以嗎??
不可以
- 防止已失效的請求報文又傳送到了服務端,建立了多余的鏈接,浪費資源
- 兩次握手只能保證單向連接是通暢的 (為了實現可靠數據傳輸, TCP 協議的通信雙方,都必須維護一個序列號,以標識發送出去的數據包中,哪些是已經被對方收到的;三次握手的過程即是通信雙方相互告知序列號起始值,并確認對方已經收到了序列號起始值的必經步驟;如果只是兩次握手,至多只有連接發起方的起始序列號能被確認,另一方選擇的序列號則得不到確認)
.
2.為什么是三次??
主要是為了建立可靠的通信通道,保證客戶端與服務端同時具備發送、接收數據的能力
.
3.四次握手可以嗎??
可以,但沒必要
四次握手可以驗證雙方的發送接收能力正常,但是這樣做效率比較低
.
3.2.斷開連接 - 四次揮手 ▲
三次握手: 雙方各自向對方發起建立連接的請求,再各自給對方回應,只不過,中間的 SYN 和 ACK 能合并在一起
四次揮手: 雙方各自向對方發起建立連接的請求,再各自給對方回應,只不過,中間的 FIN 和 ACK 不一定能合并在一起
仍以打電話為例,如下圖:
TCP 中真實的斷開連接過程: (假設主機 A 主動斷開連接)
- 第一次揮手: 客戶端向服務器端發送斷開 TCP 連接請求的 [FIN,ACK] 報文,在報文中隨機生成一個序列號 SEQ=u,表示要斷開 TCP 連接
此時,客戶端進入FIN_WAIT_1 (終止等待1) 狀態 - 第二次揮手: 當服務器端收到客戶端發來的斷開 TCP 連接的請求后,回復發送 ACK 報文,表示已經收到斷開請求。回復時,隨機生成一個序列號 SEQ=v;由于回復的是客戶端發來的請求,所以在客戶端請求序列號 SEQ=u 的基礎上加 1,得到 ack=u+1
此時,服務端就進入了CLOSE_WAIT (關閉等待) 狀態,客戶端收到ACK后,就進入FIN_WAIT_2 (終止等待2) 狀態 - 第三次揮手: 服務器端在回復完客戶端的 TCP 斷開請求后,不會馬上進行 TCP 連接的斷開。服務器端會先確認斷開前,所有傳輸到客戶端的數據是否已經傳輸完畢。確認數據傳輸完畢后才進行斷開,向客戶端發送 [FIN,ACK] 報文,設置字段值為 1。再次隨機生成一個序列號 SEQ=w;由于還是對客戶端發來的 TCP 斷開請求序列號 SEQ=u 進行回復,因此 ack 依然為 u+1
此時,服務器就進入了LAST_ACK (最后確認) 狀態 - 第四次揮手: 客戶端收到服務器發來的 TCP 斷開連接數據包后將進行回復,表示收到斷開 TCP 連接數據包。向服務器發送 ACK 報文,生成一個序列號 SEQ=u+1;由于回復的是服務器,所以 ACK 字段的值在服務器發來斷開 TCP 連接請求序列號 SEQ=w 的基礎上加 1,得到 ack=w+1
此時,客戶端就進入了TIME_WAIT (時間等待) 狀態;注意此時TCP連接還沒有釋放,必須經過2MSL (最長報文段壽命) 的時間后,當客戶端撤銷相應的TCB后,才進入CLOSED狀態
兩個重要的狀態:
- CLOSE_WAIT: 表示在等待關閉; 四次揮手揮了一半了,當前可能剩下的兩次不揮了(接收方沒調用 close 方法,就會導致四次揮手只揮兩次,從而沒有正確關閉連接)
- TIME_WAIT: 誰主動斷開連接,誰進入 TIME-WAIT 狀態,此時該主機已經完成了四次揮手的過程,但仍不能立刻斷開連接,而是要以 TIME-WAIT 狀態來保持連接一段時間之后,再徹底釋放連接 (處理最后一個 ACK 丟包之后重傳的問題)
為了解決網絡的丟包和網絡不穩定所帶來的其他問題,確保連接方能在時間范圍內,關閉自己的連接
1.四次揮手,三次揮完行不行??
通常情況下不行,若觸發了延時應答機制,就可以三次揮完
"不行",即:上述的 ② ③ 為什么沒有合并在一起??
因為中間兩次操作的時機不一樣
ACK 是收到 FIN 之后立刻由內核返回的數據報,FIN 是應用程序處理完接受緩沖區的數據之后,調用的 close 方法觸發的
.
2.為什么四次??
因為要確保客戶端和服務端的數據能夠完成傳輸
.
3.為什么 TIME_WAIT 狀態要等待 2MSL??
假設網絡上傳輸數據的最大時間為 MSL
MSL 就是 ACK / FIN 從主機 A 到主機 B 的最大時間
TIME-WAIT 等待時間,需要分成兩個部分:
①等待 ACK 經歷一個最大時間到達主機 B
②萬一 ACK 丟了,在等待一個最大時間,主機 B 重傳 FIN 到達主機 A
因此,TIME_WAIT 就需要等待 2倍的MSL,即:2MSL
原因:
- 確保 ACK 報文能夠到達服務端,從而使服務端正常關閉連接
第四次揮手時,客戶端第四次揮手的 ACK 報文不一定會到達服務端;服務端會超時重傳 FIN / ACK 報文,此時如果客戶端已經斷開了連接,那么就無法響應服務端的二次請求,這樣服務端遲遲收不到 FIN / ACK 報文的確認,就無法正常斷開連接
MSL 是報文段在網絡上存活的最長時間,客戶端等待 2MSL 時間,即「客戶端 ACK 報文 1MSL 超時 + 服務端 FIN 報文 1MSL 傳輸」,就能夠收到服務端重傳的 FIN / ACK 報文,然后客戶端重傳一次 ACK 報文,并重新啟動 2MSL 計時器;如此保證服務端能夠正常關閉
如果服務端重發的 FIN 沒有成功地在 2MSL 時間里傳給客戶端,服務端則會繼續超時重試直到斷開連接 - 防止已失效的連接請求報文段出現在之后的連接中
TCP 要求在 2MSL 內不使用相同的序列號;客戶端在發送完最后一個 ACK 報文段后,再經過時間 2MSL,就可以保證本連接持續的時間內產生的所有報文段都從網絡中消失;這樣就可以使下一個連接中不會出現這種舊的連接請求報文段;或者即使收到這些過時的報文,也可以不處理它
總結
以上是生活随笔為你收集整理的TCP 协议(包含三次握手,四次挥手)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C程序设计语言(KR)笔记
- 下一篇: 用友NC移动审批