TCP 发送窗口滑动 — P₁ · P₂ · P₃

1. 收到 ACK=31, win=20 → 构造发送窗口

接收方通告 ack=31, rwnd=20。发送方据此构造 swnd=20(暂不考虑 cwnd)。

已确认 0–30
允许发送 31–50
不允许 51+
P₁=P₂=31
P₃=51
20
swnd = P₃−P₁ = 51−31
0
已发未确认 = P₂−P₁
20
可用窗口 = P₃−P₂
/* 发送方收到 ACK=31, win=20 后 */
P1 = 31;  // 窗口后沿
P2 = 31;  // 尚未发送任何数据
P3 = 51;  // 窗口前沿 = 31+20

send_window  = P3 - P1 = 20;
inflight     = P2 - P1 = 0;
usable       = P3 - P2 = 20;

2. 发送了 31–41(11 字节)

发送方把 31–41 号字节封装在几个 TCP 报文段中发出。这些数据进入"已发送未确认"区。

已确认 0–30
已发未确认 31–41
可发 42–50
不允许 51+
P₁=31
P₂=42
P₃=51
20
swnd 不变
11
已发未确认 = 42−31
9
可用窗口 = 51−42
/* 发送 31–41 后 */
P1 = 31;  // 后沿不动 —— 未收到新 ACK
P2 = 42;  // 已发送到 41,下一位是 42
P3 = 51;  // 前沿不动

/* 关键规则 */
// 后沿 P1:只在收到确认时前移
// 后沿 P1:绝不后移(已确认不可撤销)
// 前沿 P3:可能前移、不动、或(不推荐)后缩

3. 收到 ACK=34 → 窗口向前滑动 3 字节

确认号 34 表示 31–33 已按序到达。后沿 P₁ 前移到 34,`31–33` 可从发送缓存删除。前沿 P₃ 前移到 54。

已确认 0–33
已发未确认 34–41
可发 42–50
不允许 54+
P₁=34
P₂=42
P₃=54
20
swnd 不变
8
已发未确认 = 42−34
12
可用窗口 = 54−42
/* 收到 ACK=34 后 */
P1 = 34;  // 后沿前移:31-33 已确认
P2 = 42;  // P2 不动:尚未发送新数据
P3 = 54;  // 前沿前移:P1+swnd = 34+20

/* 已确认的 31-33 可从缓存删除 */
// 小于 P1 的数据 = 已确认 = 可删除

4. 三个指针的核心规则总结

P₁ 后沿只有两种可能:不动(无新确认)或前移(收到新累积确认)。
绝不后移——已确认的字节不可撤销,数据可从发送缓存删除。
P₂ 可用起点指向下一个允许发送但尚未发送的字节。发送数据时 P₂ 前移;收到 ACK 时 P₂ 不动。
P₃ 前沿三种可能:前移(常态·确认扩大窗口)、不动(无变化或变化抵消)、后缩(接收方缩小窗口——TCP 强烈不推荐)。
/* 三个指针的精确含义(用于编程实现) */
/* < P1 : 已确认可删除 */
/* P1 ~ P2 : 已发送未确认(inflight) */
/* P2 ~ P3 : 允许发送但未发送(usable) */
/* > P3 : 不允许发送 */

swnd    = P3 - P1;  // 发送窗口总大小
inflight = P2 - P1;  // 已发未确认
usable   = P3 - P2;  // 可用窗口/有效窗口