tcp重傳機制,流量控制,擁塞控制


 三次握手 

三次握手協議的過程:

a.客戶端 向 服務器端 發送一個 SYN 包,請求一個主動打開。該包攜帶客戶端為這個連接請求設定的隨機數A作為消息列號。

b.服務器端接收到一個SYN包后,把該包放入SYN隊列中;回送一個SYN/ACK。ACK的確認碼應為A+1,SYN/ACK包本身攜帶一個隨機產生的序號B。

c.客戶端收到SYN/ACK包后,發送一個ACK的包,該包的序號被設定為A+1,而ACK的確認碼為B+1。當服務器端收到這個ACK包的時候,把請求幀從SYN隊列中移出,放置ACCEPT隊列中。

場景:當服務器端接收到客戶端發送過來的SYN后, 回了SYN-ACK后,客戶端掉線了,服務器端沒有收到客戶端回來的ACK,那這個連接 就 處於 一個中間狀態,沒成功也沒失敗。

但是,服務器端如果在一定時間內沒有收到TCP會重新發SYN-ACK。

  • 主機收到一個TCP包時,用兩端的IP地址與端口號來標識這個TCP包屬於哪個session。使用一張表來存儲所有的session,表中的每條稱作TCB(Transmit Control Block)。
  • TCB結構的定義包含:連接使用的源端口, 目的端口,目的ip, 序號, 應答序號, 對方窗口大小, 已方窗口大小, tcp狀態, tcp輸入/輸出隊列, 應用層輸出隊列, tcp的重傳有關變量等。
  • 服務器端的連接數量是無限的,只受內存的限制。

 

tcp報文頭

  • 來源連接端口,16位長,識別發送連接端口
  • 目的連接端口,16位長,識別接收連接端口
  • 序列號(seq,32位長)
  • 確認號(ack,32位長),期望收到的數據的開始序列號,也即已經收到的數據的字節長度加1
  • 資料偏移(4位長),以4字節為單位計算出的數據段開始地址的偏移值。
  • 保留,需置0
  • ACK—為1表示確認號字段有效
  • SYN—為1表示這是連接請求或是連接接受請求,用於創建連接和使順序號同步
  • FIN—為1表示發送方沒有數據要傳輸了,要求釋放連接
  • RST—為1表示出現嚴重差錯。可能需要重新創建TCP連接。還可以用於拒絕非法的報文段和拒絕連接請求
  • 緊急指針(16位長)—本報文段中的緊急數據的最后一個字節的序號
  • 窗口(WIN,16位長)—表示從確認號開始,本報文的發送方可以接收的字節數,即接收窗口大小。用於流量控制
  • 校驗和(Checksum,16位長)—對整個的TCP報文段,包括TCP頭部和TCP數據,以16位字進行計算所得。這是一個強制性的字段

 

重傳機制

方式

  • 超時重傳

    • 概念

      • 發送數據時設定一個定時器,若在指定時間內沒有收到應答報文,就會重發數據
    • 發生超時重傳的時機

      • 數據包丟失
      • 確認應答丟失
    • 超時時間RTO選擇

      • 略大於RTT
      • 重傳超時策略:超時時間間隔加倍
  • 快速重傳

    • 概念

      • 發送方可以一次發送多個數據包,若中間的某個數據包丟失了,接收方會一直回復這個丟失的數據包應答報文,接收方若收到三次這個數據包的應答報文,就知道該報文還沒有被接收方收到,可以重傳這個數據包
    • 問題

      • 因為接收方對后續的數據包也返回了丟失的那個應答報文,所以發送方不知道后續的數據包是否丟失,也就不知道應該重傳丟失的數據包還是把后續的數據包都重傳
  • SACK

    • 概念

      • 選擇性重傳,解決快速重傳不知道重傳哪些報文的缺點
    • 實現方式

      • 在tcp頭部“選項”字段里加一個SACK,將緩存的地圖發送給發送方,這樣發送方就知道哪些數據接收方收到了,哪些數據接收方沒有收到,以便重傳接收方沒有收到的數據
    • 參數

      • 要開啟SACK,需要發送方和接收方都支持:net.ipv4.tcp_sack,linux2.4以后默認打開
  • D-SACK

    • 概念

      • 對SACK的擴展,通過SACK告訴發送方哪些數據被重復接收了
    • 實現方式

      • 若有發送方有重復發送數據包,會通過SACK告訴發送方這這個數據包已被發送過
    • 好處

      • 發送方能夠知道是發出去的包丟了還是接收方發送的ACK丟了

        • SACK若告知這個包已被發送過,那么說明是接收方發送的ACK丟了
      • 發送方可以知道發出去的數據包是否被網絡延遲了

        • 發送方在延遲之后會重發數據包,之前的數據包若一段時間后到達了接收方,接收方返回的應答報文中能看到該數據包已經被接收了,是個重復的報文,說明被網絡延遲了,而重發的數據包已被收到

