閱讀758 返回首頁    go 微軟 go Office


就是要你懂TCP -- 握手和揮手

就是要你懂 TCP -- 握手和揮手

看過太多tcp相關文章,但是看完總是不過癮,似懂非懂,反複考慮過後,我覺得是那些文章太過理論,看起來沒有體感,所以吸收不了。

希望這篇文章能做到言簡意賅,幫助大家透過案例來理解原理

tcp的特點

tcp的特點大家基本都能說幾句,麵試的時候候選人也肯定會告訴你這些:

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

但是我隻希望大家記住一個核心的:**tcp是可以可靠傳輸協議,它的所有特點都為這個可靠傳輸服務**。

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

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

tcp建連接的三次握手

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

image.png

三個紅框表示建立連接的三次握手:

  • 第一步: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+包的大小),這樣發送端就知道有沒有丟包了

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

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

這就是tcp為什麼要握手建立連接,就是為了解決tcp的可靠傳輸

tcp斷開連接的四次揮手

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

image.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.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.png

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

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

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

MSS的問題具體可以看我這篇文章: scp某個文件的時候卡死問題的解決過程

SACK_PERM 用於丟包的話提升重傳效率,比如client一次發了1、2、3、4、5 這5個包給server,實際server收到了 1、3、4、5這四個包,中間2丟掉了。這個時候server回複ack的時候,都隻能回複2,表示2前麵所有的包都收到了,給我發第二個包吧,如果server 收到3、4、5還是沒有收到2的話,也是回複ack 2而不是回複ack 3、4、5、6的,表示快點發2過來。

但是這個時候client雖然知道2丟了,然後會重發2,但是不知道3、4、5有沒有丟啊,實際3、4、5 server都收到了,如果支持sack,那麼可以ack 2的時候同時告訴client 3、4、5都收到了,這樣client重傳的時候隻重傳2就可以,如果沒有sack的話那麼可能會重傳2、3、4、5,這樣效率就低了。

來看一個例子:

image.png

圖中的紅框就是SACK。

知識點:ack數字表示這個數字前麵的數據**都**收到了

總結下

tcp所有特性基本上核心都是為了**可靠傳輸**這個目標來服務的,然後有一些是出於優化性能的目的

三次握手建連接的詳細過程可以參考我這篇: 關於TCP 半連接隊列和全連接隊列

後續希望再通過幾個案例來深化一下上麵的知識。

我的其他幾篇跟網絡問題相關的文章,也很有趣,借著案例來理解好概念和原理,希望對大家也有點幫助

網絡通不通這是個麻煩的大問題 -- 一個網絡包的旅程

https://www.atatech.org/articles/73289

https://www.atatech.org/articles/76138

https://www.atatech.org/articles/60633

https://www.atatech.org/articles/73174


說點關於學習的題外話

什麼是工程效率,什麼是知識效率

有些人純看理論就能掌握好一門技能,還能舉一反三,這是知識效率,這種人非常少;

大多數普通人都是看點知識然後結合實踐來強化理論,要經過反反複複才能比較好地掌握一個知識,這就是工程效率,講究技巧、工具來達到目的。

肯定知識效率最牛逼,但是擁有這種技能的人畢竟非常少。從小我們周邊那種不怎麼學的學霸型基本都是這類,這種學霸都還能觸類旁通非常快的掌握一個新知識,非常氣人。剩下的絕大部分隻能拚時間+方法+總結等也能掌握一些知識

非常遺憾我就是工程效率型,隻能羨慕那些知識效率型的學霸。但是這事又不能獨立看待有些人在某些方向上是工程效率型,有些方向就又是知識效率型(有一種知識效率型是你掌握的實在太多也就比較容易觸類旁通了,這算灰色知識效率型)

使勁挖掘自己在知識效率型方麵的能力吧,即使灰色地帶也行啊

最後更新:2017-06-07 13:31:54

  上一篇:go  阿裏雲前端周刊 - 第 10 期
  下一篇:go  八年了必須放手了,我不是你媽媽