0%

TCP的四大机制

文章 https://mp.weixin.qq.com/s?__biz=MzUxODAzNDg4NQ==&mid=2247484017&idx=1&sn=dc54d43bfd5dc088e48adcfa2e2bc13f 的学习笔记

重传机制

.image-20220317191648427

重传机制基于序列号

超时重传

RTO(Retransmission Timeout,超时重传时间),过大和过小都会出现问题👇

.image-20220317192319646

  • 一般RTO应该略大于RTT,由于RTT是动态变化的,所以RTO也应该是动态变化的

  • 对于单次数据而言,每当遇到一次超时重传时,都会把RTT翻倍,这是因为网络环境很差,不宜频繁发送

估算RTT的方法

真实的RTT变化较大,而我们希望RTT的变化更加平滑,所以使用系数$α$进行加权过渡
$$
T_{avg}=αT_{cur}+(1-α)T_{avg}
$$
.image-20220607194818657

快速重传

超时重传需要等待时间,比较慢,快速重传解决了这一问题

快速重传不以时间为驱动,而以数据为驱动👇

.image-20220317193525283

由于Seq2一直没有到,所以接收方会一直回ACK2

SACK

由于是三个相同的ACK,快速重传并不知道该重传哪个Seq(上图只是情况之一),

SACK(Selective Acknowledgement,选择性确认)解决了这一问题

  • ACK=现在需要的起始序列号,即ACK之前的数据都已收到
  • SACK只有在发生异常的时候才会出现,并且SACK=异常发生后的接收到的首尾序列号

按照以上的生成规则,可以很好地区分并处理以下三种情况,使得发送者只需要重传缺失的数据段:

  1. 数据丢包

.image-20220317202617908

  1. 网络延迟

.image-20220317204208101

  1. ACK丢包

.image-20220317201814991

滑动窗口

若收到ACK后才发送下一条数据,则通信效率会很低

于是可以使用窗口,意为消息队列的容量大小,其底层是OS内开辟的缓存空间

  • 发送方主机必须保留已发送的数据,收到应答后再从缓冲区中清除
  • 接收方同样需要在缓存空间内存储未处理的数据

.image-20220317205615516

图中窗口大小=3,即使ACK 600丢失,也可以通过ACK 700确认,叫做累计确认累计应答

通常窗口的大小通常是由接收方决定的,因为如果发送方的窗口大于接收方,接收方则有可能收不到数据

发送方的滑动窗口

.image-20220318085915726

.image-20220318085940740

.image-20220318085954774

四个不同的区域由两个指针维护👇

.image-20220318090303586

接收方的滑动窗口

.image-20220318090551888

流量控制

指TCP的发送方根据接收方的接收能力控制发送的数据量

流量控制是目的,滑动窗口是实现方式

.image-20220318092720267

丢包问题

但是如果接收方同时减小缓存区大小并且应用层不读取任何数据,可能会出现丢包现象👇

.image-20220318093349071

【解决方式】TCP不允许同时减小缓存并收缩窗口,而是先收缩窗口,过段时间再减小缓存(感觉还是没有彻底消除????)

死锁问题

如果告知窗口打开的ACK报文丢失,双方就会陷入死锁👇

.image-20220318094436630

【解决方式】窗口关闭后发送方启动计时器,超时后发送窗口探测(window probe)报文👇

.image-20220318094944082

窗口探测次数一般为3次,超过3次后如果窗口仍然关闭,有的TCP会让发送方发送RST报文中断连接

糊涂窗口综合征

如果窗口仅剩几个字节,发送方仍然会继续发送,但是TCP+IP头一共40字节,这样的开销显然很不经济,这就叫糊涂窗口综合征

就好像⼀个可以承载 50 人 的大巴车,每次只来了一两个⼈,就直接发车

解决方式有两种

  1. 接收方不通告小窗口

    通常的策略是,当”窗口大小<min(MSS,缓存空间/2)”时,ACK报文中的窗口大小直接设为0

  2. 发送方不发送小数据

    通常的策略是,发送方使用Nagle算法,原理是囤积数据,直到满足以下条件之一才发送

    • 窗口大小>=MSS
    • 数据大小>=MSS
    • 收到ACK

    此外,对于一些小数据包交互的场景(如telnet、ssh),需要把Nagle关闭

拥塞控制

有了流量控制,为何还要拥塞控制?

流量控制针对发送方和接收方,而拥塞控制针对网络拥堵

当网络拥堵时,若发送大量数据则易产生丢包和延迟,这会导致重传,而重传又加剧了网络的拥堵,所以需要拥塞控制

拥塞控制利用拥塞窗口(cwnd),主动减少发送的数据量,来避免发送方的数据填满整个网络

拥塞窗口是发送方维护的状态变量,根据网络的拥堵情况动态变化

发送窗口(swnd),接收窗口(rwnd),拥塞窗口(cwnd)三者的关系为
$$
rwnd=min(swnd,cwnd)
$$
拥塞控制主要有以下四大控制算法

慢启动

慢启动的意思就是TCP刚建立连接时,一点一点地扩大拥塞窗口,每当发送方每收到一个ACK,cwnd++

.image-20220319113942272

拥塞避免

当cwnd>=ssthresh(slow start threshold,慢启动阈值)时,就会启动拥塞避免算法,一般ssthresh=65535字节

每当发送方每收到一个ACK,cwnd+=1/cwnd,几乎是线性增长

.image-20220319114033006

拥塞发生

该状态只持续一瞬间

由于拥塞避免算法仍会使cwnd增长,一旦发生重传,就会启动拥塞发生算法,分为两种

超时重传
  1. ssthresh=cwnd/2

  2. wnd=1

  3. 进入慢启动

.image-20220319114602771

这种方式会使cwnd急剧下降,造成网络卡顿

快速重传

快速重传因为只丢了一小部分包,TCP认为这种情况不严重,所以采用以下算法

  1. cwnd/=2

  2. ssthresh=cwnd

  3. 进入快速恢复算法

快速恢复

  1. cwnd=ssthresh+3(意思是有3个重复的ACK收到了)
  2. 重传丢失的数据包
    • 如果再次收到重复的ACK,cwnd++
    • 如果收到了新的ACK,把 cwnd 设置为第⼀步中的 ssthresh 的值,原因是新的ACK说明从重复ACK时的数据都已收到,恢复过程已经结束,所以回到拥塞避免状态(???????)

.image-20220319121309050