一、前言
这是一个有趣的话题。
承接上文用 WireGuard 整了个三层组网游戏加速方案,发现 WireGuard 客户端和服务端之间主要采用的是 UDP 协议传输。
网友都说 UDP 加速器在打游戏时,国内的运营商有时候也会 QOS ,所以游戏体验也不好。
他们常用的方案是采用 udp2raw 这种开源解决方案
用 Raw Socket 在 IP 报文的基础上伪造传输层协议头。让报文长得像 TCP ,但实际不是 TCP 。
这种方案的好处在于绕过了内核协议栈,绕过了系统的拥塞控制算法。
并且包头是 TCP 从而达到欺骗运营商这是一个 TCP 报文从而解决 QOS 限制。
但是是否想过为什么 WireGuard 不支持 TCP 传输。如果 WireGuard 直接支持 TCP 传输不就行了吗?
为此我继续贴一下当初的游戏加速架构图如下所示

图中国内用户到国内 BGP 服务器之间是使用 UDP 报文传输。家宽运营商也是会对 UDP 进行 QOS 。那么这一段路如果 WireGuard 用 TCP 实现又会怎么样? 是解决了 QOS 问题吗? 那么为什么 VPN 不直接 TCP 报文去传输呢?
这就回到了文章的标题。
二、TCP over TCP 造成的雪崩问题
这个问题还是得细化到微观层面去看。
我们看一个应用程序通过 VPN 发送数据报文给目标服务器的情况,流程如下所示:
应用程序--->数据报文 [网络层[传输层[应用层[payload data]]]] ----> VPN 虚拟网卡 wg0 ----> VPN 客户端 ----[VPN协议头[[网络层[传输层[应用层[payload data]]]]]]----------> VPN 服务端 ----->目标服务器
假设 应用程序 当前发送的 TCP 报文,分别为报文 1、2、3 。
网络数据包被 VPN 劫持,成功转发给了 VPN 客户端。但客户端在给服务端公网传输环节假设丢了报文2 ,即 VPN 服务端收到了报文 1、3
因此服务端那边会返回 SACK (报文1的确认序列号) [报文1、报文3] 给到 VPN 客户端。
由于VPN服务端收到了报文1,服务端会将报文1 通过网卡 + SNAT 的形式发送给目标服务器。因此目标服务器也会返回 ACK 报文,给到内部应用程序。
我们来继续梳理一下这个过程,看看各方都收到了什么
- 首先是 应用程序 ,应用程序所在的协议栈只收到了报文1的ACK。为什么? 因为报文2在公网传输中丢了,VPN服务端虽然收到了报文3,但是由于少了2,和TCP 队头阻塞的原因 报文3只会被夯在 VPN 服务器所在的 TCP 接收缓冲区。直到2的到来
- 其次是 VPN 客户端所在的内核协议栈,收到了 SACK (报文1的确认序列号) [报文1、报文3]。 内核协议栈发现少了报文2,因此立刻出发快速重传,但是这个过程还没结束
这都造成了什么? 首先,报文3一直被夯在 VPN 的 TCP 接收缓冲区中迟迟未给上层。并且也没到达 目标服务器,对方自然也不会返回 ACK 这很明显。
因此应用程序的 RTT 变长了。
最要命的是,由于 VPN客户端在公网丢了报文2,导致应用程序报文3的 ACK 也迟迟没收到,触发了 RTO (超时重传) 又将报文3重传了一遍。
回想一下,内部应用程序所在的协议栈它有必要重传报文3吗? 因为它压根没丢,只是被夯在服务端的接收缓冲区没发给目标服务器罢了。 不仅报文3重传,报文2也会重传。 内部协议栈的 RTO 瞬间将 cwnd 发送缓冲区降为 1 。
好家伙,瞬间没包可发了。并且由于内部重传的原因,导致原有的网络更加拥堵了。
这就是 TCP 拥塞控制堆叠触发的反作用。
三、再来看看 UDP Over TCP 的情况
假设 VPN 是用 UDP 协议进行传输的。
还是老样子,内部应用程序发送报文1、2、3
UDP 没有队头阻塞,没有拥塞控制。发送给 VPN 后,全部发给了目标服务器。就算中间的报文 2 丢了。目标服务器也收到了报文 1、3。
因此目标服务器发起 SACK 报文1的确认序列号 [报文1,报文3]
这个报文将会直接影响应用程序的协议栈,触发快速重传。最多 cwnd/2 。并且能快速恢复。没有多余的重传动作,一切都是那么的和谐。
文章评论(0)