圖解 TCP 三次握手與四次分手

引言

TCP三次握手和四次揮手不論是在開發仍是面試中都是一個很是重要的知識點,它是咱們優化web程序性能的基礎。可是大部分教材都對這部分解釋的比較抽象,本文咱們就利用wireshark來抓包以真正體會整個流程的細節。html

三次握手

根據下面這幅圖咱們來看一下TCP三次握手。p.s: 每一個箭頭表明一次握手。web

tcp三次握手

第一次握手

client發送一個SYN(J)包給server,而後等待server的ACK回覆,進入SYN-SENT狀態。p.s: SYN爲synchronize的縮寫,ACK爲acknowledgment的縮寫。面試

第二次握手

server接收到SYN(seq=J)包後就返回一個ACK(J+1)包以及一個本身的**SYN(K)**包,而後等待client的ACK回覆,server進入SYN-RECIVED狀態。算法

第三次握手

client接收到server發回的ACK(J+1)包後,進入ESTABLISHED狀態。而後根據server發來的SYN(K)包,返回給等待中的server一個ACK(K+1)包。等待中的server收到ACK回覆,也把本身的狀態設置爲ESTABLISHED。到此TCP三次握手完成,client與server能夠正常進行通訊了。shell

爲何要進行三次握手

咱們來看一下爲何須要進行三次握手,兩次握手難道不行麼?這裏咱們用一個生活中的具體例子來解釋就很好理解了。咱們能夠將三次握手中的客戶端和服務器之間的握手過程比喻成A和B通訊的過程:緩存

  • 在第一次通訊過程當中,A向B發送信息以後,B收到信息後能夠確認本身的收信能力和A的發信能力沒有問題。
  • 在第二次通訊中,B向A發送信息以後,A能夠確認本身的發信能力和B的收信能力沒有問題,可是B不知道本身的發信能力到底如何,因此就須要第三次通訊。
  • 在第三次通訊中,A向B發送信息以後,B就能夠確認本身的發信能力沒有問題。

wireshark

上面分析還不夠形象,很容易忘記,下面咱們利用wireshark來證實一下上面的分析過程。從下面的的輸出就能夠很容易看出來,必需要通過前面的三次tcp請求才會有起一次http請求。服務器

第一次請求客戶端發送一個SYN包,序列號是0。tcp

wireshark-tcp-01

第二次請求服務器會發送一個SYN和一個ACK包,序列號是0,ack號是1。ide

wireshark-tcp-02

第三次本地客戶端請求會發送一個ACK包,序列號是1,ack號是1來回復服務器。性能

wireshark-tcp-03

四次揮手

如下面這張圖爲例,咱們來分析一下TCP四次揮手的過程。

tcp四次揮手

第一次揮手

client發送一個FIN(M)包,此時client進入FIN-WAIT-1狀態,這代表client已經沒有數據要發送了。

第二次揮手

server收到了client發來的FIN(M)包後,向client發回一個ACK(M+1)包,此時server進入CLOSE-WAIT狀態,client進入FIN-WAIT-2狀態。

第三次揮手

server向client發送FIN(N)包,請求關閉鏈接,同時server進入LAST-ACK狀態。

第四次揮手

client收到server發送的FIN(N)包,進入TIME-WAIT狀態。向server發送**ACK(N+1)**包,server收到client的ACK(N+1)包之後,進入CLOSE狀態;client等待一段時間尚未獲得回覆後判斷server已正式關閉,進入CLOSE狀態。

TCP滑動窗口

通常在提到TCP三次握手的時候,一樣會涉及到TCP滑窗,下面咱們補充一下什麼是TCP滑窗。若是採用PAR的形式來傳遞的話,每一次發送方發送完包後必須獲得接收方的確認回覆,改進一下這個流程,發送端一次能夠發送多個包,沒必要每次等到接收方的ack回覆,同時接收端也要告訴發送端本身能接收多少。固然還須要保證順序性,對於亂序的情況,咱們能夠容許等待必定時間的亂序,好比先緩存提早到達的數據,而後等待須要的數據,若是必定時間沒有達到就drop掉。

