1、流控基础:什么是流量控制?为什么需要流控?流控与拥塞控制的区别

各位好,我是老张。今天咱们正式开讲《流控调试实战》的第一章。说实话,我做了十几年网络优化,最常被问到的问题就是:「为什么我带宽加了一倍,视频还是卡?」「为什么延迟忽高忽低?」

这些问题,十有八九都跟流量控制有关。咱们今天就把这个基础打牢。

1.1 什么是流量控制?

流量控制,英文叫 Flow Control。说白了,就是控制发送方别把接收方撑死。

你想想看,网络通信就像两个人用管子倒水。发送方使劲倒,接收方来不及接,水就洒了一地。在网络上,这个「洒水」就是丢包。丢包了就得重传,重传就导致延迟和卡顿。

所以流量控制的核心逻辑就一句话:发送速度 = 接收能力。

核心定义:流量控制是端到端的、基于接收方剩余缓存空间的发送速率调节机制。它确保发送方不会发送超过接收方处理能力的数据量。

我记得刚入行时,带我的老工程师跟我说过一句话,我一直记到现在:「流控不是让车跑多快,而是让车在下一个路口停得住。」

1.2 为什么需要流控?

这个问题其实可以反过来问:没有流控会怎样?

我直接说结论:没有流控,网络会陷入「疯狂重传」的死循环。

咱们看一个真实场景。我在某视频平台做过一次压测,关闭了客户端的流控机制。结果呢?

  • 发送端以 100 Mbps 的速率猛发数据
  • 接收端缓存只有 64 KB,每秒只能处理 10 Mbps
  • 结果:丢包率飙升到 35%,有效吞吐量反而掉到 6 Mbps
  • 用户端表现:画面卡成 PPT,缓冲转圈转个不停

这就是典型的「好心办坏事」——发送方太积极,反而把网络搞崩了。

所以流控的必要性可以总结为三点:

  1. 防止接收方缓存溢出 —— 避免丢包和重传
  2. 保证公平性 —— 多个连接共享带宽时,谁也别把谁饿死
  3. 提升用户体验 —— 减少卡顿和延迟抖动

我的经验:在实际调优中,我习惯先看接收窗口(rwnd)的变化曲线。如果 rwnd 频繁降到 0,说明接收端处理不过来,这时候加带宽没用,得优化接收端处理逻辑。

1.3 流控与拥塞控制的区别

这是新手最容易搞混的地方。我当年也犯过这个错——面试时被问到「流控和拥塞控制有什么区别」,我愣了半天没答上来。

咱们用一张图把这事说清楚:

流量控制 vs 拥塞控制 发送端 接收端 中间网络 接收窗口 rwnd ← 流量控制(端到端,基于接收方能力) 网络拥塞状态(丢包/延迟) ← 拥塞控制(基于网络状态) 核心区别对比 维度 流量控制 拥塞控制 作用范围 端到端(发送⇄接收) 端到端 + 中间网络 触发条件 接收缓存剩余空间 网络丢包 / 延迟增加 核心目标 防止接收方过载 防止网络过载

从图上可以看得很清楚:

  • 流量控制:关注的是接收端能不能接得住。发送端根据接收端通告的窗口大小(rwnd)来调整发送速率。
  • 拥塞控制:关注的是中间网络能不能扛得住。发送端根据网络丢包或延迟信号来调整发送速率。

我再用一个比喻帮你理解:

  • 流量控制 = 你倒水时看着杯子的容量,别溢出来
  • 拥塞控制 = 你倒水时看着管道的粗细,别堵住

两者缺一不可。光有流控没有拥塞控制,网络一堵你就疯狂丢包;光有拥塞控制没有流控,接收端缓存一满你也得丢包。

避坑指南:我曾经遇到过一个案例,某团队把接收窗口设得特别大(2MB),以为这样能提升吞吐量。结果网络一拥塞,大量数据积压在中间路由器,延迟从 20ms 飙升到 800ms。这就是典型的「只调流控、不管拥塞」的教训。

1.4 流控的核心参数

搞清楚了概念,咱们看看实际调优时盯哪些参数。我列一个常用表格:

参数 全称 作用 调优方向
rwnd 接收窗口 接收方剩余缓存大小 根据带宽延迟积(BDP)设置
cwnd 拥塞窗口 发送方估计的网络容量 根据丢包/延迟动态调整
ssthresh 慢启动阈值 慢启动到拥塞避免的切换点 根据历史丢包记录设置
RTT 往返时间 数据包从发到收的耗时 越小越好,但受物理距离限制
RTO 重传超时 等待 ACK 的最长时间 基于 RTT 动态计算

这里我多说一句关于 rwnd 的。很多人以为 rwnd 越大越好,其实不一定。我见过一个极端案例:某直播平台把 rwnd 设成 4MB,结果用户手机内存不够,频繁 OOM 崩溃。所以 rwnd 的设置要综合考虑接收端的内存和处理能力。

1.5 流控的两种实现方式

在实际工程中,流控主要有两种实现方式:

1.5.1 基于窗口的流控(TCP 经典方式)

这是 TCP 协议默认的方式。接收方在 ACK 包里携带当前剩余窗口大小(rwnd),发送方根据这个值决定还能发多少数据。

// 伪代码示意:发送方逻辑
while (有数据要发送) {
    if (已发送未确认数据量 < 接收方通告的 rwnd) {
        发送数据;
    } else {
        等待 ACK;
    }
}

这种方式的好处是简单可靠,坏处是 rwnd 更新有延迟——等你收到 ACK 时,接收方的缓存可能已经变了。

1.5.2 基于速率的流控(现代协议常用)

像 QUIC、SRT 这些新协议,更倾向于基于速率控制。发送方直接计算一个目标发送速率,然后按这个速率匀速发送。

// 基于速率的流控示意
target_rate = 接收方反馈的可用带宽;
while (有数据要发送) {
    send_at_rate(target_rate);
    // 定期根据反馈调整 target_rate
}

我个人更偏好基于速率的方式,尤其是在视频流场景下。为什么呢?因为视频编码本身就是恒定码率或可变码率,用速率控制能跟编码器配合得更好。

我的建议:如果你在做实时音视频或直播,优先考虑基于速率的流控方案。TCP 的窗口流控在弱网环境下表现不够理想,而基于速率的方案可以更精细地控制发送节奏。

1.6 本章小结

好了,咱们把第一章的核心内容捋一遍:

  • 流量控制是端到端的、基于接收方能力的发送速率调节
  • 没有流控会导致缓存溢出 → 丢包 → 重传 → 卡顿的恶性循环
  • 流控 vs 拥塞控制:流控看接收端,拥塞控制看网络
  • 核心参数包括 rwnd、cwnd、ssthresh、RTT、RTO
  • 实现方式有基于窗口和基于速率两种,场景不同选择不同