文章目录
- 概要&序論
- 一、拥塞控制
- 1.1 为什么需要拥塞控制?
- 1.2 慢启动算法与拥塞窗口(cwnd)
- 1.2.1 拥塞窗口(cwnd)与滑动窗口的最终计算
- 1.3 拥塞控制的过程:从慢启动到拥塞避免
- 1.4 常见疑问与网络极限
- 1. 拥塞窗口增加,发送的数据量一定增加吗?
- 2. 在网络特别健康的情况下,拥塞窗口会无限一直增大吗?
- 二、延迟应答
- 2.1 延迟应答的核心逻辑
- 2.2 延迟应答带来的直接效益
- 三、TCP 总结
- 3.1 可靠性与性能优化机制分类
- 1. 保证可靠性的机制
- 2. 提高性能的机制
- 3. 其他关键支持机制
- 3.2 纠偏思考:UDP 一定比 TCP 效率高吗?
概要&序論
Hello大家好,我是此方。在前面的讨论中,我们主要关注的是客户端与服务端两端的问题(例如通过滑动窗口进行流量控制)。然而,TCP 协议不仅考虑了传输两端主机的接收能力,还必须考量中间网络环境本身的状况——拥塞控制。人们想到TCP一定是他的可靠性,但是TCP也有效率的考量——本文将讲解延迟应答和捎带应答两种机制。好,我们开始吧。
一、拥塞控制
1.1 为什么需要拥塞控制?
在网络传输过程中,丢包少和丢包多所反映的本质问题是完全不同的:
- 发送 1000 次报文,丢包 5 次:属于正常现象,偶尔的网络抖动会导致少数报文丢失。
- 发送 10000 次报文,丢包 500 次:属于非常规现象,说明当前网络链路存在异常。
当出现大面积丢包时,发送方会判定这是网络拥塞所导致的问题。此时如果盲目地立即重发数据,不仅无法解决问题,反而会进一步增加网络的负载,导致网络更加拥塞。
或许有人会产生疑问:单个客户端发送 1000 个报文对于庞大的网络来说微不足道,为什么不能立即重发?因为网络是由成千上万个客户端和服务端共同共享的。如果每个主机都认为自己的数据量小而选择立即重发,就会加速整个网络的瘫痪。
因此,我们需要拥塞控制机制。拥塞控制的作用在于统一管理和协调发送端的多个主机,让所有主机在面对网络拥塞时都采用相同的策略,以共同维护网络环境的稳定。
1.2 慢启动算法与拥塞窗口(cwnd)
既然在网络发生拥塞时不能立即重发大量报文,TCP 采取的解决方案就是:慢启动算法。
慢启动的策略是先发少量的数据探探路,摸清当前网络的拥塞程度,然后再决定按照多大的速度发送数据。
- 在网络拥塞时,通过按指数级增加发送数据量(每次翻倍,如 1、2、4、8…),使得前期大家发送都比较慢。所有主机都遵循这个共识,使得网络有足够的时间恢复,把积压的报文发送干净。
- 在网络不拥塞后,通过这种指数级增长的方式,可以极快地恢复网络通信。
1.2.1 拥塞窗口(cwnd)与滑动窗口的最终计算
我们之前讲过,发送方一次能发多少数据是由滑动窗口决定的,而滑动窗口的大小取决于对方的接收能力。但这里就带来了一个问题:慢启动算法的实现到底依靠的是什么?
为了支持慢启动算法,TCP 引入了拥塞窗口(cwnd,Congestion Window)的概念。在当前计算机内部,拥塞窗口就是用来衡量网络是否会拥塞的一个指标,其本质就是一个整数:
- 临界值:在拥塞窗口数值以内,网络较大概率不拥塞;超过这个数值,网络就可能发生拥塞。
- 动态更新:很显然,网络状况是随时变化的,这就决定了拥塞窗口的值一定要进行更新变化。
引入拥塞窗口后,发送方实际的滑动窗口大小不再单单由对方决定,而是进行了升级:
滑动窗口大小 = min ( 对方的 win 大小 , 拥塞窗口大小 ) \text{滑动窗口大小} = \min(\text{对方的 win 大小}, \text{拥塞窗口大小})滑动窗口大小=min(对方的win大小,拥塞窗口大小)
谁小,谁就是主要问题、主要矛盾!这样的机制,既考虑了网络拥塞问题,又考虑了对方接收能力的问题。
1.3 拥塞控制的过程:从慢启动到拥塞避免
拥塞窗口不可能一直指数增长下去,否则很快又会引发网络拥塞。TCP 的拥塞控制过程主要分为三个阶段:
- 前期(慢启动阶段):拥塞窗口初始值较小,数据量按指数规律增长(2 n 2^n2n),目的是前期慢发、减少网络压力,并快速探测网络状况。
- 中期(拥塞避免阶段):为了防止拥塞窗口增长过快导致网络拥塞,TCP 引入了一个阈值——慢启动阈值(ssthresh)。
- 当拥塞窗口达到
ssthresh之前,采用指数增长。 - 当拥塞窗口达到或超过
ssthresh时,不再按指数增长,而是转变为线性增长(每次增加 1),这种方式被称为“加法增大”,本质是为了探测网络的通畅程度。
- 当拥塞窗口达到
- 后期(乘法减小与重新开始):
- 随着拥塞窗口持续线性增大,当网络再次发生拥塞(出现丢包)时,TCP 会采取“乘法减小”策略:将新的
ssthresh降低为当前拥塞窗口的一半。 - 同时,拥塞窗口
cwnd会重新被置为初始值(如 1),重新开始慢启动过程,以此来探测网络的健康状况。
- 随着拥塞窗口持续线性增大,当网络再次发生拥塞(出现丢包)时,TCP 会采取“乘法减小”策略:将新的
网络拥塞不是单台主机导致的,而是由全网所有主机共同引起的。所有主机都在不断地探测网络的拥塞窗口,这个过程会不断重复,拥塞窗口与慢启动阈值ssthresh的值也会随之不断更新。
1.4 常见疑问与网络极限
在理解拥塞控制时,有两个经常被提及的问题:
1. 拥塞窗口增加,发送的数据量一定增加吗?
不一定!因为实际发送的数据量(滑动窗口)取决于min{对方的 win 大小, 拥塞窗口大小}。即使拥塞窗口一直在增长,如果对方的接收缓冲区满了(win变小),实际发送的数据量依然会受到限制。
2. 在网络特别健康的情况下,拥塞窗口会无限一直增大吗?
实际上不会!逻辑上如果网络非常健康,拥塞窗口似乎应该一直增大,但单位时间内的物理带宽(硬件阈值)是限制拥塞窗口不可能无限增长的关键因素。当达到带宽上限时,必然会导致数据延迟或丢包,进而触发拥塞控制。
总结来说,最理想的网络传输状态是:网络状态健康稳定,传输过程完全以对方的接收能力限制为准。
二、延迟应答
在 TCP 传输过程中,如果接收数据的主机立刻返回 ACK 应答,这时候返回的通告窗口可能相对比较小。
2.1 延迟应答的核心逻辑
我们可以通过一个具体的场景来理解为什么需要延迟应答:
- 假设接收端缓冲区总大小为 1M,一次收到了 500K 的数据。如果接收端立刻进行应答,那么剩余的缓冲区空间就只有 500K,此时返回的窗口大小就是 500K。
- 但实际上,上层应用处理数据的速度可能很快,也许在 10ms 之内就把这 500K 数据从接收缓冲区中全部消费掉了(通过
read等系统调用将数据拷贝到了应用层)。 - 在这种情况下,接收端的处理能力远还没有达到自身的极限,即便窗口再放大一些,接收端也完全能处理过来。
- 如果接收端稍微等待一会儿再进行应答,比如等待 200ms 再应答,那么在这段时间内上层应用已经把数据消费完,此时返回的通告窗口大小就是 1M!
一定要记得:窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况之下,尽量提高传输效率。
2.2 延迟应答带来的直接效益
通过延迟应答机制,TCP 能够实现以下两个维度的优化:
- 通告更大窗口:给上层应用留出消费缓冲区数据的时间,从而能够向发送端通告更大的接收窗口,提高传输吞吐量。
- 减少应答数量:由于并不是所有的报文都需要发送应答,接收端可以在收到多个报文后,直接针对后续接收到的最新报文发送一次确认应答,从而减少网络中 ACK 报文的数量,节省网络带宽。
三、TCP 总结
为什么 TCP 协议这么复杂?因为 TCP 既要保证数据的可靠性,同时又要在保证可靠性的前提下尽可能地提高性能。
3.1 可靠性与性能优化机制分类
我们可以将 TCP 核心的技术机制划分为三大维度:
1. 保证可靠性的机制
- 校验和
- 序列号(按序到达)
- 确认应答
- 超时重发
- 连接管理
- 流量控制
- 拥塞控制
2. 提高性能的机制
- 滑动窗口
- 快速重传
- 延迟应答
- 捎带应答
3. 其他关键支持机制
- 定时器(超时重传定时器、保活定时器、TIME_WAIT 定时器等)
3.2 纠偏思考:UDP 一定比 TCP 效率高吗?
平时听别人说:UDP比TCP效率高。我认为是错误的,应该结合具体场景分析。
TCP还有最后两个补充话题——异常、从源码看TCP,我们下一期再介绍。