看過太多tcp相關文章,可是看完老是不過癮,似懂非懂,反覆考慮事後,我以爲是那些文章太過理論,看起來沒有體感,因此吸取不了。 但願這篇文章能作到言簡意賅,幫助你們透過案例來理解原理。
java
這個你們基本都能說幾句,面試的時候候選人也確定會告訴你這些:mysql
可是我只但願你們記住一個核心的:tcp是能夠可靠傳輸協議,它的全部特色都爲這個可靠傳輸服務。面試
tcp在傳輸過程當中都有一個ack,接收方經過ack告訴發送方收到那些包了。這樣發送方能知道有沒有丟包,進而肯定重傳。sql
來看一個java代碼鏈接數據庫的三次握手過程數據庫
三個紅框表示創建鏈接的三次握手:性能
握手的核心目的是告知對方seq(綠框是client的初始seq,藍色框是server 的初始seq),對方回覆ack(收到的seq+包的大小),這樣發送端就知道有沒有丟包了。優化
握手的次要目的是告知和協商一些信息,圖中黃框。spa
這就是tcp爲何要握手創建鏈接,就是爲了解決tcp的可靠傳輸。3d
再來看java連上mysql後,執行了一個SQL: select sleep(2); 而後就斷開了鏈接
四個紅框表示斷開鏈接的四次揮手:
這個問題太噁心,面試官太喜歡問,其實他也許只能背誦:由於……。
我也不知道怎麼回答。網上都說tcp是雙向的,因此斷開要四次。可是我認爲建鏈接也是雙向的(雙向都協調告知對方本身的seq號),爲何不須要四次握手呢,因此網上說的不必定精準。
你再看三次握手的第二步發 syn+ack,若是拆分紅兩步先發ack再發syn徹底也是能夠的(效率略低),這樣三次握手也變成四次握手了。
看起來揮手的時候多一次,主要是收到第一個fin包後單獨回覆了一個ack包,若是能回覆fin+ack那麼四次揮手也就變成三次了。 來看一個案例:
圖中第二個紅框就是回覆的fin+ack,這樣四次揮手變成三次了(若是一個包就是一次的話)。
個人理解:之因此絕大數時候咱們看到的都是四次揮手,是由於收到fin後,知道對方要關閉了,而後OS通知應用層要關閉啥的,這裏應用層可能須要作些準備工做,有一些延時,因此先回ack,準備好了再發fin 。 握手過程沒有這個準備過程因此能夠當即發送syn+ack。
ack老是seq+len(包的大小),這樣發送方明確知道server收到那些東西了。
可是特例是三次握手和四次揮手,雖然len都是0,可是syn和fin都要佔用一個seq號,因此這裏的ack都是seq+1。
看圖中左邊紅框裏的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,這樣效率就低了。
來看一個例子:
圖中的紅框就是SACK。
知識點:ack數字表示這個數字前面的數據都收到了。
tcp全部特性基本上核心都是爲了可靠傳輸這個目標來服務的,而後有一些是出於優化性能的目的。
後續但願再經過幾個案例來深化一下上面的知識。