計算機網絡-TCP的流量控制

內容來源:小林coding

本文是對小林coding的TPC流量控制的精簡總結


什么是流量控制

發送方不能無腦的發數據給接收方,要考慮接收方處理能力

如果一直無腦的發數據給對方,但對方處理不過來,那么就會導致觸發重發機制

從而導致網絡流量的無端的浪費

為了解決這種現象發生,TCP 提供一種機制可以讓「發送方」根據「接收方」的實際接收能力控制發送的數據量,這就是所謂的流量控制


操作系統緩沖區與滑動窗口的關系

前面的流量控制例子,我們假定了發送窗口和接收窗口是不變的

但是實際上,發送窗口和接收窗口中所存放的字節數,都是放在操作系統內存緩沖區中的

而操作系統的緩沖區,會被操作系統調整
當應用進程沒辦法及時讀取緩沖區的內容時,也會對我們的緩沖區造成影響


操作系統緩沖區是如何影響發送窗口和接收窗口的呢?

例子一:應用程序沒有及時讀取緩存

當應用程序沒有及時讀取緩存時,發送窗口和接收窗口的變化。

考慮以下場景:

  • 客戶端作為發送方,服務端作為接收方,發送窗口和接收窗口初始大小為 360;
  • 服務端非常的繁忙,當收到客戶端的數據時,應用層不能及時讀取數據

根據上圖的流量控制,說明下每個過程:

  1. 客戶端發送 140 字節數據后,可用窗口變為 220(360 - 140)。
  2. 服務端收到 140 字節數據,但是服務端非常繁忙,應用進程只讀取了 40 個字節,還有 100 字節占用著緩沖區,于是接收窗口收縮到了 260(360 - 100),最后發送確認信息時,將窗口大小通告給客戶端。
  3. 客戶端收到確認和窗口通告報文后,發送窗口減少為 260。
  4. 客戶端發送 180 字節數據,此時可用窗口減少到 80。
  5. 服務端收到 180 字節數據,但是應用程序沒有讀取任何數據,這 180 字節直接就留在了緩沖區,于是接收窗口收縮到了 80(260 - 180),并在發送確認信息時,通過窗口大小給客戶端。
  6. 客戶端收到確認和窗口通告報文后,發送窗口減少為 80。
  7. 客戶端發送 80 字節數據后,可用窗口耗盡。
  8. 服務端收到 80 字節數據,但是應用程序依然沒有讀取任何數據,這 80 字節留在了緩沖區,于是接收窗口收縮到了 0,并在發送確認信息時,通過窗口大小給客戶端。
  9. 客戶端收到確認和窗口通告報文后,發送窗口減少為 0。

簡單總結

1.應用進程沒有及時讀取部分數據,導致數據暫時留在了緩沖區

2.為了讓接收窗口能成功我們緩沖區中還沒讀取的數據,所以我們的接收窗口要變小,告訴發送端我們不能再接收大于這個量的數據了(接收大于這個量的數據我們會丟失)

3.然后我們把接收端改變的窗口大小發送給發送端,發送窗口大小減少

可見最后窗口都收縮為 0 了,也就是發生了窗口關閉。

當發送方可用窗口變為 0 時,發送方實際上會定時發送窗口探測報文,以便知道接收方的窗口是否發生了改變


例子二:系統資源緊張然后減少了接收緩沖區大小,導致數據包丟失

當服務端系統資源非常緊張的時候,操作系統可能會直接減少了接收緩沖區大小

這時應用程序又無法及時讀取緩存數據,那么這時候就有嚴重的事情發生了,會出現數據包丟失的現象

說明下每個過程:

  1. 客戶端發送 140 字節的數據,于是可用窗口減少到了 220。
  2. 服務端因為現在非常的繁忙,操作系統于是就把接收緩存減少了 120 字節,當收到 140 字節數據后,又因為應用程序沒有讀取任何數據,所以 140 字節留在了緩沖區中,于是接收窗口大小從 360 收縮成了 100,最后發送確認信息時,通告窗口大小給對方。
  3. 此時客戶端因為還沒有收到服務端的通告窗口報文,所以不知道此時接收窗口收縮成了 100,客戶端只會看自己的可用窗口還有 220,所以客戶端就發送了 180 字節數據,于是可用窗口減少到 40。
  4. 服務端收到了 180 字節數據時,發現數據大小超過了接收窗口的大小,于是就把數據包丟失了
  5. 客戶端收到第 2 步時,服務端發送的確認報文和通告窗口報文,嘗試減少發送窗口到 100,把窗口的右端向左收縮了 80,此時可用窗口的大小就會出現詭異的負值。

所以,如果發生了先減少緩存,再收縮窗口,就會出現丟包的現象

為了防止這種情況發生,TCP 規定是不允許同時減少緩存又收縮窗口的

而是采用先收縮窗口,過段時間再減少緩存

這樣就可以避免了丟包情況


窗口關閉

什么是窗口關閉

在前面我們都看到了,TCP 通過讓接收方指明希望從發送方接收的數據大小(窗口大小)來進行流量控制

如果窗口大小為 0 時,就會阻止發送方給接收方傳遞數據,直到窗口變為非 0 為止

這就是窗口關閉


窗口關閉的潛在危險(ACK報文丟失導致發送方一直等待非0窗口通知)

接收方向發送方通告窗口大小時,是通過 ACK 報文來通告的

那么,當發生窗口關閉時,接收方處理完數據后,會向發送方通告一個窗口非 0 的 ACK 報文

如果這個通告窗口的 ACK 報文在網絡中丟失了,那麻煩就大了

這會導致發送方一直等待接收方的非 0 窗口通知,接收方也一直等待發送方的數據

如不采取措施,這種相互等待的過程,會造成了死鎖的現象


TCP是如何解決窗口關閉時潛在的死鎖現象的(通過持續計數器)

為了解決這個問題,TCP 為每個連接設有一個持續定時器

只要 TCP 連接一方收到對方的零窗口通知,就啟動持續計時器。

如果持續計時器超時,就會發送窗口探測(Window probe)報文

而對方在確認這個探測報文時,給出自己現在的接收窗口大小

  • 如果接收窗口仍然為 0,那么收到這個報文的一方就會重新啟動持續計時器
  • 如果接收窗口不是 0,那么死鎖的局面就可以被打破了

窗口探測的次數一般為 3 次,每次大約 30 - 60 秒(不同的實現可能會不一樣)

如果 3 次過后接收窗口還是 0 的話,有的 TCP 實現就會發 RST 報文來中斷連接


糊涂窗口綜合癥

什么是糊涂窗口綜合癥

如果接收方太忙來不及取走接收窗口里的數據,那么就會導致發送方的發送窗口越來越小

到最后,如果接收方騰出幾個字節并告訴發送方現在有幾個字節的窗口

而發送方會義無反顧地發送這幾個字節

這就是糊涂窗口綜合癥(接收方一有空間,發送方就會拼盡全力發送去爭奪接收方那一點空間)


糊涂窗口綜合癥帶來的問題

要知道,我們的 TCP + IP 頭有 40 個字節,為了傳輸那幾個字節的數據,要搭上這么大的開銷,這太不經濟了

就好像一個可以承載 50 人的大巴車,每次來了一兩個人,就直接發車

除非家里有礦的大巴司機,才敢這樣玩,不然遲早破產

要解決這個問題也不難,大巴司機等乘客數量超過了 25 個,才認定可以發車


糊涂窗口綜合癥的場景例子

現舉個糊涂窗口綜合癥的栗子,考慮以下場景:

接收方的窗口大小是 360 字節,但接收方由于某些原因陷入困境,假設接收方的應用層讀取的能力如下:

  • 接收方每接收 3 個字節,應用程序就只能從緩沖區中讀取 1 個字節的數據(每次只能讀三分之一)
  • 在下一個發送方的 TCP 段到達之前,應用程序還從緩沖區中讀取了 40 個額外的字節;

每個過程的窗口大小的變化,在圖中都描述的很清楚了,可以發現窗口不斷減少了,并且發送的數據都是比較小的了。

總結:也就是我們的窗口不斷減小,導致我們能發送的數據都在不斷減小,用更多的請求發更少的數據造成了資源浪費

所以,糊涂窗口綜合癥的現象是可以發生在發送方和接收方:

  • 接收方可以通告一個小的窗口
  • 而發送方可以發送小數據

如何解決糊涂窗口綜合癥

要解決糊涂窗口綜合癥,就要同時解決上面兩個問題:

  • 讓接收方不通告小窗口給發送方
  • 讓發送方避免發送小數據

MSS(Maximum Segment Size)最大報文段長度,指的是 TCP 報文段中數據部分的最大長度。它決定了每次傳輸的數據塊大小,與網絡傳輸效率相關。

緩存空間:接收方用于存儲接收到但尚未被應用程序讀取的數據的內存空間


如何讓接收方不通告小窗口

接收方通常的策略如下:
當「窗口大小」小于 min (MSS,緩存空間 / 2),也就是小于 MSS 與 1/2 緩存大小中的最小值時會向發送方通告窗口為 0,也就阻止了發送方再發數據過來。
?

等到接收方處理了一些數據后,窗口大小 >=MSS,或者接收方緩存空間有一半可以使用 就可以把窗口打開讓發送方發送數據過來


如何讓發送方避免發送小數據

發送方通常的策略如下:
使用 Nagle 算法,該算法的思路是延時處理,只有滿足下面兩個條件中的任意一個條件,才可以發送數據:

  • 條件一:要等到窗口大小 >= MSS 并且數據大小 >= MSS
  • 條件二:收到之前發送數據的 ack 回包

只要上面兩個條件都不滿足,發送方一直在囤積數據,直到滿足上面的發送條件

Nagle 偽代碼如下:

if 有數據要發送 {if 可用窗口大小 >= MSS and 可發送的數據 >= MSS {立刻發送MSS大小的數據} else {if 有未確認的數據 {將數據放入緩存等待接收ACK} else {立刻發送數據}}
}

PS:如果接收方不能滿足「不通告小窗口給發送方」,那么即使開了 Nagle 算法,也無法避免糊涂窗口綜合癥

因為如果對端 ACK 回復很快的話(達到 Nagle 算法的條件二),Nagle 算法就不會拼接太多的數據包,這種情況下依然會有小數據包的傳輸,網絡總體的利用率依然很低。

所以,接收方得滿足「不通告小窗口給發送方」+ 發送方開啟 Nagle 算法,才能避免糊涂窗口綜合癥

另外,Nagle 算法默認是打開的,如果對于一些需要小數據包交互的場景的程序,比如,telnet 或 ssh 這樣的交互性比較強的程序,則需要關閉 Nagle 算法。

可以在 Socket 設置 TCP_NODELAY 選項來關閉這個算法(關閉 Nagle 算法沒有全局參數,需要根據每個應用自己的特點來關閉)

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

簡單總結一下流量控制

什么是流量控制

發送方不能無腦的發數據給接收方,要考慮接收方處理能力

不然就會導致觸發重發機制造成網絡流量的無端浪費


發送窗口和接收窗口中所存放的字節數,都是放在操作系統內存緩沖區中的,所以說和緩沖區大小有關

操作系統緩沖區影響發送窗口和接收窗口(未及時讀取or資源緊張導致窗口收縮)

應用程序沒有及時讀取緩存

因為應用層沒有及時讀取數據導致我們的接收窗口的量不斷減少

因為接收窗口不斷減少,導致我們的發送窗口量不斷減少

系統資源緊張然后減少了接收緩沖區大小,導致數據包丟失

如果發生了先減少緩存,再收縮窗口,就會出現丟包的現象

為了防止這種情況發生,TCP 規定是不允許同時減少緩存又收縮窗口的

而是采用先收縮窗口,過段時間再減少緩存

這樣就可以避免了丟包情況


窗口關閉

什么是窗口關閉

如果窗口大小為 0 時,就會阻止發送方給接收方傳遞數據,直到窗口變為非 0 為止

窗口關閉的危險

當發生窗口關閉時,接收方處理完數據后,會向發送方通告一個窗口非 0 的 ACK 報文,如果這個通告窗口的 ACK 報文在網絡中丟失了,這會導致發送方一直等待接收方的非 0 窗口通知,接收方也一直等待發送方的數據

如不采取措施,這種相互等待的過程,會造成了死鎖的現象

TCP是如何解決窗口關閉時潛在的死鎖現象的(通過持續計數器)

TCP 為每個連接設有一個持續定時器

只要 TCP 連接一方收到對方的零窗口通知,就啟動持續計時器

如果持續計時器超時,就會發送窗口探測(Window probe)報文

  • 如果接收窗口仍然為 0,那么收到這個報文的一方就會重新啟動持續計時器
  • 如果接收窗口不是 0,那么死鎖的局面就可以被打破了

窗口探測的次數一般為 3 次,每次大約 30 - 60 秒(不同的實現可能會不一樣)

如果 3 次過后接收窗口還是 0 的話,有的 TCP 實現就會發 RST 報文來中斷連接


窗口糊涂綜合癥

什么是窗口糊涂綜合征

(接收方一有空間,發送方就會拼盡全力發送去爭奪接收方那一點空間)

窗口糊涂綜合征帶來的問題

太浪費了,那么點空間我們還拼盡全力去發送還發送不完全

一個可以承載 50 人的大巴車,每次來了一兩個人,就直接發車

除非家里有礦的大巴司機,才敢這樣玩,不然遲早破產

要解決這個問題也不難,大巴司機等乘客數量超過了 25 個,才認定可以發車

糊涂窗口綜合癥是怎么發生的

糊涂窗口綜合癥的現象是可以發生在發送方和接收方:

  • 接收方可以通告一個小的窗口
  • 而發送方可以發送小數據

如何解決糊涂窗口綜合征

兩個問題,兩個概念

我們要解決兩個問題:

1.讓接收方不通告小窗口給發送方

2.讓發送方避免發送小數據

兩個概念

1.MSS(Maximum Segment Size)最大報文段長度,指的是 TCP 報文段中數據部分的最大長度。它決定了每次傳輸的數據塊大小,與網絡傳輸效率相關

2.緩存空間:接收方用于存儲接收到但尚未被應用程序讀取的數據的內存空間


如何讓接收方不通告小窗口

當「窗口大小」小于 min (MSS,緩存空間 / 2),也就是小于 MSS 與 1/2 緩存大小中的最小值時會向發送方通告窗口為 0,也就阻止了發送方再發數據過來。
?

等到接收方處理了一些數據后,窗口大小 >=MSS,或者接收方緩存空間有一半可以使用 就可以把窗口打開讓發送方發送數據過來


如何讓發送方避免發送小數據

使用 Nagle 算法,該算法的思路是延時處理,只有滿足下面兩個條件中的任意一個條件,才可以發送數據:

  • 條件一:要等到窗口大小 >= MSS 并且數據大小 >= MSS
  • 條件二:收到之前發送數據的 ack 回包

PS:如果接收方不能滿足「不通告小窗口給發送方」,那么即使開了 Nagle 算法,也無法避免糊涂窗口綜合癥

因為如果對端 ACK 回復很快的話(達到 Nagle 算法的條件二),Nagle 算法就不會拼接太多的數據包,這種情況下依然會有小數據包的傳輸,網絡總體的利用率依然很低。

所以,接收方得滿足「不通告小窗口給發送方」+ 發送方開啟 Nagle 算法,才能避免糊涂窗口綜合癥

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76633.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76633.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76633.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Boot 七種事務傳播行為只有 REQUIRES_NEW 和 NESTED 支持部分回滾的分析

Spring Boot 七種事務傳播行為支持部分回滾的分析 支持部分回滾的傳播行為 REQUIRES_NEW:始終開啟新事務,獨立于外部事務,失敗時僅自身回滾。NESTED:在當前事務中創建保存點(Savepoint),可局部…

突破反爬困境:SDK開發,瀏覽器模塊(七)

聲明 本文所討論的內容及技術均純屬學術交流與技術研究目的,旨在探討和總結互聯網數據流動、前后端技術架構及安全防御中的技術演進。文中提及的各類技術手段和策略均僅供技術人員在合法與合規的前提下進行研究、學習與防御測試之用。 作者不支持亦不鼓勵任何未經授…

C++數據排序( 附源碼 )

一.冒泡排序 原理:自左向右依次遍歷,若相鄰兩數順序錯誤,則交換兩數. 這樣,每一輪結束后,最大/最小的數就會到最后. Code: #include <iostream> #include <cstdio> using namespace std; const int N1e51; int n,a[N],in; void PrintArray(int a[],int n){for…

I2C 讀寫 AT24C02

根據AT24C02的 Datasheet 可知AT24C02有2K bit&#xff0c;即256B&#xff0c;分為32頁,每頁8個字節&#xff0c;結合數據手冊和原理圖可以得知&#xff0c;板載AT24C02的讀地址為0xA2&#xff0c;寫地址為0xA3&#xff1a; #define AT24C02_ADDR_WRITE 0xA2 #define AT24C02_…

K8S學習之基礎七十四:部署在線書店bookinfo

部署在線書店bookinfo 在線書店-bookinfo 該應用由四個單獨的微服務構成&#xff0c;這個應用模仿在線書店的一個分類&#xff0c;顯示一本書的信息&#xff0c;頁面上會顯示一本書的描述&#xff0c;書籍的細節&#xff08;ISBN、頁數等&#xff09;&#xff0c;以及關于這本…

Linux 查找文本中控制字符所在的行

參考資料 ASCIIコード表 目錄 一. 業務背景二. 遇到的問題三. 分析3.1 url編碼的前置知識3.2 出現控制字符的transactionid分析3.3 16進制分析 四. 從文本中查找控制字符所在的行五. 控制字符一覽 一. 業務背景 ?在項目中&#xff0c;業務請求對應著下URL http://www.test.…

python將pdf文件轉為圖片,如果pdf文件包含多頁,將轉化的多個圖片通過垂直或者水平合并成一張圖片

要將PDF文件轉換為圖片&#xff0c;并將多頁PDF垂直合并成一張圖片&#xff0c;可以使用PyMuPDF&#xff08;也稱為fitz&#xff09;庫來讀取PDF文件&#xff0c;并使用Pillow庫來處理和合并圖片。以下是一個示例代碼&#xff0c;展示了如何實現這個功能&#xff1a; 首先&…

HarmonyOS 基礎組件和基礎布局的介紹

1. HarmonyOS 基礎組件 1.1 Text 文本組件 Text(this.message)//文本內容.width(200).height(50).margin({ top: 20, left: 20 }).fontSize(30)//字體大小.maxLines(1)// 最大行數.textOverflow({ overflow: TextOverflow.Ellipsis })// 超出顯示....fontColor(Color.Black).…

FrameWork基礎案例解析(四)

文章目錄 單獨拉取framework開機與開機動畫橫屏Android.mk語法單獨編譯SDKmake 忽略warning單獨修改和編譯Camera2單獨編譯Launcher3Android Studio 導入、修改、編譯Settings導入 Android Studio 導入、修改、編譯Launcher3android 開機默認進入指定Launcher植入自己的apk到系…

基于vscode(GDB)調試ros2節點

一、環境準備 必備vscode插件 1&#xff09;Docker Docker - Visual Studio Marketplace 2&#xff09;Dev Containers Dev Containers - Visual Studio Marketplace 3&#xff09;GDB GDB Debug - Visual Studio Marketplace 二、進去docker鏡像 1&#xff09;docker安…

基于springboot的考研成績查詢系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 這些年隨著Internet的迅速發展&#xff0c;我們國家和世界都已經進入了互聯網大數據時代&#xff0c;計算機網絡已經成為了整個社會以及經濟發展的巨大動能&#xff0c;考研成績查詢管理事務現在已經成為社會關注的重要內容&#xff0c;因此運用互聯網技術來提高考研成績…

C++:算術運算符

程序員Amin &#x1f648;作者簡介&#xff1a;練習時長兩年半&#xff0c;全棧up主 &#x1f649;個人主頁&#xff1a;程序員Amin &#x1f64a; P? ?S : 點贊是免費的&#xff0c;卻可以讓寫博客的作者開心好久好久&#x1f60e; &#x1f4da;系列專欄&#xff1a;Java全…

PyQt6實例_A股日數據維護工具_使用

目錄 前置&#xff1a; 下載預備更新的數據 使用工具更新 用工具下載未復權、前復權、權息數據 在PostgreSQL添加兩個數據表 工具&視頻 前置&#xff1a; 1 本系列將以 “PyQt6實例_A股日數據維護工具” 開頭放置在“PyQt6實例”專欄 2 日數據可在“數據庫”專欄&…

REST 方法

FUNCTION ZFM_INTERFACE_LOG. *"---------------------------------------------------------------------- *"*"本地接口&#xff1a; *" IMPORTING *" REFERENCE(IV_DSTART) TYPE EDI_UPDDAT *"---------------------------------------…

QT 中的元對象系統(五):QMetaObject::invokeMethod的使用和實現原理

目錄 1.簡介 2.原理概述 3.實現分析 3.1.通過方法名調用方法的實現分析 3.2.通過可調用對象調用方法的實現分析 4.使用場景 5.總結 1.簡介 QMetaObject::invokeMethod 是 Qt 框架中的一個靜態方法&#xff0c;用于在運行時調用對象的成員函數。這個方法提供了一種動態調…

Unity3D開發AI桌面精靈/寵物系列 【三】 語音識別 ASR 技術、語音轉文本多平臺 - 支持科大訊飛、百度等 C# 開發

Unity3D 交互式AI桌面寵物開發系列【三】ASR 語音識別 該系列主要介紹怎么制作AI桌面寵物的流程&#xff0c;我會從項目開始創建初期到最終可以和AI寵物進行交互為止&#xff0c;項目已經開發完成&#xff0c;我會仔細梳理一下流程&#xff0c;分步講解。 這篇文章主要講有關于…

Java 狀態模式 詳解

狀態模式詳解 一、狀態模式概述 狀態模式(State Pattern)是一種行為型設計模式&#xff0c;它允許一個對象在其內部狀態改變時改變它的行為&#xff0c;使對象看起來似乎修改了它的類。 核心特點 狀態封裝&#xff1a;將每個狀態的行為封裝到獨立的類中狀態轉換&#xff1a…

Nginx 配置 HTTPS 與 WSS 完整指南

Nginx 配置 HTTPS 與 WSS 完整指南 本教程將手把手教你如何為網站配置 HTTPS 加密訪問&#xff0c;并通過反向代理實現安全的 WebSocket&#xff08;WSS&#xff09;通信。以 https://www.zhegepai.cn 域名為例&#xff0c;完整流程約需 30 分鐘完成。 一、前置準備 1.1 域名…

雙向鏈表的理解

背景 代碼中經常會出現雙向鏈表&#xff0c;對于雙向鏈表的插入和刪除有對應的API函數接口&#xff0c;但直觀的圖表更容易理解&#xff0c;所以本文會對rt-thread內核代碼中提供的雙向鏈表的一些API函數操作進行繪圖&#xff0c;方便后續隨時查看。 代碼塊 rt-thread中提供…

大文件上傳源碼,支持單個大文件與多個大文件

大文件上傳源碼&#xff0c;支持單個大文件與多個大文件 Ⅰ 思路Ⅱ 具體代碼前端--單個大文件前端--多個大文件前端接口后端 Ⅰ 思路 具體思路請參考我之前的文章&#xff0c;這里分享的是上傳流程與源碼 https://blog.csdn.net/sugerfle/article/details/130829022 Ⅱ 具體代碼…