TCP 流量控制 — 接收窗口 rwnd 动态调整
初始A 连续发送,填满发送窗口
B 初始通告 rwnd=400。A 设 swnd=400,连续发送:
| A 发送 | seq=1, DATA 1-100 | 可用窗口 |
| ① | seq=1, 100B | 300 |
| ② | seq=101, 100B | 200 |
| ③ | seq=201, 100B | 100 |
每次发送后可用窗口减少。窗口仍是 400,但"已发未确认"字节从 0 → 300。
/* 发送窗口状态 */
swnd = 400;
/* 已发送 1-300,未确认 */
/* P1=1, P2=301, P3=401 */
inflight = 300; // 已发未确认
usable = 100; // 还可发送
第一次流量控制rwnd 从 400 → 300
B 的应用程序读取速度慢,接收缓存可用空间从 400 降到 300。B 在 ACK 中通告新的 rwnd=300。
| B → A:ACK=201, rwnd=300 | ||
| 含义 | 201 之前的数据已收到;接收窗口缩小为 300 | |
| A 的发送窗口变化 | ||
| swnd | 400 → 300 | 窗口收缩 → 覆盖 201–500 |
| 201-300 | 已发未确认 | 暂留在发送缓存中 |
| 301-500 | 允许发送 | 可用窗口 = 200 |
/* A 收到 ACK=201, rwnd=300 后 */
P1 = 201; // 后沿前移:1-200 已确认可删除
P2 = 301; // 201-300 已发未确认
P3 = 501; // 前沿 = 201+300=501
swnd = P3 - P1 = 300; // 窗口缩小!
inflight = P2 - P1 = 100;
usable = P3 - P2 = 200;
窗口用完发送 301-500 → 可用窗口归零
A 将窗口内剩余数据发出:
| A 发送 | seq=301, DATA 301-400 | 可用窗口 = 100 |
| A 发送 | seq=401, DATA 401-500 | 可用窗口 = 0 |
可用窗口归零 → A 不能再发送新数据。
此时 P₁=201, P₂=501, P₃=501。inflight=300, usable=0。
如果迟迟收不到 ACK,已发送的数据(201-300, 301-400, 401-500)将等待超时重传。
/* 窗口用完 */
P1 = 201; P2 = 501; P3 = 501;
inflight = 300; usable = 0;
/* A 暂停发送,等待 ACK */
/* 如果 201-300 超时 → 重传 */
/* 如果 B 的 ACK 到达 → 窗口滑动 */
第二、三次流量控制rwnd → 100 → 0
A 重传 201-300。B 收到后回复 ACK,但 B 的缓存持续堆积:
| 阶段 | B 通告 | A 响应 |
| 第二次流量控制 | ACK=501, rwnd=100 | swnd→100,覆盖 501-600 |
| A 发送 | — | seq=501, 100B → 可用窗口归零 |
| 第三次流量控制 | ACK=601, rwnd=0 | swnd→0,停止发送 |
B 的缓存已满,应用进程读取过慢 → rwnd 从 300 → 100 → 0。
A 三次被迫收缩发送窗口:接收方掌握绝对控制权。
/* 流量控制的核心逻辑 */
/* rwnd 由接收方决定,发送方被动执行 */
// B 应用读得慢 → 缓存满 → rwnd↓
// B 应用读得快 → 缓存空 → rwnd↑
// rwnd=0 → A 停止发送
// A 不主动决定窗口大小
/* 这就是"流量控制":
接收方直接反馈 → 发送方被动适应 */