什么保证了TCP的可靠

1,435 阅读7分钟

本文正在参与 “网络协议必知必会”征文活动

可靠的基石——ACK机制

ACK机制是指接收方对发送方做出应答,由于TCP是基于字节流的,因此ACK对应的数据单位是字节,例如收到的数据报序号seq=x,报文数据的字节大小为1000,那么ACK报文中的ack=x+1000。

ACK报文中的应答序号表示的是“该序号之前的报文全部收到”,例如一个1M的文件被拆分成{0,1,…N-1,N,N+1,N+2} 几个报文进行传输,接收方接收后根据需要将其组装。如果接收的顺序为N-1,N+1,N+2,由于最后一个有序包为N-1,接收方仅会进行ACK(N-1)的确认。

在不可靠的信道中达成一致

在网络中不可靠的信道是必须被考虑的一点,三次握手的目的是:发送方和接收方确认要建立一条链接并确定报文序列号。

在不可靠的信道中进行两次握手会怎么样?在谢希仁的《计算机网络》中指出:在不可靠的网络下,当客户端的第一次连接请求在网络中滞留了,但实际上通过超时重发机制,客户端和服务器已经建立连接,那么当服务器接收到这个超时的连接后,如果仅通过两次握手建立连接,那么服务端会监听这个不会存在的连接,造成白白的资源浪费。

简而言之,要保证二者在不可靠信道中达成一致,三次是最小的握手次数

实现网络流控的技术——滑动窗口

TCP协议实际传输的是字节流,发送方和接收方在流上圈定了一个窗口用于控制数据的发送和接收,窗口随着报文的发送和被确认滑动,因而被称为滑动窗口。对于发送方来讲,这个窗口被称为发送窗口(swnd),对于接收方来讲,这个窗口被称为接收窗口(rwnd)。

image.png

对于发送端来说,滑动窗口从允许发送的字节开始,到已经ACK的字节结束。对于end端可以直接从ack序号获取,而对于start端,是从TCP协议中的window字段获取的。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。发送端根据接收端的处理能力来发送数据,以防接收端处理不过来。

image.png

对于接收端来说,接收窗口框定出允许接收的字节,在ACK包中会在win变量中携带这个值,用于告知发送方接收窗口的处理能力。

应对丢包的机制——超时重传

在不可靠的信道中,对于可能丢失的数据报文需要超时重传。首先需要解决的问题是:如何判断报文超时?由于网络是波动的,因此判断报文超时的超时重传时间RTO)不是一成不变的,其必然是一个根据网络情况动态变化的值。

一种经典计算方法如下所示,其目的是综合每个报文离散的传输时间,使计算出的RTO 变平滑,其计算公式如下

RTTS=(1α)RTTS+αRTTRTT_S= (1-\alpha) RTT_S + \alpha RTT

RTTd=(1β)RTTd+βRTTRTTSRTT_d = (1-\beta) RTT_d + \beta |RTT-RTT_S|

RTO=RTTS+4RTTdRTO = RTT_S + 4 * RTT_d

公式1的目的是将往返时间平滑处理,这里引入了 往返时间 RTT(round trip time)平滑往返时间 SRTT(Smoothed round trip time)RTT代表一个报文从发出到被ACK的时间间隔。α\alpha用来调控平滑程度,值越小,单条报文的RTTSRTT的影响越小,对网络的变化越不敏感,超时重传时间的变化越平滑,其建议值为0.125。公式2的目的计算RTTsRTT_s的平均偏差,β\beta的建议值为0.25。公式3最终推导出超时重传时间。

Karn算法——处理重传的RTT

对于超时重传的报文,计算RTT时可能出现下面的错误

image.png

Karn算法采用的策略为,当出现超时重传 (1)不更新RTT。 (2)RTO进行“指数退避”,即RTO=RTO2NRTO=RTO * 2^N,下一次传送就使用这个RTO值,如果仍超时N继续加1。

重传数据确认之后。再次发送的数据假设正常被确定。则恢复Jacobson公式,更新RTO和RTT。

避免等待的丢包补偿——快速重传

超时重传机制中,重传一定要等待RTO的时间间隔,但如果发送方能判断出哪些报文丢失了,就可以立即进行重传,发送方利用就是ACK机制。之前提到ACK机制是接收方向发送方确认连续收到的最后一个字节序号,那么假设发送了{101-200}{201-300}{301-400}{401-500}四个数据报文,其中{201-300}的报文在信道中丢失了,那么发送方会受到三次对201的ACK报文,基于这些信息,我们明确知道接收方没有收到{201-300}的报文,那么就会触发重传。

如何确认重传哪些报文

在TCP协议中提供了SACK(selective acknowledge) 字段,用于接收方在发送ACK报文时同时告诉发送方接收到了哪些数据,在上面的场景中,三个ack=201报文的sack分别为{101-200}{301-400}{401-500},这样发送方就只对{201-300}报文重传。

试探信道传输速率的机制——拥塞控制

当信道出现拥塞,发送方超时在RTO内没有收到ACK,就会重发报文,这显然加剧了信道的拥塞,因此必须有一个能够根据信道拥塞情况控制报文的发送数量的机制,这个控制报文发送数量的变量为拥塞窗口(cwnd) 。因此发送方实际的发送速率为Min(cwnd,rwnd)Min(cwnd, rwnd)

TCP需要不断试探cwnd的大小,其方法就是不断尝试多发几个报文,观察其是否有超时,如果有超时再将其调低,重新进行试探。这样就需要解决几个问题

  1. cwnd的初始值是多少?
  2. 试探上限时,每次多发几个报文?
  3. 什么时候将发送的报文数量降低?降低到多少合适?

cwnd的初始值是多少?

目前拥塞窗口的初始值为10,主要是根据 Google 的研究,90% 的 HTTP 请求数据都在 16KB 以内,约为 10 个 TCP 段。

试探上限时,每次多发几个报文?

在试探上限的阶段,每经过一个传输轮次,拥塞窗口变为原来的2倍,当然不能一直变化这么快,后面的增长速率需要变的缓和,即商定如果拥塞窗口大于慢启动阈值(ssthresh ,后续传输轮次中拥塞窗口每次加一。第一种方法,cwnd是指数增长的但初始值较低,因此被称为慢开始;第二种方法,cwnd是线性增长的,主要为了避免报文数迅速碰撞引发的拥塞,背刺被称为拥塞避免

什么时候将发送的报文数量降低?降低到多少合适?

如果报文发生超时,说明此时网络可能发生了拥塞,TCP协议对于这种情况会将拥塞窗口重新设定为初始值,重新进行慢开始。如果连续收到了三个重复的ACK,在前面提到过对于这种情况会进行快速重传策略,这种对个别报文段的丢失大概率不是网络拥塞,TCP协议对于这种情况将拥塞窗口设定为当前的一半,同时将慢启动阈值也设定为这个值,直接进入到拥塞避免阶段,这相比于慢开始来说是一个较为迅速的过程,被称为快恢复

  • TCP协议利用四个算法实现拥塞控制:慢开始、拥塞避免、快重传、快恢复。

参考

TCP 为什么是三次握手,而不是两次或四次?

深入理解 TCP 协议:从原理到实战