// 网络协议 · 深度图解
TCP over TCP
灾难性性能崩溃
当 VPN 使用 TCP 封装传输 TCP 流量时,两层拥塞控制相互干扰,造成吞吐量指数级崩溃。本文用图解方式还原这个经典问题的完整机制。
正常单层 TCP — 丢包重传机制
CLIENT 应用层 SERVER 应用层 互联网 ⚡ 1% 丢包率 SEQ=1 SEQ=2 ✗ 丢失 × 丢弃 ACK=1 ✓ ⟳ 重传 SEQ=2(单次处理,快速恢复) ✓ 只有一层 TCP 退避 → cwnd/2 → 快速恢复,影响轻微
单层 TCP 的 AIMD(加法增大乘法减小) 拥塞控制:丢包时窗口减半,然后线性增长恢复。整个过程由一个控制器管理,收敛速度快,通常数个 RTT 内恢复正常吞吐。
IP/TCP-in-TCP 封装结构 — VPN tun 设备如何工作
客户端 应用程序 Inner TCP Inner IP 私网 10.x.x.x → 10.y.y.y ← tun 在此截获完整 IP 包 tun 虚拟网卡 加密 + VPN 封装 Outer TCP Outer IP 公网 1.2.3.4 → 5.6.7.8 物理网卡 服务端 应用程序 Inner TCP Inner IP 私网 10.x.x.x → 10.y.y.y tun 虚拟网卡 解密 + VPN 解封装 Outer TCP Outer IP 公网 1.2.3.4→ 5.6.7.8 物理网卡 物理网络上传输的完整数据包 Outer 层(互联网可见) Outer IP Header 公网IP Outer TCP Header VPN端口 🔒 VPN 加密载荷(路由器/运营商无法读取内部) Inner IP Header 私网IP Inner TCP Header 应用端口 Application Data HTTP / SSH / 任意应用数据 (已加密,外界不可见) ✦ 原图遗漏此层 互联网(路由器只能看到 Outer IP + Outer TCP) 可能发生丢包 · 延迟抖动 · 拥塞 ⚠ tun 设备截获的是完整 IP 包(Inner IP + Inner TCP + Data) Outer TCP 与 Inner TCP 各自独立维护拥塞窗口和重传计时器 — 互不知晓 正式名称应为 "IP-in-TCP","TCP over TCP" 是工程界约定俗成的简称
精确说明:VPN 使用 tun 虚拟网卡在网络层(L3)拦截流量,拿到的是包含 Inner IP Header 的完整 IP 数据包,而非裸 TCP 段。因此准确名称是 "IP-in-TCP""TCP/IP over TCP"。"TCP over TCP" 只是业界简称,强调两层 TCP 拥塞控制冲突这一核心问题。
灾难时序 — 崩溃循环完整演示
时间 Outer TCP(VPN 隧道层) cwnd = 拥塞窗口 Inner TCP(应用层) cwnd = 拥塞窗口 吞吐量 T0 — 正常 cwnd = 64 (满速) 正常传输加密数据包 cwnd = 64 (满速) 正常传输应用数据 100% ▼ 网络丢包事件发生 T1 — 丢包 ⚡ 检测到丢包! cwnd: 64 → 32(减半) 启动重传,RTT 上升 RTT 开始上升... cwnd 暂未变化 等待 ACK 超时 ~50% T2 误判丢包 Outer TCP 重传中... cwnd = 32,持续发送 (加密隧道暂时阻塞) ⚡ ACK 超时!误以为丢包! cwnd: 64 → 32(再次减半) Inner TCP 也开始重传 → 加剧 Outer 积压 ~25% T3 双层退避 Inner 重传包涌入 Outer 缓冲区膨胀 cwnd: 32 → 16(再次减半) RTT 暴增(因 Outer 积压) 再次触发超时重传 cwnd: 32 → 16 → 继续退避 ~8% T4 — 崩溃 几乎停滞 🔴 双层 TCP 同时进入 RTO 超时等待(秒级!) Outer cwnd = 1,Inner cwnd = 1 → 吞吐量趋近于零 Inner 每次重传都要等 Outer 的 RTO,Outer 的 RTO 又被 Inner 积压撑大 → 无限循环 ~1% 死亡 T5 — 恢复 缓慢爬升 两层 TCP 各自独立缓慢恢复,但任何新的丢包都会再次触发崩溃循环 恢复时间:正常 TCP ≈ 几个 RTT  |  TCP over TCP ≈ 数十秒甚至分钟级 ~10% ⟲  恶性循环:任意新丢包 → 重回 T1,双层退避再次叠加 由于每轮 RTO 比上一轮更长(指数退避),系统吞吐在较差网络下将长期处于崩溃态
吞吐量对比 — 丢包事件后的恢复曲线
100% 75% 50% 25% 0% 吞吐量 0s 2s 4s 6s 8s 10s 12s 时间 ⚡ 丢包事件 普通 TCP — 快速恢复 TCP over TCP — 持续崩溃 AIMD 线性恢复 双层 cwnd 同时→1,停滞
普通 TCP(单层) TCP over TCP(VPN TCP模式)
关键差异:普通 TCP 丢包后通过 AIMD 在数秒内恢复到 ~90% 吞吐;TCP over TCP 在相同丢包后可能需要数十秒到数分钟才能部分恢复,且在存在持续丢包的网络环境下(如 2G/3G、拥塞 WiFi)几乎无法维持可用吞吐。
解决方案 — 为什么 UDP 封装解救了问题
OpenVPN
TCP 模式
Outer IP + Outer TCP(OpenVPN) 🔒 加密载荷 Inner IP Hdr Inner TCP Hdr App Data
封装协议TCP
TCP over TCP 问题✗ 有
丢包下性能灾难性崩溃
防火墙穿透易穿透 (443)
OpenVPN
UDP 模式
Outer IP + Outer UDP(OpenVPN) 🔒 加密载荷 Inner IP Hdr Inner TCP Hdr App Data
封装协议UDP
TCP over TCP 问题✓ 无
丢包下性能Inner TCP 正常控制
防火墙穿透UDP 可能被封
WireGuard
UDP 模式
Outer IP + Outer UDP(WireGuard) 🔒 加密载荷 Inner IP Hdr Inner TCP Hdr App Data
封装协议UDP
TCP over TCP 问题✓ 无
丢包下性能Inner TCP 正常控制
防火墙穿透UDP 可能被封
为什么 UDP 封装解决了问题?
Outer TCP 时:两层拥塞控制冲突 Inner IP Inner TCP 冲突 Outer TCP 网络 丢包 → Outer TCP 重传 + cwnd 减半 Inner TCP 因延迟暴增误判丢包 → 也退避 → 双层同时退避 → 吞吐崩溃 Outer UDP 时:控制权归还 Inner TCP Inner IP Inner TCP Outer UDP 无重传逻辑 网络 丢包 → UDP 无状态,直接丢弃该 IP 包 Inner TCP 感知丢包 → 正常 AIMD 退避 → 只有一层退避 → 正常快速恢复
核心原理:UDP 是无状态、无重传的传输协议。当用 UDP 封装 TCP 时,Outer UDP 碰到丢包直接忽略,把丢包的感知和处理权完全交给 Inner TCP。于是整个系统退化为单层 TCP 行为,拥塞控制重新变得可预测和高效。
TCP OVER TCP PROBLEM // NETWORK PROTOCOL ANALYSIS
参考:OpenVPN FAQ, RFC 793, Olaf Titz "Why TCP Over TCP Is A Bad Idea" (2001)