小林coding-TCP详解
客户端 == 发送端
服务端 == 接收端
超时重传
①在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,也就是我们常说的超时重传。
②超时重传时间 RTO(Retransmission Timeout 超时重传时间) 的值应该略大于报文往返 RTT(Round Trip Time) 的值。
③评估RTO的值,要对RTT进行加权平均,还要对RTT进行波动范围采样。重传时间是不断变化的,这个过程称为自适应重传算法(Adaptive Retransmission Algorithm)。
快速重传
①当服务端收到的报文段序号大于下一个期望的报文段序号时,就检测数据流中的一个间隔,然后发送3个冗余的ACK。客户端收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
②存在一个问题:重传的时候,是重传之前的一个,还是重传所有的问题。
SACK( Selective Acknowledgment 选择性确认)
①这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将缓存的地图发送给发送方,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。
D-SACK (Duplicate SACK)
①使用了 SACK 来告诉「客户端」有哪些数据被重复接收了。
②可以让「客户端」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了,还是数据包被网络延迟了; 可以知道网络中是不是把「发送方」的数据包给复制了;
累计确认
接收方发送ACK说某个ID之前的包都收到了,这种模式称为累计确认或者累计应答(Cumulative Acknowledgment)。
发送端滑动窗口
①TCP 头里有一个字段叫 Window,也就是窗口大小。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。
②通常窗口的大小是由接收方的窗口大小来决定的。
接收端滑动窗口
①接收窗口的大小是约等于发送窗口的大小的。因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。
为什么要流量控制?
①如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。
②TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
操作系统缓冲区与滑动窗口的关系
①发送窗口和接收窗口中所存放的字节数,都是放在操作系统内存缓冲区中的,而操作系统的缓冲区,会被操作系统调整。
②TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间再减少缓存,这样就可以避免了丢包情况。
窗口关闭/死锁
接收方向发送方通告窗口大小时,是通过 ACK 报文来通告的。
当发生窗口关闭时,接收方处理完数据后,会向发送方通告一个窗口非 0 的 ACK 报文,如果这个通告窗口的 ACK 报文在网络中丢失了,会发生死锁。
死锁解决方案:
TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。
如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。
窗口探测的次数一般为 3 次,每次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接。
糊涂窗口综合症
TCP + IP 头有 40 个字节,如果窗口很小,此时传输数据是得不偿失的。
要解决糊涂窗口综合症,需要解决①让接收方不通告小窗口给发送方 ②让发送方避免发送小数据
解决方法见原文。
为什么要进行拥塞控制,不是有流量控制吗?
流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么。
在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大…
拥塞窗口 cwnd(congestion window)
cwnd是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。
发送窗口的值是swnd = min(cwnd, rwnd)
TCP主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。
①慢启动:当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1——指数增长。 举例:cwnd=2,发送2个,收到2个ACK,cwnd += 2 = 4。
②有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。
当 cwnd < ssthresh 时,使用慢启动算法。
当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」。
拥塞避免:每当收到一个 ACK 时,cwnd 增加 1/cwnd——线性增长。 举例:cwnd = 16,发送16个,得到16个ACK,cwnd += 16 * (1/16) = 17。
③当拥塞发生,如果发生超时重传,ssthresh 设为 cwnd/2,cwnd 重置为 1。重新变成慢启动,造成网络卡顿。
快速重传算法:发送三次前一个包的 ACK,发送端就会快速地重传。cwnd = cwnd/2,ssthresh = cwnd。
④快速恢复算法:快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。
快速重传/恢复算法 没有像「超时重传」一夜回到解放前,而是还在比较高的值,后续呈线性增长。
③-③小结:
①顺序问题、丢包问题、流量控制都是通过滑动窗口来解决的,滑动窗口其实就相当于领导和下属的备忘录,布置过的工作要有编号,干完了有反馈,活不能派太多,也不能派太少。
②拥塞控制是通过拥塞窗口来解决的,相当于往管道里面倒水,快了容易溢出,慢了浪费带宽,要摸着石头过河,找到最优值,