序列號與確認應答(ACK)保證了tcp的可靠傳輸

滑動窗口

引入原因

  • 每發送一個數據都需要進行確認應答,收到了再發送下一個,效率比較低。在窗口大小限制范圍內,可以無需等待上一個數據包應答,就可以繼續發送下一個數據包

錯誤處理

  • 累計應答

    • 發送數據包丟失

      • 若發送方發送了一批數據包,中間的某個數據包丟失,那么接收方回復應答時ACK會回復丟失的那個數據包,這樣接收方就知道丟失的數據包是哪一個,因此可以重新發送;
    • 應答報文丟失

      • 若接收方返回的這一組數據包中,某一個應答報文丟失了,只要最后一個應答報文發送成功了,接收方就知道這個數據包的其實是收到了的

窗口大小

  • tcp頭里的字段window

    • 接收方通過這個字段告訴接收方自己還有多少緩沖區可以接收數據,發送方就可以根據接收方的處理來發送數據,以免導致接收方處理不過來,因此窗口的大小是由接收方決定的
  • 接收方和發送方的窗口

    • 接收方和發送方的窗口大小基本相等,因為發送方的窗口大小取決於接收方,當接收方處理能力快,窗口變大,通過tcp報文中的window字段告訴接收方,若傳輸過程中出現了延遲,所以這時兩個窗口大小不一致

擁塞控制

概念

  • 流量控制是避免發送方填滿接收方的緩存,但若因為其他主機之間的通信造成網絡擁堵,會有超時和丟包發生,這樣會導致重傳 ,網絡負擔會更大,進入惡性循環,所以tcp不能忽略網絡上發生的事,當網絡發生擁堵時,它會降低數據的發送量。擁塞控制的目的就是避免發送方的數據填滿整個網絡

擁塞窗口cwnd

  • 發送窗口swnd和接收窗口rwnd是約等於的關系,有了擁塞窗口的概念后,發送窗口swnd=min(cwnd, rwnd)

    • 網絡中沒有出現擁塞,cwnd會增大,反之會減小
  • 判斷網絡擁塞的方法

    • 發生超時重傳
  • 相關算法

    • 慢啟動

      • tcp剛建立連接完成,會有個慢啟動的過程,一點一點提高發送數據包的數量
      • 每接收到一個ack,cwnd大小就會加1,初始化時,cwnd大小為1,即cwnd大小按指數級增長
      • ssthreshold,slow start thresold,慢啟動門限

        • 當cwnd < ssthreshold時,會采用慢啟動算法
        • 當cwnd >= ssthreshold時,會使用擁塞避免算法
    • 擁塞避免

      • 每收到一個ack,cwnd增加1/cwnd。即囤積了cwnd這么多個包后,一次性發送過去。之后都會這樣發送,cwnd按線性增長
      • 當一直這么增長,會慢慢進入擁塞狀況,於是開始出現丟包現象,這時需要對丟失的數據進行重傳,當觸發了重傳機制也就進入了擁塞發生算法
    • 擁塞發生

      • 超時重傳

        • ssthreshold設為cwnd/2
        • cwnd設為1
        • 即一旦發生超時重傳就重新進入慢啟動
      • 快速重傳

        • 當接收方發現丟了一個中間包時,會發送三次丟失包的ack,發送方收到后就會快速重傳丟失的包
        • tcp認為這時候擁塞並不嚴重,只丟了一小部分包,於是

          • cwnd = cwnd/2
          • ssthreshold變為cwnd
        • 然后進入快速恢復算法
    • 快速恢復

      • cwnd = ssthresold+3
      • 重傳丟失的數據包
      • 如果再收到重復的ack,那么cwnd+1
      • 收到新的ack后,cwnd變為ssthreshold,然后進入擁塞避免算法

流量控制

概念

  • 流量控制是基於滑動窗口實現的,tcp通過讓接收方指明希望從發送方接收的數據大小(窗口大小)來進行流量控制

問題

  • 描述

    • 接收方收到了太多數據,暫時不能接收數據了,於是返回了window大小為0
    • 發送方發現window大小為0,於是暫時不再發送數據包了
    • 接收方處理完數據,可以繼續處理了,於是在之前已處理完的數據包的應答報文中更新窗口大小,但是該應答報文丟失了,導致接收方沒能收到,於是就一直不發送新的數據包了,出現了死鎖
  • 解決方法

    • tcp為每個連接設定一個持續計時器,只有發起連接的一方從對方收到零窗口通知,就啟動計時器;如果持續計時器超時,就會發送窗口探測報文,對方會給出自己現在接收窗口的大小
    • 窗口探測次數一般為3次,每次大約30-60秒,如果三次后窗口還是0,有的tcp實現會發起RST報文來中斷連接

