TCP傳輸

看過太多tcp相關文章,可是看完老是不過癮,似懂非懂,反覆考慮事後,我以爲是那些文章太過理論,看起來沒有體感,因此吸取不了。 但願這篇文章能作到言簡意賅,幫助你們透過案例來理解原理。
java

tcp的特色

這個你們基本都能說幾句,面試的時候候選人也確定會告訴你這些:mysql

  • 三次握手
  • 四次揮手
  • 可靠鏈接
  • 丟包重傳

可是我只但願你們記住一個核心的:tcp是能夠可靠傳輸協議,它的全部特色都爲這個可靠傳輸服務面試

那麼tcp是怎麼樣來保障可靠傳輸呢?

tcp在傳輸過程當中都有一個ack,接收方經過ack告訴發送方收到那些包了。這樣發送方能知道有沒有丟包,進而肯定重傳。sql

tcp建鏈接的三次握手

來看一個java代碼鏈接數據庫的三次握手過程數據庫

image.pngimage.pngtcp

三個紅框表示創建鏈接的三次握手:性能

  • 第一步:client 發送 syn 到server 發起握手;
  • 第二步:server 收到 syn後回覆syn+ack給client;
  • 第三步:client 收到syn+ack後,回覆server一個ack表示收到了server的syn+ack(此時client的48287端口的鏈接已是established)

握手的核心目的是告知對方seq(綠框是client的初始seq,藍色框是server 的初始seq),對方回覆ack(收到的seq+包的大小),這樣發送端就知道有沒有丟包了。優化

握手的次要目的是告知和協商一些信息,圖中黃框。spa

  • MSS–最大傳輸包
  • SACK_PERM–是否支持Selective ack(用戶優化重傳效率)
  • WS–窗口計算指數(有點複雜的話先不用管)

這就是tcp爲何要握手創建鏈接,就是爲了解決tcp的可靠傳輸。3d

tcp斷開鏈接的四次揮手

再來看java連上mysql後,執行了一個SQL: select sleep(2); 而後就斷開了鏈接

image.pngimage.png

四個紅框表示斷開鏈接的四次揮手:

  • 第一步: client主動發送fin包給server
  • 第二步: server回覆ack(對應第一步fin包的ack)給client,表示server知道client要斷開了
  • 第三步: server發送fin包給client,表示server也能夠斷開了
  • 第四部: client回覆ack給server,表示既然雙發都發送fin包表示斷開,那麼就真的斷開吧

爲何握手三次、揮手四次

這個問題太噁心,面試官太喜歡問,其實他也許只能背誦:由於……。

我也不知道怎麼回答。網上都說tcp是雙向的,因此斷開要四次。可是我認爲建鏈接也是雙向的(雙向都協調告知對方本身的seq號),爲何不須要四次握手呢,因此網上說的不必定精準。

你再看三次握手的第二步發 syn+ack,若是拆分紅兩步先發ack再發syn徹底也是能夠的(效率略低),這樣三次握手也變成四次握手了。

看起來揮手的時候多一次,主要是收到第一個fin包後單獨回覆了一個ack包,若是能回覆fin+ack那麼四次揮手也就變成三次了。 來看一個案例:

image.pngimage.png

圖中第二個紅框就是回覆的fin+ack,這樣四次揮手變成三次了(若是一個包就是一次的話)。

個人理解:之因此絕大數時候咱們看到的都是四次揮手,是由於收到fin後,知道對方要關閉了,而後OS通知應用層要關閉啥的,這裏應用層可能須要作些準備工做,有一些延時,因此先回ack,準備好了再發fin 。 握手過程沒有這個準備過程因此能夠當即發送syn+ack。

ack=seq+len

ack老是seq+len(包的大小),這樣發送方明確知道server收到那些東西了。

可是特例是三次握手和四次揮手,雖然len都是0,可是syn和fin都要佔用一個seq號,因此這裏的ack都是seq+1。

image.pngimage.png

看圖中左邊紅框裏的len+seq就是接收方回覆的ack的數字,表示這個包接收方收到了。而後下一個包的seq就是前一個包的len+seq,依次增長,一旦中間發出去的東西沒有收到ack就是丟包了,過一段時間(或者其餘方式)觸發重傳,保障了tcp傳輸的可靠性。

三次握手中協商的其它信息

MSS 最大一個包中能傳輸的信息(不含tcp、ip包頭),MSS+包頭就是MTU(最大傳輸單元),若是MTU過大可能在傳輸的過程當中被卡住過不去形成卡死(這個大小的包一直傳輸不過去),跟丟包還不同。

SACK_PERM 用於丟包的話提高重傳效率,好比client一次發了一、二、三、四、5 這5個包給server,實際server收到了 一、三、四、5這四個包,中間2丟掉了。這個時候server回覆ack的時候,都只能回覆2,表示2前面全部的包都收到了,給我發第二個包吧,若是server 收到三、四、5仍是沒有收到2的話,也是回覆ack 2而不是回覆ack 三、四、五、6的,表示快點發2過來。

可是這個時候client雖然知道2丟了,而後會重發2,可是不知道三、四、5有沒有丟啊,實際三、四、5 server都收到了,若是支持sack,那麼能夠ack 2的時候同時告訴client 三、四、5都收到了,這樣client重傳的時候只重傳2就能夠,若是沒有sack的話那麼可能會重傳二、三、四、5,這樣效率就低了。

來看一個例子:

image.pngimage.png

圖中的紅框就是SACK。

知識點:ack數字表示這個數字前面的數據收到了。

總結下

tcp全部特性基本上核心都是爲了可靠傳輸這個目標來服務的,而後有一些是出於優化性能的目的。

後續但願再經過幾個案例來深化一下上面的知識。

相關文章
相關標籤/搜索