TCP滑動窗口能夠解決咱們上面提到的概念,滑動窗口中的數據主要分爲下面幾類:

  1. Sent and Acknowledged: 這些數據表示已經發送成功並已被確認的數據。
  2. Sent But Not Yet Acknowledged: 這部分數據稱爲發送但尚未被確認,數據被髮送出去,沒有收到接收端的ACK,認爲並無徹底發送,這個屬於窗口內的數據。
  3. Not Sent, Recipient Ready to Receive: 這部分是儘快發送的數據,這部分數據已經被加載到緩存中,也就是窗口中了,等待發送,接受端已經告訴發送端本身可以徹底接受這些包,因此發送方須要儘快發送這些包。
  4. Not Sent, Recipient Not Ready to Reccive: 這些數據屬於未發送,由於這些數據已經超出了接收端所能接受的範圍。

對於接收端也是有一個接收窗口,相似發送端,接收端的數據有三個分類(注意接收端並不要等待ACK):

  1. Received and ACK Not Send to Process: 這部分數據屬於接收了數據可是尚未被上層的應用程序接收,也是被緩存在窗口中。
  2. Received Not ACK: 已經接收,可是尚未ACK。
  3. Not Received: 有空位可是尚未接收數據。

TCP重傳機制

下面這部份內容參考自coolshell,我認爲寫的比較好,因此轉過來分享一下,感興趣的朋友能夠閱讀一下 :)

TCP要保證全部的數據包均可以到達,因此,必須要有重傳機制。

注意,接收端給發送端的Ack確認只會確認最後一個連續的包,好比,發送端發了1,2,3,4,5一共五份數據,接收端收到了1,2,因而回ack 3,而後收到了4(注意此時3沒收到),此時的TCP會怎麼辦?咱們要知道,由於正如前面所說的,SeqNum和Ack是以字節數爲單位,因此ack的時候,不能跳着確認,只能確認最大的連續收到的包,否則,發送端就覺得以前的都收到了。

超時重傳機制

一種是不回ack,死等3,當發送方發現收不到3的ack超時後,會重傳3。一旦接收方收到3後,會ack 回 4——意味着3和4都收到了。

可是,這種方式會有比較嚴重的問題,那就是由於要死等3,因此會致使4和5即使已經收到了,而發送方也徹底不知道發生了什麼事,由於沒有收到Ack,因此,發送方可能會悲觀地認爲也丟了,因此有可能也會致使4和5的重傳。

對此有兩種選擇:

  1. 一種是僅重傳timeout的包。也就是第3份數據。
  2. 另外一種是重傳timeout後全部的數據,也就是第3,4,5這三份數據。

這兩種方式有好也有很差。第一種會節省帶寬,可是慢,第二種會快一點,可是會浪費帶寬,也可能會有無用功。但整體來講都很差。由於都在等timeout,timeout可能會很長。

快速重傳機制

因而,TCP引入了一種叫Fast Retransmit 的算法,不以時間驅動,而以數據驅動重傳。也就是說,若是,包沒有連續到達,就ack最後那個可能被丟了的包,若是發送方連續收到3次相同的ack,就重傳。Fast Retransmit的好處是不用等timeout了再重傳。

好比:若是發送方發出了1,2,3,4,5份數據,第一份先到送了,因而就ack回2,結果2由於某些緣由沒收到,3到達了,因而仍是ack回2,後面的4和5都到了,可是仍是ack回2,由於2仍是沒有收到,因而發送端收到了三個ack=2的確認,知道了2尚未到,因而就立刻重轉2。而後,接收端收到了2,此時由於3,4,5都收到了,因而ack回6。

Fast Retransmit只解決了一個問題,就是timeout的問題,它依然面臨一個艱難的選擇,就是,是重傳以前的一個仍是重傳全部的問題。對於上面的示例來講,是重傳#2呢仍是重傳#2,#3,#4,#5呢?由於發送端並不清楚這連續的3個ack(2)是誰傳回來的?也許發送端發了20份數據,是#6,#10,#20傳來的呢。這樣,發送端頗有可能要重傳從2到20的這堆數據(這就是某些TCP的實際的實現)。可見,這是一把雙刃劍。

References

TCP-CONNECTION

TCP-TERMINATION

相關文章
相關標籤/搜索