TCP如何保證傳輸可靠性

 TCP 滑動窗口和 擁塞窗口

 

 

TCP協議保證數據傳輸可靠性的方式主要有:

(校序重流擁)

 

校驗和

       發送的數據包的二進制相加然後取反,目的是檢測數據在傳輸過程中的任何變化。如果收到段的檢驗和有差錯,TCP將丟棄這個報文段和不確認收到此報文段。 

確認應答+序列號

       TCP給發送的每一個包進行編號,接收方對數據包進行排序,把有序數據傳送給應用層。 

超時重傳

       當TCP發出一個段後,它啓動一個定時器等待目的端確認收到這個報文段如果不能及時收到一個確認,將重發這個報文段。 

 

流量控制

       TCP連接的每一方都有固定大小的緩衝空間,TCP的接收端只允許發送端發送接收端緩衝區能接納的數據。當接收方來不及處理髮送方的數據,能提示發送方降低發送的速率,防止包丟失。TCP使用的流量控制協議是可變大小的滑動窗口協議。

        接收方有即時窗口(滑動窗口),隨ACK報文發送

 

擁塞控制

       當網絡擁塞時,減少數據的發送。

         發送方有擁塞窗口,發送數據前比對接收方發過來的即使窗口,取小

          慢啓動、擁塞避免、擁塞發送、快速恢復

 

應用數據被分割成TCP認爲最適合發送的數據塊。 

TCP的接收端會丟棄重複的數據。 

 

 

1、校驗和

計算方式:在數據傳輸的過程中,將發送的數據段都當做一個16位的整數。將這些整數加起來。並且前面的進位不能丟棄,補在後面,最後取反,得到校驗和。 

發送方:在發送數據之前計算檢驗和,並進行校驗和的填充。 

接收方:收到數據後,對數據以同樣的方式進行計算,求出校驗和,與發送方的進行比對。


 注意:如果接收方比對校驗和與發送方不一致,那麼數據一定傳輸有誤。但是如果接收方比對校驗和與發送方一致,數據不一定傳輸成功

 

2、確認應答與序列號

序列號:TCP傳輸時將每個字節的數據都進行了編號,這就是序列號。 

確認應答:TCP傳輸的過程中,每次接收方收到數據後,都會對傳輸方進行確認應答。也就是發送ACK報文。這個ACK報文當中帶有對應的確認序列號,告訴發送方,接收到了哪些數據,下一次的數據從哪裏發。


 

序列號的作用不僅僅是應答的作用,有了序列號能夠將接收到的數據根據序列號排序,並且去掉重複序列號的數據。這也是TCP傳輸可靠性的保證之一。

 

3、超時重傳

在進行TCP傳輸時,由於確認應答與序列號機制,也就是說發送方發送一部分數據後,都會等待接收方發送的ACK報文,並解析ACK報文,判斷數據是否傳輸成功。如果發送方發送完數據後,遲遲沒有等到接收方的ACK報文,這該怎麼辦呢?而沒有收到ACK報文的原因可能是什麼呢?

 

首先,發送方沒有接收到響應的ACK報文原因可能有兩點

a、數據在傳輸過程中由於網絡原因等直接全體丟包,接收方沒有接收到

b、接收方接收到了響應的數據,但是發送的ACK報文響應卻由於網絡原因丟包了

 

TCP在解決這個問題的時候引入了一個新的機制,叫做超時重傳機制。簡單理解就是發送方在發送完數據後等待一個時間,時間到達沒有接收到ACK報文,那麼對剛纔發送的數據進行重新發送。如果是剛纔第一個原因,接收方收到二次重發的數據後,便進行ACK應答。如果是第二個原因,接收方發現接收的數據已存在(判斷存在的根據就是序列號,所以上面說序列號還有去除重複數據的作用),那麼直接丟棄,仍舊發送ACK應答。

 

那麼發送方發送完畢後等待的時間是多少呢?如果這個等待的時間過長,那麼會影響TCP傳輸的整體效率,如果等待時間過短,又會導致頻繁的發送重複的包。如何權衡?

 

由於TCP傳輸時保證能夠在任何環境下都有一個高性能的通信,因此這個最大超時時間(也就是等待的時間)是動態計算的。

 

注意:

超時以500ms(0.5秒)爲一個單位進行控制,每次判定超時重發的超時時間都是500ms的整數倍。重發一次後,仍未響應,那麼等待2*500ms的時間後,再次重傳。等待4*500ms的時間繼續重傳。以一個指數的形式增長。累計到一定的重傳次數,TCP就認爲網絡或者對端出現異常,強制關閉連接。

 

 

4、連接管理

連接管理就是三次握手與四次揮手的過程,在前面詳細講過這個過程,這裏不再贅述。保證可靠的連接,是保證可靠性的前提。

 

4.1 流量控制

