网络协议(五) -- 传输限制

一:前情概述

下载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

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