- tcp 的重传
- 解决了什么问题
- tcp的几种重传机制分别解决什么问题?
- 方案 1: 超时重传
- 方案2: 快速重传
- 选择性确认(sack)
- d-sack(重复接收)
- 滑动窗口:
- 累计应答
- 流量控制
- 解决什么问题?
- 如何做的?
- 问题1: 那如果第一次发送的数据都大于缓冲区的大小怎么办?
- 问题2: 如果剩余大小为0会发生什么?
- 问题: 如果返回的ack丢失了会发生什么?
- 如何解决死锁的问题?
- 问题3: tcp 的报文头有 40 字节,如果收到的窗口大小是 1 字节会发生什么?
- 如何解决?
- 拥塞控制
- 解决什么问题?
- 如何做?
- 慢启动状态(试探网络压力的上限)
- 阻塞避免(超过门限)
- 阻塞发生
tcp 的重传
解决了什么问题
tcp 作为可靠的传输层协议,要确保每一个包都能被接收端收到.
如果有数据包在传输中丢失了怎么办?
如果有数据包丢失就需要重传数据包.
但是如何判断一个数据包是否丢失了呢?
tcp的几种重传机制分别解决什么问题?
如何设计一个重传机制?
方案 1: 超时重传
- 每传输一个包等待接收方返回 ack.
- 设置一个超时时间,超过超时时间还没有收到返回的,就任务丢包了要重传
问题1: 效率太慢了,每次都要等接收方返回 ack 才传输下一个
问题2: 超时时间太长与太短的问题
太长: 等待的时间就,传输效率低
太短: 太容易过期,包的重传率上升,无端消耗网络带宽
所以超时时间是一个动态的值,与网络的延时有关
方案2: 快速重传
要解决的问题: 超时重传的效率低,可以在超时重传的基础上进行升级:
升级:
接收方每次收到数据返回 ack 的时候带上下一个的编号
发送方如果连续收到 3 次相同的 编号,则判断为改数据丢包
就需要重传该数据
优点: 不需要等到每个包收到 ack 再传输下一个,提高了网络传输的效率
问题: 不知道具体哪些数据丢包了,也就是不知道传一个包还是传后面所有的包
传一个包: 假设发送了 1,2,3,4,5,6,7 包, 1 包接受到了,2,3,4 都没有收到,那么一个个重传就需要重传 3 次,效率太低
传后面所有的包:假设发送了 1,2,3,4,5,6,7 包, 1 包接受到了,只有2没有收到,那么 3,4,5,6,7 包就重复传输了,增加了网络的压力
选择性确认(sack)
要解决的问题: 快速恢复中到底传哪些包的问题(哪些包丢失的问题)
升级:
我们在 ack 的后面再加上一个已经接受到的包的编号(sack),这样发送方接收到ack 信息就知道哪些包已经收到了哪些包没有收到,就可以针对没有收到的包重传,不需要重传所有,与一个个重传
判断是否要重传的原理与快速重传相同
问题: 有的包可能并没有丢失,而是因为网络延时阻塞了,当我们重传之后,接收方又收到了网络中阻塞的包,就会收到重复的数据
d-sack(重复接收)
解决的问题: 哪些包是因为网络延时的问题重传(重复接受)/哪些包被复制了.
小于 ack 编号的 sack 就是重复接收的包,这些包是由于网络延迟太高/包在网络中被复制了导致的重复接收.
滑动窗口:
滑动窗口简单理解就是为了解决每一个包传输要接受到 ack 后再传下一个的效率太慢的问题,如果中途的 ack 丢包了,岂不是要重传,导致性能浪费.
基本原理:
发送方与接收方都有一个缓存区(窗口)
发送方的缓冲区: 存放已发送但没有收到 ack 的数据
接收方的缓冲区: 存放准备接受的数据(没收到)
这样就可以同时发送多个包了
累计应答
假设发送了 1,2,3,4,5,6,7 都 包, 前面 6 个包都没有收到,但是后面有一个 ack 7 证明包 1~6 都已经收到了,只是前面的 ack 丢包了. 就不需要重传了
解决的问题: ack 丢包重传的性能浪费.
流量控制
解决什么问题?
- 解决接收端对包的处理能力有限,超过限度会丢包重传,加大网络压力的问题
流量控制: 就是让**「发送方」根据「接收方」的实际接收能力控制发送的数据量**
如何做的?
接收端有一个缓冲区,发送端发送的数据先会读到缓冲区中,然后再做处理
逻辑:
每次接受端发送 ack的时候都带上缓冲区的剩余大小,那么发送端读取到剩余大小,发送数据的数量就不会超过这个大小
问题1: 那如果第一次发送的数据都大于缓冲区的大小怎么办?
拥塞控制原理中数据传输开始是慢启动的,只发送 1mss 的大小,所以不会超
问题2: 如果剩余大小为0会发生什么?
按照上面的逻辑,如果剩余大小为 0,那么就不会发送数据,而是等接收方处理数据然后返回一个窗口大于 0 的 ack,这样就可以继续传输数据了
问题: 如果返回的ack丢失了会发生什么?
发送方不知道接收方可以发送数据了,而接受方也不知道发送方没收到 ack,就 造成了一种死锁的状态,没有数据和传输,卡死了.
如何解决死锁的问题?
当发送方收到的ack 的窗口大小为 0 时:
每过一段时间(启动计时器)问一下(发送探测报文)发送端是否可以接受数据了,而不是坐以待毙.
问题3: tcp 的报文头有 40 字节,如果收到的窗口大小是 1 字节会发生什么?
小窗口问题(糊涂窗口综合症)
当窗口较小时数据的有效传输就会非常低(当只有 41 字节的窗口,一次发送有效传输的数据只有一字节)这好比一列高铁只载了1 个人.并且接收端还要解析报文头,报文越多,效率越低.
如何解决?
-
小窗口时先返回 ack 的窗口为0,等窗口变大(当「窗口大小」小于 min( MSS,缓存空间/2),也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。)了再返回大的窗口
-
发送方保证可以发送的数据>=mss,如果小于则等待(接收方处理数据并返回 ask )大于的时候再发送.
拥塞控制
解决什么问题?
流量控制只是根据接收方的能力决定发送数据的速度,但是网络带宽是有限的,如果发送的数据超过了带宽的上限,就会出现大量的丢包与重传,而重传又会增加额外的网络负担,导致更多的丢包
所以当网络压力大时需要发送方自动的降低数据发送的效率,避免丢包重传导致的网络压力激增.
如何做?
核心思想是如何判断当前的网络压力状况?
判断的依据是丢包与超时(如果路由器的发送队列满了(压力大)再收到包就会丢掉)
- 丢包是收到ack 中有缺少的包
- 超时是收不到返回的 ack
慢启动状态(试探网络压力的上限)
-
启动时的状态,先从 1mss 开始发送,指数级递增(1,2,4,8…)
-
问题: 网络带宽是有限的,如果一直不断的增加一定会讲网络打爆
-
解决方案: 设置门限,当超过门限就不再指数级递增
-
问题: 门限是多少,由什么决定?
-
一般来说大小是 65535 字节。
阻塞避免(超过门限)
-
超过门限增加的速度就会变慢
-
问题:多慢?
-
每当收到一个 ACK 时,cwnd 增加 1/cwnd.
阻塞发生
需要判断当前网络情况:
-
丢包: 重复收到三个相同的 ack 表示有丢包的情况(阻塞不严重)
- 快速恢复(重传丢的包)
- 设置新门限=当前速度的一半
- 减速:速度降为当前速度的一半
- 进入阻塞避免阶段
-
超时: 超过过期时间没有收到 ack (阻塞很严重,已经收不到数据了)
- 减速: 将速度降到1mss, 进入慢启动阶段
- 重传超时的包.
参考:
https://www…com/network/3_tcp/tcp_feature.html#%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3
https://zhuanlan.zhihu.com/p/37379780
https://juanha.github.io/2018/05/05/tcp/