TCP 的拥塞控制
拥塞控制的一般原理
在计算机网络中的链路容量(即宽带)、交换结点中的缓存和处理机等,都是网络资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫做拥塞
(congestion)
拥塞控制
就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载
。拥塞控制所要做的都是一个前提,就是网络能够承受现有的网络负荷
TCP 的拥塞控制方法
TCP 进行拥塞控制的算法有四种,即慢开始
(slow-start)、拥塞避免
(congestion avoidance)、快重传
(fast retransmit)和快恢复
(fast recovery)
慢开始
当主机开始发送数据时,由于并不清楚网络的负荷情况,如果立即把大量数据字节注入到网络,就有可能引起网络发生拥塞。经验证明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口
,也就是说,由小到大逐渐增大拥塞窗口数值
cwnd:发送方的拥塞窗口,开始发送方设置 cwnd = 1
拥塞避免
让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加1,而不是像慢开始阶段那样加倍增加。因此在拥塞避免阶段就有“加法增大
” AI(Additive Increase)的特点。这表明在拥塞避免阶段,拥塞窗口 cwnd 按线性规律缓慢增长
,比慢开始算法的拥塞窗口增长速率缓慢得多
“拥塞避免”并非完全能够避免拥塞,而是把拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞
在执行慢开始算法时,发送方每收到一个队新报文段的确认 ACK,就把拥塞窗口值加1,然后开始下一轮的传输。因此拥塞窗口 cwnd 随着传输轮次按指数规律增长。当拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时,就改成执行拥塞避免算法,拥塞窗口按线性规律增长
ssthresh:慢开始门限,一般的,会有一个初始值,下图中为16个报文段
当拥塞窗口 cwnd = 24 时,网络出现了超时,发送方判断为网络拥塞。于是调整门限值 ssthresh = cwnd / 2 = 12,同时设置拥塞窗口 cwnd = 1,进入慢开始阶段
快重传
采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失
。快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认
,即使收到了失序的报文段
也要立即发出对已收到的报文段的重复确认
快恢复
发送发知道当前只是丢失了个别的报文段。于是不启动慢开始,而是执行快恢复
算法。这时,发送方调整门限值 ssthresh = cwnd / 2 = 8,同时设置拥塞窗口 cwnd = ssthresh = 8,并开始执行拥塞避免算法
TCP 可靠传输的实现
以字节为单位的滑动窗口
发送窗口构造
TCP 的滑动窗口是以字节为单位的。假定 A 收到了 B 发来
的确认报文段,其中窗口是20字节,而确认号是31(这表明 B 期望收到的下一个序号是31,而序号30为止的数据已经收到了)。根据这两个数据,A 就构造出自己的发送窗口
发送窗口标识:在没有收到 B 的确认的情况下,A 可以连续把窗口内的数据都发送出去。凡是已经发送出去的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用
发送窗口变化
发送窗口的位置由窗口前沿和后沿的位置共同确定。发送窗口后沿的变化情况有两种,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销已收到的确认
发送窗口前沿通常是不断向前移动,但也有可能不动。这对应于两种情况:
- 一是没有收到信的确认,对应通知的窗口大小也不变
- 二是收到了新的窗口单对方通知的窗口缩小了,使得发送窗口前沿正好不动
发送窗口前沿也有可能向后收缩
。这发生在对方通知的窗口缩小了。但 TCP 的标准强烈不赞成这样做
。因为很可能发送方在收到这个通知以前已经发生了窗口中的许多数据,现在又要收缩窗口,不让发送这些数据,这样就会产生一些错误
要描述一个发送窗口的状态需要三个指针:P1,P2,P3。指针都指向字节的序号。这三个指针指向的几个部分的意义如下:
- 小于 P1 的是已发送并已收到确认的部分,而大于 P3 的是不允许发送的部分
- P3 - P1 = A 的发送窗口
- P2 - P1 已发送但尚未收到确认的字节数
- P3 - P2 允许发送但当前尚未发送的字节数(又称为
可用窗口
或有效窗口
)
B 的接收窗口大小是20。在接收窗口外面,到30号为止的数据是已经发送过确认,并且已经交付主机了。因此在 B 可以不再保留这些数据。接收窗口内的序号(31\~50)是允许接收的。在上图中,B 收到了序号为32和33的数据。这些数据没有按序到达,因为序号为31的数据没有收到(也许丢失了,也许滞留在网络中的某处)。请注意,B 只能对按序收到的数据中的最高序号给出确认,因此 B 发送的确认报文段中的确认号仍然是31(即期望收到的序号),而不是32或33
现在假定 B 收到了序号为31的数据,并把序号为31\~33的数据交付主机,然后 B 删除这些数据。接着把接收窗口向前移动3个序号,同时给 A 发送确认,其中窗口值仍为20,但确认号是34.这表明 B 已经收到了到序号33为止的数据。B 还收到了序号为37,38和40的数据,但这些都没有按序到达,只能先暂存在接收窗口中。A 收到 B 的确认后,就可以把发送窗口向前滑动3个序号,但指针 P2 不动。现在 A 的可用窗口增大了,可发送的序号范围是42\~53
A 在继续发送完序号42\~53的数据后,指针 P2 向前移动和 P3 重合。发送窗口内的序号都已用完,但还没有再收到确认。由于 A 的发送窗口已满,可用窗口已减小到零,因此必须停止发送。发送窗口内所有的数据都已正确到达 B,B 也早已发出了确认。但所有这些确认都滞留在网络中。在没有收到 B 的确认时,A 不能猜测:“或许 B 收到了吧!”为了保证可靠传输,A 只能认为 B 还没有收到这些数据。于是,A 在经过一段时间后(由超时计时器控制)就重传这部分数据,重新设置超时计时器,知道收到 B 的确认为止。如果 A 收到确认号落在发送窗口内,那么 A 就可以发送窗口继续向前滑动,并发送新的数据
Nagle 算法
在 TCP 的实现中广泛使用 Nagle 算法
若发送应用进程把要发送的数据逐个字节地送到 TCP 的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。当发送方收到对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发送出去,同时继续对随后到达的数据进行缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。当数据达到较快而网络速率较慢时,用这样的方法可明显地减少所用的网络宽带。Nagle 算法还规定,当到达的数据已达到发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。这样可以有效提高网络的吞吐量