接收端在接收到數據後,對其進行處理。如果發送端的發送速度太快,導致接收端的結束緩衝區很快的填充滿了。此時如果發送端仍舊發送數據,那麼接下來發送的數據都會丟包,繼而導致丟包的一系列連鎖反應,超時重傳呀什麼的。而TCP根據接收端對數據的處理能力,決定發送端的發送速度,這個機制就是流量控制。

 

在TCP協議的報頭信息當中,有一個16位字段的窗口大小。在介紹這個窗口大小時我們知道,窗口大小的內容實際上是接收端接收數據緩衝區的剩餘大小。這個數字越大,證明接收端接收緩衝區的剩餘空間越大,網絡的吞吐量越大。接收端會在確認應答發送ACK報文時,將自己的即時窗口大小填入,並跟隨ACK報文一起發送過去發送方根據ACK報文裏的窗口大小的值的改變進而改變自己的發送速度。如果接收到窗口大小的值爲0,那麼發送方將停止發送數據。並定期的向接收端發送窗口探測數據段,讓接收端把窗口大小告訴發送端。 


 注:16位的窗口大小最大能表示65535個字節(64K),但是TCP的窗口大小最大並不是64K。在TCP首部中40個字節的選項中還包含了一個窗口擴大因子M,實際的窗口大小就是16爲窗口字段的值左移M位。每移一位,擴大兩倍。

 

4.2 擁塞控制

TCP傳輸的過程中,發送端開始發送數據的時候,如果剛開始就發送大量的數據,那麼就可能造成一些問題。網絡可能在開始的時候就很擁堵,如果給網絡中在扔出大量數據,那麼這個擁堵就會加劇。擁堵的加劇就會產生大量的丟包,就對大量的超時重傳,嚴重影響傳輸。

 

所以TCP引入了慢啓動的機制,在開始發送數據時,先發送少量的數據探路。探清當前的網絡狀態如何,再決定多大的速度進行傳輸。這時候就引入一個叫做擁塞窗口的概念。發送剛開始定義擁塞窗口爲 1,每次收到ACK應答,擁塞窗口加 1。在發送數據之前,首先將擁塞窗口與接收端反饋的窗口大小比對,取較小的值作爲實際發送的窗口。

 

擁塞窗口的增長是指數級別的。慢啓動的機制只是說明在開始的時候發送的少,發送的慢,但是增長的速度是非常快的。爲了控制擁塞窗口的增長,不能使擁塞窗口單純的加倍,設置一個擁塞窗口的閾值,當擁塞窗口大小超過閾值時,不能再按照指數來增長,而是線性的增長。在慢啓動開始的時候,慢啓動的閾值等於窗口的最大值,一旦造成網絡擁塞,發生超時重傳時慢啓動的閾值會爲原來的一半(這裏的原來指的是發生網絡擁塞時擁塞窗口的大小),同時擁塞窗口重置爲 1。 


 

擁塞控制是TCP在傳輸時儘可能快的將數據傳輸,並且避免擁塞造成的一系列問題。是可靠性的保證,同時也是維護了傳輸的高效性。

 

擁塞控制主要是四個算法:1)慢啓動,2)擁塞避免,3)擁塞發生,4)快速恢復。

 

擁塞避免

當擁塞窗口 >= 閥值時,就會進入“擁塞避免算法”。

這樣就可以避免增長過快導致網絡擁塞,慢慢的增加調整到網絡的最佳值。是一個線性上升的算法。

 

擁塞發生

當發生超時重傳後,閥值會變成擁塞窗口的一半

 

快速恢復

 

================================================================================

TCP滑動窗口

 

需要說明一下,如果你不瞭解TCP的滑動窗口這個事,你等於不瞭解TCP協議。我們都知道,TCP必需要解決的可靠傳輸以及包亂序(reordering)的問題,所以,TCP必需要知道網絡實際的數據處理帶寬或是數據處理速度,這樣纔不會引起網絡擁塞,導致丟包。

 

所以,TCP引入了一些技術和設計來做網絡流控,Sliding Window是其中一個技術。 前面我們說過,TCP頭裏有一個字段叫Window,又叫Advertised-Window,這個字段是接收端告訴發送端自己還有多少緩衝區可以接收數據。於是發送端就可以根據這個接收端的處理能力來發送數據,而不會導致接收端處理不過來。 爲了說明滑動窗口,我們需要先看一下TCP緩衝區的一些數據結構:



 

上圖中,我們可以看到:

 

1、接收端LastByteRead指向了TCP緩衝區中讀到的位置,NextByteExpected指向的地方是收到的連續包的最後一個位置,LastByteRcved指向的是收到的包的最後一個位置,我們可以看到中間有些數據還沒有到達,所以有數據空白區。

2、發送端的LastByteAcked指向了被接收端Ack過的位置(表示成功發送確認),LastByteSent表示發出去了,但還沒有收到成功確認的Ack,LastByteWritten指向的是上層應用正在寫的地方。

 

於是:

a、接收端在給發送端回ACK中會彙報自己的AdvertisedWindow = MaxRcvBuffer – LastByteRcvd – 1;

b、而發送方會根據這個窗口來控制發送數據的大小,以保證接收方可以處理。

下面我們來看一下發送方的滑動窗口示意圖:


 上圖中分成了四個部分,分別是:(其中那個黑模型就是滑動窗口

 

#1已收到ack確認的數據。

#2已發出但還沒收到ack的

#3在窗口中還沒有發出的(接收方還有空間)

#4窗口以外的數據(接收方沒空間)

 

注意:

滑動窗口裏是 已發出但未收到ACk、還未發出的 數據

 

下面是個滑動後的示意圖(收到36的ack,併發出了46-51的字節):


 下面我們來看一個接受端控制發送端的圖示:



 

=====================================================================

問題:

TCP是以段爲單位進行數據包的發送的。

(1)在建立TCP連接的同時,也可以確定發送數據包的單位,稱之爲“最大消息長度”:MSS。最理想的情況是,最大消息長度MSS正好是IP層中不被分片處理的最大數據長度。

(2)TCP在傳送大量數據的時候,是以“段=MSS的大小”將數據進行分割發送的,進行重發時也是以MSS爲單位的。

(3)最大消息長度——MSS是在三次握手的時候,在兩端主機之間被計算得出的。兩端主機在發出“建立TCP連接請求的SYN包”時,會在SYN包的TCP首部中寫入MSS選項,告訴對方自己所能夠適應的MSS的大小,然後發送端主機會在兩者之間選擇一個較小的MSS值投入使用。

 

 

TCP爲什麼引入接受緩存這個數據結構?

如果沒有接受緩存的話,或者說只有一個緩存的話,爲了保證接受的數據是按順序傳輸的,所以如果位於x序號之後的序號分組先到達目的主機的運輸層的話必然丟棄,這樣的話將在重傳上花費很大的開銷,所以一般如果有過大的序號達到接收端,那麼會按照序號緩存起來等待之前的序號分許到達,然後一併交付到應用進程。

 

TCP 粘包/拆包的原因及解決方法

TCP是以流的方式來處理數據,一個完整的包可能會被TCP拆分成多個包進行發送也可能把小的封裝成一個大的數據包發送。

 

TCP粘包/分包的原因:

應用程序寫入的字節大小大於套接字發送緩衝區的大小,會發生拆包現象,而應用程序寫入數據小於套接字緩衝區大小,網卡將應用多次寫入的數據發送到網絡上,這將會發生粘包現象;

進行MSS大小的TCP分段,當TCP報文長度-TCP頭部長度>MSS的時候將發生拆包

以太網幀的payload(淨荷)大於MTU(1500字節)進行ip分片。

 

解決方法

消息定長:FixedLengthFrameDecoder類

包尾增加特殊字符分割:行分隔符類:LineBasedFrameDecoder或自定義分隔符類 :DelimiterBasedFrameDecoder

將消息分爲消息頭和消息體:LengthFieldBasedFrameDecoder類。分爲有頭部的拆包與粘包、長度字段在前且有頭部的拆包與粘包、多擴展頭部的拆包與粘包。

 

 

利用滑動窗口控制提高速度

(1)TCP是以一個段爲單位進行數據的傳輸的,每發送一個段,就會等待對端主機的針對這個段的確認應答信號ACK,但這樣的傳輸方式的缺點也很明顯,就是:當數據包的往返時間越長,通信性能越低

(2)爲了解決這個問題,TCP引入了窗口這個概念,即使在往返時間比較長的情況下,它也能夠控制網絡性能的下降。

確認應答包不再以每個段爲單位進行確認了,而是以更大的單位進行確認,轉發時間將會被大幅度的縮短。也就是說,發送端主機在發送了一個段之後,沒必要一直等待對端主機的確認應答信號,而是繼續發送。

(3)窗口大小,指的就是無需等待接收端主機的確認應答信號而可以持續發送的數據的最大值,或者說段的最大值。滑動窗口控制的實現,使用了大量的緩衝區,通過對多個段的數據同時進行確認應答來實現高效傳輸。

(4)發送數據中的高亮部分正是前面提到的窗口。在這個窗口內的數據即便沒有收到確認應答也可以發送出去,此外發送端主機在等到對端主機的確認應答之前,必須在緩衝區中保留這部分數據,以便數據包的丟失而重發。

(5)在滑動窗口以外的數據,包括尚未發送出去的數據,以及已經確認對端收到的數據,當發送端確認對端已經收到數據包之後,此數據包就可以從緩衝區中清除了。

 

當收到確認應答信號過之後,會把滑動窗口的位置滑動到確認應答的序列號的位置,這樣就可以順序的將多個段同時發送以提高通信功能了。這就是滑動窗口控制。

 

 

 

 

發佈了701 篇原創文章 · 獲贊 67 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章