糊塗窗口綜合症

  • 發送方

    • 發送方雖然知道窗口很大,但是每次都只發送很少的數據
  • 接收方

    • 接收方太忙,每次都只能從window中取出很少的數據,然后通知發送方,這樣發送方每次也只發送很少的數據
  • 問題

    • 每次都只傳輸小包,效率低
  • 解決方法

    • 讓接收方不通知小窗口給發送方

      • 當窗口大小小於min(mss,(緩存空間/2))時,就會向發送方通知窗口為0,阻止發送方后續發送數據過來

        • 注mss,Maximum Segment Size,最大報文長度,MSS是TCP報文段中的數據字段的最大長度,不包括TCP首部的長度
    • 讓發送方不發送小包

      • Nagel算法

        • 發送條件

          • 等到窗口大小>=mss或數據大小>=mss
          • 收到之前發送數據的ack應答
        • 滿足發送條件的兩點才會發送
        • 設置關閉

          • Nagel算法默認開啟,若想要關閉,需要在socket設置TCP_NODELAY來關閉。沒有全局參數,需要每個應用根據自己的特點來關閉
    

    Silly Window Syndrome翻譯成中文就是“糊塗窗口綜合症”。正如你上面看到的一樣,如果我們的接收方太忙了,來不及取走Receive Windows里的數據,那么,就會導致發送方越來越小。到最后,如果接收方騰出幾個字節並告訴發送方現在有幾個字節的window,而我們的發送方會義無反顧地發送這幾個字節。

要知道,我們的TCP+IP頭有40個字節,為了幾個字節,要達上這么大的開銷,這太不經濟了。

另外,你需要知道網絡上有個MTU,對於以太網來說,MTU是1500字節,除去TCP+IP頭的40個字節,真正的數據傳輸可以有1460,這就是所謂的MSS(Max Segment Size)注意,TCP的RFC定義這個MSS的默認值是536,這是因為 RFC 791里說了任何一個IP設備都得最少接收576尺寸的大小(實際上來說576是撥號的網絡的MTU,而576減去IP頭的20個字節就是536)。

如果你的網絡包可以塞滿MTU,那么你可以用滿整個帶寬,如果不能,那么你就會浪費帶寬。(大於MTU的包有兩種結局,一種是直接被丟了,另一種是會被重新分塊打包發送) 你可以想像成一個MTU就相當於一個飛機的最多可以裝的人,如果這飛機里滿載的話,帶寬最高,如果一個飛機只運一個人的話,無疑成本增加了,也而相當二。

所以,Silly Windows Syndrome這個現像就像是你本來可以坐200人的飛機里只做了一兩個人。 要解決這個問題也不難,就是避免對小的window size做出響應,直到有足夠大的window size再響應,這個思路可以同時實現在sender和receiver兩端。

  • 如果這個問題是由Receiver端引起的,那么就會使用 David D Clark’s 方案。在receiver端,如果收到的數據導致window size小於某個值,可以直接ack(0)回sender,這樣就把window給關閉了,也阻止了sender再發數據過來,等到receiver端處理了一些數據后windows size 大於等於了MSS,或者,receiver buffer有一半為空,就可以把window打開讓send 發送數據過來。
  • 如果這個問題是由Sender端引起的,那么就會使用著名的 Nagle’s algorithm。這個算法的思路也是延時處理,他有兩個主要的條件:1)要等到 Window Size>=MSS 或是 Data Size >=MSS,2)收到之前發送數據的ack回包,他才會發數據,否則就是在攢數據。

另外,Nagle算法默認是打開的,所以,對於一些需要小包場景的程序——比如像telnet或ssh這樣的交互性比較強的程序,你需要關閉這個算法。你可以在Socket設置TCP_NODELAY選項來關閉這個算法(關閉Nagle算法沒有全局參數,需要根據每個應用自己的特點來關閉)

 

setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value,sizeof(int));

 

另外,網上有些文章說TCP_CORK的socket option是也關閉Nagle算法,這不對。TCP_CORK其實是更新激進的Nagle算漢,完全禁止小包發送,而Nagle算法沒有禁止小包發送,只是禁止了大量的小包發送。最好不要兩個選項都設置。

 

https://coolshell.cn/articles/11609.html

http://zdyi.com/books/unp/s1/1.3.5.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM