網絡協議(五) -- 傳輸限制

一:前情概述

下載100M資源數據傳輸的過程一帆風順麼?100M的數據瞬間寫入?前面介紹TCP首部的時候講到序列號就提到其核心作用爲數據接收端將拆分的數據包進行重新組裝。也就是說一個完整的應用層數據包會在傳輸過程中經歷拆分的過程。同時,還有一個問題值得思考的是如果接收端是一個小霸王,內存和處理能力都有限,那麼數據發送端還要一直不停的發送數據?綜上,引出本文兩個核心的議題:

  • 數據包傳輸過程中的拆包處理
  • 數據包傳輸數量與速度的控制

二:最大傳輸單元 MTU

應用層的數據終歸是要交給鏈路層進行傳輸的,鏈路層每次傳輸都是幀爲單位的數據。以太網中一幀數據最小是64字節,最大是1518字節,減去14字節頭部與4字節CRC,最終有效字節數量範圍爲46 ~ 1500字節。查看主機的幀大小可以使用命令netstat -i,如下所示:
在這裏插入圖片描述
數據傳輸過程是一個鏈路,也就是數據包可能不是直接由發送方就傳輸到了接收方,中間會經歷多次轉發。這就涉及到了著名的木桶效應,最後傳輸數據大小取決於MTU最小的一個環節
在這裏插入圖片描述

三:網絡層IP分片

MTU是鏈路層數據傳輸的限制,也就是針對網絡層IP協議的,這個操作對於IP協議來講就是分片。例如傳輸層採用UDP協議時傳輸到IP協議的數據包大小就可能超過MTU限制,這時候就需要IP協議對數據包進行分片符合MTU限制。通過IP協議頭部可以看到如下效果:
在這裏插入圖片描述
紅框中第一個字段More fragments表示是否還具備其它分片,Fragment offset表示數據包偏移量,也就是分片大小

四:傳輸層TCP分段與MSS

首先想一下IP協議是一個不可靠的傢伙,它將數據包進行分片傳輸後如果有一個分片丟了,傳輸層是TCP協議就會進行數據重傳,但是TCP傳輸層會將該分片的數據包全部重傳!所以,傳輸層TCP協議有自己的限制那就是MSS(Max Segments Size)

 MSS = MTU - TCP頭部(20) - IP頭部(20)

在這裏插入圖片描述
總結起來就是傳輸層TCP爲了避免IP對自己的數據包進行分片,將數據包交給IP協議時會根據MSS對數據包進行分段

五:滑動窗口

數據包已經按照要求被完美切割,然後就可以肆無忌憚的開始傳輸了?小霸王只能接收10K數據難道你要包藏禍心的給我傳10M?明顯這個也是需要被限制的,當TCP進行三次握手的時候都會交換初始限制值大小,並且隨着數據傳輸與確認過程的發展限制值會動態發生變化,這個實現稱之爲滑動窗苦。其初始值通過Wireshark抓包三次握手過程可以看到,如下所示:win表示自己的窗口大小,告訴連接對方只能給我這麼多數據
在這裏插入圖片描述

5.1 接收窗口

接收窗口就是上面說的窗口大小,表示可以接受的最大數據量。其動態收縮的過程如下所示:
在這裏插入圖片描述
接收窗口大小 = 已經發送但是未被接收方ACK的數據包大小 + 目前還可以發送數據包大小,隨着數據包發送與ACK的進程,窗口會動態變化。當窗口全部變化爲未ACK時發送方會等待接收方ACK後再進行數據發送,當發送方收到接收方ACK時會恢復可以發送數據包大小!

5.2 TCP Window Full

使用packetdrill模擬,腳本如下:Linux中使用tcpdump抓包並使用wireshark進行分析

--tolerance_usecs=100000
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0

// 三次握手告訴客戶端告訴服務器自己的接收窗口大小爲 4000
+0  < S 0:0(0) win 4000 <mss 1000>
+0  > S. 0:0(0) ack 1 <...>
+.1 < . 1:1(0) ack 1 win 4000
+0  accept(3, ..., ...) = 4

// 寫客戶端寫 5000 字節數據
+0  write(4, ..., 5000) = 5000

+0 `sleep 1000000`

在這裏插入圖片描述
如上圖所示,三次握手時交互Win大小爲4000,當發送到4000時就會提示發送端Tcp Window Full,意思窗口滿了別發了。然後後續就是系列重試,這個重試過程次數爲16次,每次重試時間級跳,1s - 2s - 4s - 8s …

5.3 TCP Zero Window

TCP Zero Window與TCP Window Full是相對的,當發送端發送數據量達到接收窗口大小時最後一條數據發送包就會顯示TCP Window Full,這是針對發送端的

--tolerance_usecs=100000
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0

+0  < S 0:0(0) win 4000 <mss 1000>
+0  > S. 0:0(0) ack 1 <...>
// 三次握手確定客戶端接收窗口大小爲 360
+.1 < . 1:1(0) ack 1 win 360
+0  accept(3, ..., ...) = 4

// 第一步:往客戶端(接收端)寫 140 字節數據
+0  write(4, ..., 140) = 140
// 第二步:模擬客戶端回覆 ACK,接收端滑動窗口減小爲 260
+.01 < . 1:1(0) ack 141 win 260
// 第四步:服務端(發送端)接續發送 180 字節數據給客戶端(接收端)
+0  write(4, ..., 180) = 180
// 第五步:模擬客戶端回覆 ACK,接收端滑動窗口減小到 80
+.01 < . 1:1(0) ack 321 win 80
// 第七步:服務端(發送端)繼續發送 80 字節給客戶端(接收端)
+0  write(4, ..., 80) = 80
// 第八步:模擬客戶端回覆 ACK,接收端滑動窗口減小到 0
+.01 < . 1:1(0) ack 401 win 0

+0 `sleep 1000000`

在這裏插入圖片描述

六:擁塞控制

滑動窗口解決了數據接收方處理能力不足導致的問題,限制發送方數據傳輸數量。但是還有一個致命的問題就是網絡波動延遲,若目前網絡波動延遲導致數據傳輸異常緩慢,數據發送方我行我素的發送數據加塞網絡通道,這也是災難性的後果,可能導致網絡風暴式的服務癱瘓。上面這段話不理解示意圖如下:
在這裏插入圖片描述
首先闡明一點:

數據發送方發送數據量受到發送窗口大小限制
發送窗口大小 = MIN(數據接收方接收窗口 , 擁塞窗口)
6.1 慢啓動 Slow Start

當數據發送開始時數據發送方上來就猛烈的灌數據?暴風雨來的更猛烈些麼?答案顯然是不行的,萬一一上來路就堵了後續怎麼辦?慢啓動的概念就是試探性數據傳輸,隨着數據完美的ACK擴大擁塞窗口調整傳輸數據量。這些細節個人不喜歡去深究,所以慢啓動規則如下:

初始值發送10段數據,隨着每次RTT(發送 - 響應確認)進行指數翻倍
6.2 慢啓動閾值 Slow Start Threshold

慢啓動初始值爲10,每經過一次RTT後指數翻倍。即10 - 20 - 40 - 80 … ,無止境的增長下去肯定不可能,這種指數翻倍的限制就是慢啓動閾值

6.3 擁塞控制 Congestion Avoidance

到達慢啓動閾值後總不能不增長髮送數據段量總不能不增長了吧?即閾值限制後的增長由指數增髒變爲線性增長,每次RTT後數值 + 1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章