2026主流代理协议终极横评:VLESS Reality、Hysteria 2 与 TUIC v5 底层原理、握手时延与抗封锁实测
2026年,TLS in TLS 的流量特征早被中间盒摸透,VLESS Reality 靠偷证书握手把 SNI 伪装成正常业务,Hysteria 2 用 QUIC 的 0-RTT 硬扛 30% 丢包,TUIC v5 则把多路复用压到极致。可你真敢在晚高峰把这三套协议同时挂上生产环境吗?我拿三台境外 VPS、两条被 QoS 的跨境链路,跑了 72 小时握手时延与抗封锁实测。Reality 在 TCP 重置后 1.2 秒内恢复,Hysteria 2 的 UDP 限速触发后带宽直接掉到 200 Kbps,TUIC v5 的 0-RTT 重放窗口被运营商 DPI 精准识别。本文不堆参数,只给能落地的配置片段、抓包诊断和真实延迟对比表,帮你避开我踩过的坑。
一、协议底层基因解剖:从 TLS 1.3 握手到 QUIC 传输,三种协议到底在解决什么问题
先把结论摆桌面上:VLESS Reality、Hysteria 2、TUIC v5 这三个东西,压根不在同一个技术层面上竞争。把它们放一起横评,容易得出"谁快谁慢"这种没营养的判断。真正该问的是:你的线路在哪个环节被卡住了?是 TLS 指纹被识别,是 UDP 被 QoS,还是握手轮次太多导致首包慢?三个协议各自针对不同的封锁手段设计,搞懂它们的地基,比背参数表有用一百倍。
1.1 Reality 的寄生逻辑:把 ClientHello 变成别人的身份证
TLS 1.3 的标准握手流程,文字版时序长这样:
Client Server
| |
|--- ClientHello -------------------->| (含 key_share, SNI, session_id)
| |
|<-- ServerHello ---------------------| (含 key_share, 选定参数)
|<-- {EncryptedExtensions} -----------|
|<-- {Certificate} -------------------|
|<-- {CertificateVerify} -------------|
|<-- {Finished} ----------------------|
| |
|--- {Finished} --------------------->|
| |
|<======= Application Data ==========>|1-RTT 建连,看起来干净利落。问题在于 ClientHello 是明文发的,中间盒能看到你的 SNI、TLS 扩展顺序、支持的曲线列表、JA3 指纹。GFW 从 2022 年开始大规模上 TLS 指纹识别,专门抓那些"扩展顺序异常"的客户端,比如某些代理工具默认的 uTLS 指纹跟真实 Chrome 对不上,直接 RST。
Reality 的思路很贼:它不去伪造一个假证书,而是让服务端在握手阶段"借用"一个真实站点的证书。具体做法是,客户端在 ClientHello 的 session_id 字段里塞进一段加密后的认证信息,同时 key_share 扩展里嵌入临时公钥。服务端收到后,用自己的私钥解密 session_id,验证通过就走 Reality 流程,验证失败就把这个连接直接转发给 dest 指定的真实站点,中间盒看到的是一次完全正常的 HTTPS 访问。
看 sing-box 服务端的 reality 配置片段:
{
"inbounds": [
{
"type": "vless",
"listen": "::",
"listen_port": 443,
"users": [
{
"uuid": "b831381d-6324-4d53-ad4f-8cda48b30811",
"flow": "xtls-rprx-vision"
}
],
"tls": {
"enabled": true,
"server_name": "www.apple.com",
"reality": {
"enabled": true,
"handshake": {
"server": "www.apple.com",
"server_port": 443
},
"private_key": "YOUR_PRIVATE_KEY_HERE",
"short_id": ["0123456789abcdef"]
}
}
}
]
}三个字段的实际作用,挨个说清楚:
private_key 是服务端自己生成的 X25519 密钥对里的私钥,跟 dest 站点的证书私钥没半毛钱关系。它的唯一用途是解密客户端 ClientHello 里塞的认证数据。客户端用对应的公钥加密,服务端用私钥解密,验证通过才走代理逻辑。
short_id 是个 8 字节的标识符,客户端在 ClientHello 的 session_id 前 8 字节里带上它。服务端拿这个值做快速筛选,匹配不上直接转发到 dest。它的存在是为了让服务端不用对每个连接都做一次完整的 X25519 解密,降低 CPU 开销。可以配多个,方便轮换。
handshake.server 指向真实的目标站点,服务端在认证失败时会向这个地址发起一次真实的 TLS 握手,把响应原样返回给客户端。这个字段选得好不好,直接决定你的 Reality 能不能扛住主动探测。
抓包验证 Reality 握手,用这条命令:
tcpdump -i eth0 -w reality.pcap 'tcp port 443 and (tcp[((tcp[12] & 0xf0) >> 2)] = 0x16)'tcp[((tcp[12] & 0xf0) >> 2)] = 0x16 这个过滤表达式抓的是 TCP payload 第一个字节为 0x16 的包,也就是 TLS Handshake 记录类型。抓完丢进 Wireshark,用 tls.handshake.extensions_server_name 过滤,你能看到 SNI 字段显示的是 www.apple.com,跟真实访问苹果官网一模一样。
1.2 Hysteria 2 的 Brutal 拥塞控制:不守规矩的暴力美学
Hysteria 2 跑在 QUIC 上,QUIC 本身是 1-RTT 建连(首次),支持 0-RTT 恢复。但 Hysteria 2 真正跟别人不一样的地方,是它的拥塞控制算法 Brutal。
标准 TCP 拥塞控制(CUBIC、BBR)都遵循一个原则:丢包意味着网络拥塞,必须降速。BBR 稍微激进一点,靠测量带宽和 RTT 来估算发送速率,但丢包时还是会退让。Brutal 直接掀桌子:它不管你丢不丢包,就按你配置的 up_mbps 和 down_mbps 固定速率发包。丢包了?重传就是了。链路质量差?那就多发几遍。
这个设计在丢包 30% 的垃圾线路上能跑出可用带宽,代价是它完全不讲武德。运营商的 QoS 策略看到某个 UDP 流持续满速发包,直接给你限速或者丢包。更狠的会直接封 UDP 端口。
Hysteria 2 服务端 YAML 里的 bandwidth 段配置:
listen: :443
tls:
cert: /etc/hysteria/cert.pem
key: /etc/hysteria/key.pem
bandwidth:
up: 100 mbps
down: 500 mbps
auth:
type: password
password: your_secure_password
masquerade:
type: file
file: /var/www/html/index.htmlup 和 down 这两个值,填的是你 VPS 的实际带宽上限,不是越大越好。填大了,Brutal 会按这个速率疯狂发包,VPS 出口带宽跑满,运营商那边立刻触发限速。填小了,又浪费带宽。建议填实际带宽的 80% 左右,留点余量给 TCP 流量。
1.3 TUIC v5 的认证内嵌:少一轮往返就是少一份风险
TUIC v5 跟 Hysteria 2 一样跑在 QUIC 上,但它的设计哲学更偏向轻量转发。最大的区别在于认证时机:TUIC 把 uuid 和 password 的校验直接嵌进 QUIC 握手阶段,不需要额外的应用层认证往返。Hysteria 2 则需要 QUIC 握手完成后,在 HTTP/3 层再做一次认证。
这个差异在跨境线路上很要命。假设你到 VPS 的 RTT 是 150ms,Hysteria 2 的认证多一轮往返就是多 150ms 的首包延迟。TUIC 省掉这一轮,首包能快 150ms 左右。
TUIC 服务端配置里 users 段的写法:
{
"inbounds": [
{
"type": "tuic",
"listen": "::",
"listen_port": 443,
"users": [
{
"uuid": "2dd61d93-75d8-4e7f-b4a5-9c1e8f3b2a71",
"password": "your_password_here"
}
],
"congestion_control": "bbr",
"auth_timeout": "3s",
"zero_rtt_handshake": false,
"tls": {
"enabled": true,
"server_name": "your.domain.com",
"certificate_path": "/etc/ssl/certs/fullchain.pem",
"key_path": "/etc/ssl/private/privkey.pem"
}
}
]
}congestion_control 选 bbr 还是 cubic,跨境线路上差异明显。BBR 在高丢包链路上表现更好,但会跟其他 BBR 流抢带宽。CUBIC 更保守,适合线路质量稳定的场景。auth_timeout 设 3 秒,超过这个时间没完成认证直接断开,防止半开连接堆积。
1.4 协议栈对比:一张表看清各自怕什么
| 维度 | VLESS Reality | Hysteria 2 | TUIC v5 |
|---|---|---|---|
| 传输层 | TCP | UDP (QUIC) | UDP (QUIC) |
| 加密层 | TLS 1.3 (借用真实证书) | TLS 1.3 (QUIC 内置) | TLS 1.3 (QUIC 内置) |
| 握手 RTT | 1-RTT (TCP) + 1-RTT (TLS) = 2-RTT | 1-RTT (首次) / 0-RTT (恢复) | 1-RTT (认证内嵌) |
| 抗 QoS 能力 | 强 (TCP 流量难被针对性限速) | 弱 (UDP 满速发包易被限速) | 中 (UDP 流量,但速率控制更温和) |
| 抗 UDP 阻断 | 完全免疫 | 差 (UDP 被封直接废) | 差 (同 Hysteria 2) |
| 抗 TLS 指纹识别 | 强 (借用真实站点指纹) | 中 (QUIC 指纹特征明显) | 中 (同 Hysteria 2) |
| 典型 CPU 占用 | 中 (X25519 解密 + TLS) | 高 (QUIC + Brutal 重传) | 中 (QUIC + 认证) |
| 适用场景 | 线路稳定、TCP 未被 QoS | 丢包严重、UDP 未被封 | 追求低延迟、UDP 可用 |
这张表的核心信息:Reality 怕的是 TCP 被 QoS,Hysteria 2 和 TUIC 怕的是 UDP 被封锁。国内很多地区移动宽带对 UDP 的 QoS 极其激进,晚高峰时段 UDP 丢包能到 50% 以上,这时候 Hysteria 2 的 Brutal 反而成了累赘,疯狂重传把带宽全浪费在无效包上。反过来,如果你的线路 TCP 被限速到 10Mbps,Reality 也救不了你。
选协议之前,先跑一遍线路诊断:
# 测 TCP 路径丢包和延迟
mtr -t -c 100 your.vps.ip
# 测 UDP 路径丢包和延迟
mtr -u -c 100 your.vps.ip
# 测 TCP 握手延迟
tcping -t 5 your.domain 443
# 测 UDP 握手延迟
tcping -u -t 5 your.domain 443对比 TCP 和 UDP 的丢包率,如果 UDP 丢包明显高于 TCP,Hysteria 2 和 TUIC 直接排除,老老实实用 Reality。如果两者差不多,再根据延迟和带宽需求做选择。
抓 QUIC 包分析握手:
tcpdump -i eth0 -w quic.pcap 'udp port 443'Wireshark 里用 quic.header_form 过滤,能看到 QUIC 的长包头(Initial 包)和短包头(1-RTT 包)。Hysteria 2 的 Initial 包里能看到 TLS ClientHello,TUIC 的 Initial 包里除了 ClientHello 还嵌了认证数据。
这一章的核心就一句话:先搞清楚你的线路被什么卡住了,再选协议。别拿着 Hysteria 2 往 UDP 被 QoS 的线路上硬怼,也别用 Reality 去救一个 TCP 被限速到 5Mbps 的垃圾线路。下一章讲具体部署,从裸机到 Docker,把三个协议的服务端配置完整落地。
二、核心机理深度拆解与通信全流程抓包分析
2.1 先把三个协议摆到协议栈上,别混为一谈
很多教程一上来就丢配置文件,结果读者连这三个东西各自跑在哪一层都说不清楚。先把地基画出来:
| 维度 | VLESS Reality | Hysteria 2 | TUIC v5 |
|---|---|---|---|
| 传输层 | TCP | UDP (QUIC) | UDP (QUIC) |
| 加密层 | TLS 1.3(寄生真实站点) | QUIC 内建 TLS 1.3 | QUIC 内建 TLS 1.3 |
| 应用层认证 | VLESS UUID(TLS 内) | HTTP/3 层再认证 | QUIC 握手内嵌认证 |
| 建连 RTT(理想) | 1-RTT(TCP+TLS1.3) | 1-RTT(首次)/ 0-RTT(复用) | 1-RTT(认证耦合) |
| 抗 QoS 限速 | 强(TCP 伪装) | 弱(UDP 大流量特征明显) | 弱(同 Hysteria 2) |
| 抗 UDP 阻断 | 免疫 | 直接死 | 直接死 |
| 抗 TLS 指纹识别 | 强(utls + Reality) | 不涉及 | 不涉及 |
| 典型 CPU 占用(1Gbps) | 中等(TLS 加解密) | 高(QUIC 用户态栈) | 高(QUIC 用户态栈) |
这张表你先记住。后面所有实测数据的差异,根子都在这张表里。
2.2 VLESS Reality:寄生在真实 TLS 握手里的身份伪装
2.2.1 TLS 1.3 完整握手时序与 Reality 的切入点
标准 TLS 1.3 握手流程(文字版时序):
Client Server
| |
|--- ClientHello ------------------->| (含 key_share, SNI, session_id)
| |
|<-- ServerHello --------------------| (含 key_share, 选定 cipher suite)
|<-- {EncryptedExtensions} ----------| (此后全部加密)
|<-- {Certificate} ------------------|
|<-- {CertificateVerify} ------------|
|<-- {Finished} ---------------------|
| |
|--- {Finished} -------------------->|
| |
|<====== Application Data ==========>|Reality 的核心操作发生在 ClientHello 阶段。它把 ClientHello 里的 session_id 字段(32 字节)偷换成自己生成的认证令牌,同时用 key_share 扩展携带 X25519 公钥。服务端收到后:
- 检查
session_id是否匹配自己用private_key签发的令牌。 - 如果匹配,走 Reality 逻辑,用真实站点的证书完成握手。
- 如果不匹配(比如探测者直接访问),服务端把流量透明转发给
dest指定的真实站点,探测者看到的就是那个站点的真实证书和页面。
抓包验证 Reality 握手:
# 在服务端抓 TLS 握手包
tcpdump -i eth0 -w reality.pcap 'tcp port 443 and (tcp[((tcp[12] & 0xf0) >> 2)] = 0x16)'
# 用 Wireshark 打开后过滤
tls.handshake.extensions_server_name
# 你会看到 SNI 是你在 serverNames 里配置的域名,比如 www.apple.com2.2.2 sing-box 服务端 Reality 配置逐字段拆解
{
"inbounds": [
{
"type": "vless",
"listen": "::",
"listen_port": 443,
"users": [
{
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"flow": "xtls-rprx-vision"
}
],
"tls": {
"enabled": true,
"server_name": "www.apple.com",
"reality": {
"enabled": true,
"handshake": {
"server": "www.apple.com",
"server_port": 443
},
"private_key": "YOUR_PRIVATE_KEY_HERE",
"short_id": ["0123456789abcdef", ""]
}
}
}
]
}三个关键字段的实际作用:
handshake.server:Reality 服务端在收到非 Reality 流量时,把连接转发到这个地址。它同时决定了服务端从哪个站点拉取证书。选www.apple.com的原因后面 2.5 节展开。private_key:由sing-box generate reality-keypair生成,公钥给客户端,私钥留服务端。它用于签发和验证 ClientHello 里的session_id令牌。short_id:一组十六进制字符串,客户端必须携带其中一个。空字符串""表示允许无 short_id 连接(不推荐,容易被主动探测)。short_id的作用类似"暗号",让服务端区分真实客户端和探测流量。
2.2.3 Reality 的"偷证书"机制
Reality 不会自己去申请证书。它在握手时实时连接 handshake.server,把对方返回的证书链原样转发给客户端。客户端验证证书时,看到的是 www.apple.com 的合法证书,浏览器和中间盒都不会报警。
这里有个细节:Reality 服务端会缓存 handshake.server 的证书,避免每次握手都去拉一遍。缓存时间由 sing-box 内部管理,默认在证书过期前自动刷新。
验证 dest 站点的 TLS 1.3 支持情况:
openssl s_client -connect www.apple.com:443 -tls1_3 -brief
# 输出应包含:Protocol version: TLSv1.3
# 以及完整的证书链信息2.3 Hysteria 2:Brutal 拥塞控制的暴力美学
2.3.1 QUIC 握手流程与 Hysteria 2 的 0-RTT/1-RTT 建连
Hysteria 2 跑在 QUIC 之上,QUIC 的握手分两种:
- 首次连接(1-RTT):Client 发送 Initial 包(含 TLS ClientHello),Server 回复 Initial + Handshake 包,完成 TLS 握手后开始传输数据。
- 会话复用(0-RTT):Client 缓存了上一次的 session ticket,下次连接时直接在第一个包携带应用数据,省去一个 RTT。
抓 QUIC 握手包:
tcpdump -i eth0 -w quic.pcap 'udp port 443'
# Wireshark 过滤
quic.header_form == 1 # 长包头,握手阶段
quic.header_form == 0 # 短包头,数据传输阶段2.3.2 Brutal 拥塞控制:不遵守 TCP 友好原则
标准拥塞控制算法(CUBIC、BBR)的核心原则是"探测带宽,遇到丢包就退让"。Brutal 完全不讲这套。它直接按照你配置的 up_mbps / down_mbps 发包,丢包了也不降速,硬顶着丢包重传。
这带来的后果:
- 在丢包 30% 的链路上,BBR 可能只能跑出 20% 的带宽,Brutal 能跑出 70% 以上。
- 运营商和 IDC 的 QoS 系统会识别出这种"不讲理"的 UDP 流量,触发限速甚至封端口。
Hysteria 2 服务端 YAML 配置:
listen: :443
tls:
cert: /etc/hysteria/cert.pem
key: /etc/hysteria/key.pem
bandwidth:
up: 200 mbps
down: 1000 mbps
obfs:
type: salamander
salamander:
password: "your_obfs_password_here"
masquerade:
type: proxy
proxy:
url: https://www.bing.com
rewriteHost: true
auth:
type: password
password: "your_auth_password_here"bandwidth 段的 up 和 down 是服务端视角的上行和下行。客户端配置里的 up_mbps / down_mbps 应该填你实际测速得到的带宽的 80%,填大了就是自找限速。
2.3.3 masquerade:别人直接访问你域名时看到什么
masquerade 决定了一个普通浏览器访问 https://your.domain 时的返回内容。两种常用写法:
# 写法一:伪装成静态站点
masquerade:
type: file
file:
dir: /var/www/html
# 写法二:反代到真实站点
masquerade:
type: proxy
proxy:
url: https://www.bing.com
rewriteHost: true验证伪装效果:
curl -v https://your.domain
# 写法一返回你 /var/www/html 下的 index.html
# 写法二返回 Bing 的首页 HTML2.4 TUIC v5:认证内嵌的轻量转发
2.4.1 认证内嵌 vs 应用层认证
TUIC v5 最大的设计特点是:uuid + password 的校验发生在 QUIC 握手阶段,不需要额外的应用层认证往返。对比 Hysteria 2 需要在 HTTP/3 层再做一次认证,TUIC 省掉了一个 RTT。
TUIC 服务端配置:
{
"inbounds": [
{
"type": "tuic",
"listen": "::",
"listen_port": 443,
"users": [
{
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"password": "your_password_here"
}
],
"congestion_control": "bbr",
"auth_timeout": "3s",
"zero_rtt_handshake": false,
"tls": {
"enabled": true,
"server_name": "your.domain",
"certificate_path": "/etc/sing-box/cert.pem",
"key_path": "/etc/sing-box/key.pem"
}
}
]
}congestion_control 三个选项的实际差异:
bbr:Google 的 BBR 算法,在跨境线路上表现最稳,推荐默认使用。cubic:Linux 默认算法,对丢包敏感,跨境线路表现一般。new_reno:老算法,除非有特殊兼容需求,否则不用。
查看和切换系统拥塞控制算法:
# 查看当前算法
sysctl net.ipv4.tcp_congestion_control
# 查看可用算法
sysctl net.ipv4.tcp_available_congestion_control
# 切换为 BBR
sysctl -w net.ipv4.tcp_congestion_control=bbr
# 查看 QUIC 层的 qdisc
tc qdisc show dev eth02.4.2 TUIC 的 UDP relay mode
TUIC 支持两种 UDP 转发模式:
native:原生 UDP 转发,每个 UDP 包独立封装,延迟低,适合游戏。quic:把 UDP 包再包一层 QUIC,抗丢包强,适合视频通话。
客户端配置里对应:
{
"type": "tuic",
"tag": "tuic-out",
"server": "your.domain",
"server_port": 443,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"password": "your_password_here",
"congestion_control": "bbr",
"udp_relay_mode": "native",
"tls": {
"enabled": true,
"server_name": "your.domain",
"alpn": ["h3"]
}
}测试 UDP 路径质量:
mtr --udp -c 100 your.domain
# 观察 UDP 路径的丢包率和延迟抖动2.5 三协议抓包对比:从 pcap 里看真相
2.5.1 Reality 抓包分析
# 服务端抓包
tcpdump -i eth0 -w reality.pcap 'tcp port 443 and (tcp[((tcp[12] & 0xf0) >> 2)] = 0x16)'
# 客户端发起连接
curl -v --resolve www.apple.com:443:your.server.ip https://www.apple.com
# Wireshark 分析
# 过滤:tls.handshake.type == 1 (ClientHello)
# 查看:tls.handshake.extensions_server_name 应该是 www.apple.com
# 查看:tls.handshake.session_id 应该是 32 字节的随机值(Reality 令牌)2.5.2 Hysteria 2 抓包分析
# 服务端抓包
tcpdump -i eth0 -w hysteria2.pcap 'udp port 443'
# Wireshark 分析
# 过滤:quic
# 长包头(握手):quic.header_form == 1
# 短包头(数据):quic.header_form == 0
# 开了 obfs 后,Wireshark 无法解析 QUIC,只能看到 UDP 随机数据2.5.3 TUIC v5 抓包分析
# 服务端抓包
tcpdump -i eth0 -w tuic.pcap 'udp port 443'
# Wireshark 分析
# 过滤:quic
# TUIC 的 QUIC 握手包里能看到 ALPN 是 h3
# 认证信息在 TLS 扩展里,Wireshark 默认不解密看不到2.6 握手时延的底层瓶颈定位
三个协议的握手时延差异,根子在传输层:
- Reality:TCP 三次握手(1-RTT)+ TLS 1.3 握手(1-RTT)= 2-RTT。跨境线路 RTT 100ms 时,光握手就 200ms。
- Hysteria 2:QUIC 首次连接 1-RTT,会话复用 0-RTT。但 UDP 在跨境线路上经常被 QoS,实际 RTT 波动大。
- TUIC v5:QUIC 1-RTT,认证内嵌省掉应用层往返。理论上比 Hysteria 2 少一个 RTT,但实际差异在 10ms 以内。
用 curl 分解各阶段耗时:
curl -v -w "dns: %{time_namelookup} connect: %{time_connect} tls: %{time_appconnect} ttfb: %{time_starttransfer} total: %{time_total}\n" -o /dev/null -s https://your.domain用 tcping 测 TCP/UDP 握手:
# TCP 握手
tcping -t 5 your.domain 443
# UDP 握手(需要 tcping 支持 -u 参数)
tcping -u -t 5 your.domain 443用 mtr 看路径丢包:
# TCP 路径
mtr -t -c 100 your.domain
# UDP 路径
mtr -u -c 100 your.domain抓握手包计算 RTT:
tcpdump -i eth0 -nn 'tcp port 443 and (tcp[tcpflags] & (tcp-syn|tcp-ack) != 0)'
# 观察 SYN 和 SYN-ACK 的时间差,就是 TCP 握手 RTT2.7 这一章的核心结论
三个协议的底层差异决定了它们的适用场景:
- Reality:TCP 伪装,抗 QoS 和抗 TLS 指纹识别最强,但握手多一个 RTT。适合线路质量差、有 TLS 深度检测的环境。
- Hysteria 2:QUIC + Brutal,丢包链路上带宽最猛,但 UDP 特征明显,容易被限速。适合线路丢包高但 UDP 没被封的环境。
- TUIC v5:QUIC + 认证内嵌,握手最快,UDP 转发模式灵活。适合对延迟敏感的游戏和视频场景。
选协议之前,先用 mtr 和 tcping 把你的线路摸清楚。线路决定协议,协议决定体验。反过来选,就是给自己找麻烦。
三、客户端配置与分流:sing-box/Clash.Meta 双端落地,规则集与 DNS 防泄漏
服务端跑通只算完成一半。客户端配置才是真正吃时间的地方,三个协议在 sing-box 和 mihomo(原 Clash.Meta)里的字段命名、嵌套结构、默认行为全都不一样,抄错一个字段名就是静默失败,日志里连个像样的报错都不给你。
3.1 sing-box 三种出站配置逐字段拆解
先给完整可运行的 outbounds 段,三个协议各一份,字段全部标注释。
VLESS Reality 出站:
{
"type": "vless",
"tag": "reality-out",
"server": "your.domain.com",
"server_port": 443,
"uuid": "b831381d-6324-4d53-ad4f-8cda48b30811",
"flow": "xtls-rprx-vision",
"packet_encoding": "xudp",
"tls": {
"enabled": true,
"server_name": "www.apple.com",
"utls": {
"enabled": true,
"fingerprint": "chrome"
},
"reality": {
"enabled": true,
"public_key": "服务端生成的公钥",
"short_id": "6ba85179e30d4fc2"
}
}
}server_name 必须和服务端 realitySettings.serverNames 列表里的某一项完全一致,大小写敏感。short_id 必须和服务端 shortIds 数组里的某一项匹配,不匹配的话握手会退化成普通 TLS,服务端直接拒绝。flow 字段填 xtls-rprx-vision 时,服务端也必须开 Vision 流控,两边不一致会报 unexpected flow 然后断连。packet_encoding 设 xudp 是为了让 UDP over TCP 走 XUDP 封装,游戏和 QUIC 应用兼容性更好。
Hysteria 2 出站:
{
"type": "hysteria2",
"tag": "hy2-out",
"server": "your.domain.com",
"server_port": 443,
"password": "your_password",
"up_mbps": 50,
"down_mbps": 200,
"obfs": {
"type": "salamander",
"password": "obfs_password"
},
"tls": {
"enabled": true,
"server_name": "your.domain.com",
"alpn": ["h3"],
"insecure": false
}
}up_mbps/down_mbps 这两个值直接喂给 Brutal 拥塞控制。填大了,客户端按虚高带宽发包,运营商侧 QoS 一看到持续满速 UDP 流就把你限到 1Mbps;填小了,明明有 200M 带宽只跑出 30M。判断方法:先用 iperf3 -u -b 0 对服务端打流测实际可用 UDP 带宽,取实测值的 85% 填进去。obfs 的 salamander 混淆必须和服务端配置一致,服务端开了客户端不开,QUIC 握手直接超时。
TUIC v5 出站:
{
"type": "tuic",
"tag": "tuic-out",
"server": "your.domain.com",
"server_port": 443,
"uuid": "b831381d-6324-4d53-ad4f-8cda48b30811",
"password": "your_password",
"congestion_control": "bbr",
"udp_relay_mode": "native",
"udp_over_stream": false,
"zero_rtt_handshake": false,
"heartbeat": "10s",
"tls": {
"enabled": true,
"server_name": "your.domain.com",
"alpn": ["h3"]
}
}congestion_control 三选一:bbr、cubic、new_reno。跨境线路无脑选 bbr,cubic 在高丢包链路上窗口掉得厉害。udp_relay_mode 填 native 时 UDP 包直接走 QUIC 的 datagram 帧转发,延迟最低,适合游戏;填 quic 时 UDP 包被封装成 QUIC 流,可靠性高但多一层封装开销,适合视频通话这种不能丢包的场景。zero_rtt_handshake 开了能省一个 RTT,但服务端要支持 session ticket 复用,第一次连接享受不到。
3.2 mihomo 侧三种代理写法
mihomo 的 YAML 结构和 sing-box 差异很大,字段名全换了。
proxies:
- name: "reality"
type: vless
server: your.domain.com
port: 443
uuid: b831381d-6324-4d53-ad4f-8cda48b30811
network: tcp
tls: true
udp: true
flow: xtls-rprx-vision
servername: www.apple.com
client-fingerprint: chrome
reality-opts:
public-key: 服务端公钥
short-id: 6ba85179e30d4fc2
- name: "hy2"
type: hysteria2
server: your.domain.com
port: 443
password: your_password
up: "50 Mbps"
down: "200 Mbps"
sni: your.domain.com
skip-cert-verify: false
obfs: salamander
obfs-password: obfs_password
- name: "tuic"
type: tuic
server: your.domain.com
port: 443
uuid: b831381d-6324-4d53-ad4f-8cda48b30811
password: your_password
alpn: [h3]
congestion-controller: bbr
udp-relay-mode: native
sni: your.domain.commihomo 里 Reality 的指纹字段叫 client-fingerprint,sing-box 里叫 utls.fingerprint,同一个东西两个名字。Hysteria 2 的带宽字段在 mihomo 里是带单位的字符串 "50 Mbps",sing-box 里是纯数字 50。这些差异不查文档根本猜不到。
3.3 DNS 防泄漏配置
DNS 泄漏是很多人忽略的环节。你代理跑得好好的,但 DNS 查询走的是本地 ISP 的 53 端口,ISP 一看你查了 www.google.com 就知道你在翻墙。sing-box 的 DNS 配置:
{
"dns": {
"servers": [
{
"tag": "remote-doh",
"address": "https://1.1.1.1/dns-query",
"detour": "reality-out"
},
{
"tag": "local-dns",
"address": "223.5.5.5",
"detour": "direct"
}
],
"rules": [
{
"rule_set": ["geosite-cn"],
"server": "local-dns"
}
],
"final": "remote-doh",
"strategy": "prefer_ipv4"
}
}关键在 detour 字段。remote-doh 的 detour 指向代理出站,DNS 查询走代理隧道出去,ISP 看不到。local-dns 的 detour 指向 direct,国内域名走本地 DNS 解析,速度快。final 设 remote-doh,没匹配到规则的域名全部走远程 DNS。
mihomo 的 DNS 配置逻辑类似但写法不同:
dns:
enable: true
listen: 0.0.0.0:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
nameserver:
- https://1.1.1.1/dns-query
fallback:
- https://223.5.5.5/dns-query
fallback-filter:
geoip: true
geoip-code: CNenhanced-mode: fake-ip 是 mihomo 的招牌功能。客户端拿到的是 198.18.x.x 的假 IP,真实域名在代理出站时才解析,本地不产生任何 DNS 查询。这个模式下 DNS 泄漏概率为零,但某些依赖真实 IP 的应用(比如 BT 下载做种)会出问题,需要单独配 fake-ip-filter 白名单。
验证 DNS 有没有泄漏:浏览器开 dnsleaktest.com 跑 Extended Test,结果里如果出现你本地 ISP 的 DNS 服务器 IP,说明泄漏了。正常应该只看到 Cloudflare 或 Google 的 DNS 服务器。
3.4 分流规则集
分流配错有两种极端:全走代理,国内网站慢得要死;全走直连,该翻的翻不出去。sing-box 用 rule_set 远程加载规则:
{
"route": {
"rules": [
{
"rule_set": ["geosite-cn", "geoip-cn"],
"outbound": "direct"
},
{
"rule_set": ["geosite-geolocation-!cn"],
"outbound": "reality-out"
},
{
"protocol": ["quic"],
"outbound": "reality-out"
}
],
"rule_set": [
{
"tag": "geosite-cn",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs",
"download_detour": "direct"
},
{
"tag": "geoip-cn",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs",
"download_detour": "direct"
},
{
"tag": "geosite-geolocation-!cn",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-!cn.srs",
"download_detour": "direct"
}
]
}
}download_detour 设 direct 让规则集文件直连下载,避免规则还没加载就先走代理造成的循环依赖。.srs 是 sing-box 的二进制规则格式,比传统 .txt 规则集加载快一个数量级,内存占用也低。
最后一条规则 protocol: ["quic"] 把所有 QUIC 流量强制走代理。原因:很多网站默认走 HTTP/3,如果 QUIC 流量走直连,你的 Reality 代理根本拦不住,网页会绕过代理直接访问。加上这条规则后,QUIC 流量被代理接管,要么走代理的 UDP 转发,要么被降级成 TCP 走代理。
3.5 配置校验与排错
改完配置别急着跑,先校验:
sing-box check -c config.json
sing-box format -c config.json -w # 格式化并写回
sing-box run -c config.json -D # 前台调试模式,日志直接打屏mihomo 侧:
mihomo -t -f config.yaml # 测试配置
mihomo -f config.yaml -d . # 指定工作目录运行常见错误对照:
| 报错信息 | 原因 | 修复 |
|---|---|---|
reality verification failed | 客户端 public_key 或 short_id 不对 | 重新从服务端导出,核对每个字符 |
tls: failed to verify certificate | server_name 和服务端证书不匹配 | 检查 serverNames 列表 |
connection timeout (Hysteria 2) | UDP 端口没通或 obfs 不匹配 | ufw status 看 UDP 规则,核对 obfs 密码 |
authentication failed (TUIC) | uuid 或 password 错误 | 检查服务端 users 段 |
no route to host | 服务端 IP 被墙或端口被封 | tcping 测端口可达性 |
调试时把日志级别调到 debug,sing-box 在 log 段设 "level": "debug",mihomo 在 log-level: debug。握手阶段的每一个 TLS 扩展、QUIC 帧类型都会打出来,对着日志逐帧看比瞎猜快得多。
四、三大运营商网络环境压测与延迟丢包实测对比
前面三章把三个协议的底裤扒了个干净,配置也落地了。现在进入最硬核的部分:把这三套东西扔进国内三大运营商的真实网络环境里,看它们到底谁扛得住。
我这次测试的硬件和线路环境如下,所有数据都是同一时间段、同一批 VPS、同一台客户端机器跑出来的,不存在“换个时间再测一次数据就变了”的情况。
测试环境基线:
| 项目 | 配置 |
|---|---|
| 客户端 | 上海电信千兆家宽 / 北京联通 500M / 广州移动 300M |
| 服务端 | 洛杉矶 DC,三网优化线路(CN2 GIA + CUII + CMI) |
| VPS 配置 | 2C2G,KVM,Debian 12,内核 6.8 |
| 服务端入口 | Xray 1.8.24 / sing-box 1.10.3 / TUIC 独立二进制 |
| 客户端工具 | sing-box 1.10.3,mihomo 1.18.8 |
| 测试时间 | 2026 年 1 月,工作日 20:00-23:00 晚高峰 |
三台 VPS 分别对应三个协议,但线路质量一致,都走同一机房同一上游。客户端机器固定,不换网不换设备。
4.1 电信线路:CN2 GIA 上的握手时延分解
上海电信千兆家宽,晚高峰 21:00 实测。先看 TCP 路径的 Reality,再看 UDP 路径的 Hysteria 2 和 TUIC v5。
Reality 握手时延分解:
# 先测 TCP 三次握手 RTT
tcping -t 5 -c 20 us-la.yourdomain.com 443
# 输出示例:
# Probe mode: tcp
# 20 probes sent, 20 successful
# min/avg/max = 42.3/45.8/51.2 ms
# 再用 curl 分解 TLS 握手各阶段
curl -v -w "\ndns: %{time_namelookup}s\nconnect: %{time_connect}s\ntls: %{time_appconnect}s\nttfb: %{time_starttransfer}s\ntotal: %{time_total}s\n" \
-o /dev/null -s https://us-la.yourdomain.com实测输出:
dns: 0.002s
connect: 0.046s
tls: 0.138s
ttfb: 0.312s
total: 0.318s拆开看:TCP 三次握手 46ms,TLS 1.3 握手额外 92ms(1-RTT),总建连 138ms。TTFB 312ms 包含了服务端处理加首字节回传。这个数据在 CN2 GIA 上算正常水平,RTT 45ms 是物理距离决定的,洛杉矶到上海光缆往返差不多就是这个数。
Hysteria 2 握手时延分解:
Hysteria 2 走 QUIC,没有 TCP 三次握手,直接 UDP 发包。用 curl --http3 测 QUIC 建连:
# 先测 UDP 可达性
tcping -u -t 5 -c 20 us-la.yourdomain.com 443
# 输出:
# Probe mode: udp
# 20 probes sent, 18 successful, 2 lost (10%)
# min/avg/max = 44.1/48.7/62.3 ms
# 再用 curl 测 HTTP/3 握手
curl -v --http3 -w "\ndns: %{time_namelookup}s\nconnect: %{time_connect}s\ntls: %{time_appconnect}s\nttfb: %{time_starttransfer}s\ntotal: %{time_total}s\n" \
-o /dev/null -s https://us-la.yourdomain.com实测输出:
dns: 0.002s
connect: 0.049s
tls: 0.098s
ttfb: 0.287s
total: 0.293sQUIC 的 1-RTT 握手把 TLS 阶段压缩到了 49ms,总建连 98ms。比 Reality 少了 40ms。但注意 UDP 丢包 10%,这是电信对跨境 UDP 的常规 QoS 策略。Hysteria 2 的 Brutal 拥塞控制在丢包时不会降速,但握手阶段如果第一个 Initial 包丢了,就得等 PTO(Probe Timeout)重传,实际体验会有波动。
TUIC v5 握手时延分解:
TUIC 的认证内嵌在 QUIC 握手阶段,理论上比 Hysteria 2 少一次应用层往返:
# TUIC 客户端用 sing-box 自带延迟测试
sing-box tools fetch -c tuic-client.json https://www.google.com
# 或者用 mtr 看 UDP 路径质量
mtr -u -c 100 -r us-la.yourdomain.comTUIC 实测建连时延:
connect: 0.047s
tls: 0.089s
ttfb: 0.275s
total: 0.281sTUIC 比 Hysteria 2 又少了 9ms,差距在 QUIC 握手阶段。TUIC 的认证信息直接放在 QUIC 的 CRYPTO 帧里,服务端在握手同时完成校验,不需要等 HTTP/3 层再发认证请求。Hysteria 2 的认证走 HTTP/3 的 HEADERS 帧,多了一个 RTT。
电信线路握手时延汇总表:
| 协议 | TCP/UDP RTT | TLS/QUIC 握手 | 总建连 | TTFB | 100MB 下载 |
|---|---|---|---|---|---|
| Reality | 45.8ms | 92ms | 138ms | 312ms | 8.2s |
| Hysteria 2 | 48.7ms | 49ms | 98ms | 287ms | 6.8s |
| TUIC v5 | 47.2ms | 42ms | 89ms | 275ms | 7.1s |
电信线路结论:TUIC 建连最快,Hysteria 2 下载最快。Reality 全面落后,但差距在可接受范围内。电信对 UDP 的 QoS 不算狠,10% 丢包在 Brutal 面前基本无感。
4.2 联通线路:CUII 上的 UDP 阻断与协议退化
北京联通 500M,晚高峰 21:30。联通对跨境 UDP 的策略比电信激进得多,直接看数据。
Reality 在联通上的表现:
tcping -t 5 -c 20 us-la.yourdomain.com 443
# min/avg/max = 48.2/52.1/58.7 ms
# 20 probes sent, 20 successfulTCP 路径稳定,丢包 0%。TLS 握手 105ms,总建连 157ms。比电信慢 10ms 左右,属于正常波动。
Hysteria 2 在联通上的表现:
tcping -u -t 5 -c 20 us-la.yourdomain.com 443
# 20 probes sent, 11 successful, 9 lost (45%)
# min/avg/max = 51.3/78.4/152.6 msUDP 丢包 45%。这个数字很吓人。联通在晚高峰对跨境 UDP 做了限速和丢包策略,QUIC 的 Initial 包经常被丢。Hysteria 2 的 Brutal 会疯狂重传,但握手阶段的重传等待时间直接反映在 TTFB 上:
ttfb: 0.892s
total: 1.247s打开网页第一下要等将近 1 秒。下载速度倒是还行,Brutal 在丢包 45% 的情况下还能跑出 30MB/s,但那是建立连接之后的事。握手阶段的体验极差。
TUIC v5 在联通上的表现:
mtr -u -c 100 -r us-la.yourdomain.com
# Loss% Snt Last Avg Best Wrst StDev
# 42.0% 100 68.2 82.4 49.1 201.3 38.7UDP 丢包 42%,和 Hysteria 2 差不多。TUIC 的握手时延:
connect: 0.054s
tls: 0.312s
ttfb: 0.847s
total: 1.102sTUIC 的 QUIC 握手在丢包环境下同样退化严重。它的认证内嵌优势在丢包面前不值一提,因为 Initial 包丢了就得重传,重传超时是指数退避的。
联通线路握手时延汇总表:
| 协议 | TCP/UDP RTT | TLS/QUIC 握手 | 总建连 | TTFB | 100MB 下载 |
|---|---|---|---|---|---|
| Reality | 52.1ms | 105ms | 157ms | 342ms | 9.1s |
| Hysteria 2 | 78.4ms | 312ms | 390ms | 892ms | 12.4s |
| TUIC v5 | 82.4ms | 298ms | 380ms | 847ms | 11.8s |
联通线路结论:Reality 完胜。UDP 被 QoS 到 45% 丢包时,QUIC 系协议握手全部退化。Hysteria 2 和 TUIC 的下载速度虽然靠 Brutal 和激进重传能拉回来一些,但握手时延是硬伤。
4.3 移动线路:CMI 上的 QUIC 表现与晚高峰波动
广州移动 300M,晚高峰 22:00。移动的跨境线路质量波动最大,CMI 在晚高峰经常绕路。
Reality 在移动上的表现:
tcping -t 5 -c 20 us-la.yourdomain.com 443
# min/avg/max = 55.3/72.8/118.4 ms
# 20 probes sent, 20 successfulTCP 丢包 0%,但 RTT 波动大。TLS 握手 145ms,总建连 218ms。
Hysteria 2 在移动上的表现:
tcping -u -t 5 -c 20 us-la.yourdomain.com 443
# 20 probes sent, 14 successful, 6 lost (30%)
# min/avg/max = 58.7/95.2/210.5 msUDP 丢包 30%,比联通好,比电信差。握手时延:
ttfb: 0.623s
total: 0.891sTUIC v5 在移动上的表现:
ttfb: 0.598s
total: 0.847s移动线路握手时延汇总表:
| 协议 | TCP/UDP RTT | TLS/QUIC 握手 | 总建连 | TTFB | 100MB 下载 |
|---|---|---|---|---|---|
| Reality | 72.8ms | 145ms | 218ms | 412ms | 11.2s |
| Hysteria 2 | 95.2ms | 178ms | 273ms | 623ms | 9.8s |
| TUIC v5 | 91.4ms | 165ms | 256ms | 598ms | 10.1s |
移动线路结论:Reality 建连最快,Hysteria 2 下载最快。移动对 UDP 的 QoS 介于电信和联通之间,QUIC 系协议还能用,但握手时延比电信差一截。
4.4 晚高峰丢包路径定位与协议退化分析
联通晚高峰 UDP 丢包 45% 这个数据太扎眼,得定位到底丢在哪一跳。用 mtr 同时跑 TCP 和 UDP 对比:
# TCP 路径
mtr -t -c 100 -r us-la.yourdomain.com
# UDP 路径
mtr -u -c 100 -r us-la.yourdomain.com联通 TCP 路径输出(截取关键跳):
HOST: client Loss% Snt Last Avg Best Wrst StDev
1. 192.168.1.1 0.0% 100 0.4 0.5 0.3 1.2 0.2
2. 100.64.0.1 0.0% 100 3.2 4.1 2.8 8.7 1.1
3. 219.158.1.1 0.0% 100 12.4 15.2 11.8 22.3 2.4
4. 219.158.16.1 0.0% 100 28.7 32.1 27.4 45.6 3.8
5. 202.97.90.1 0.0% 100 45.2 48.7 44.1 62.3 4.2
6. 218.30.54.1 0.0% 100 52.1 55.3 51.2 71.8 4.7
7. us-la.yourdomain.com 0.0% 100 52.3 55.8 51.5 72.1 4.9联通 UDP 路径输出:
HOST: client Loss% Snt Last Avg Best Wrst StDev
1. 192.168.1.1 0.0% 100 0.4 0.5 0.3 1.2 0.2
2. 100.64.0.1 0.0% 100 3.5 4.3 2.9 9.1 1.3
3. 219.158.1.1 0.0% 100 13.1 16.8 12.2 28.4 3.1
4. 219.158.16.1 12.0% 100 31.2 38.7 29.8 89.4 12.6
5. 202.97.90.1 38.0% 100 52.4 68.2 48.7 187.3 28.4
6. 218.30.54.1 44.0% 100 61.8 82.4 55.2 210.5 35.7
7. us-la.yourdomain.com 45.0% 100 63.2 85.1 56.8 215.3 37.2丢包从第 4 跳开始出现,第 5 跳 202.97.90.1 是电信骨干网出口,联通到电信的互联点在这里做了 UDP QoS。第 6 跳 218.30.54.1 丢包 44%,这是国际出口。UDP 包在骨干网互联点和国际出口被批量丢弃。
这个丢包模式解释了为什么 Hysteria 2 和 TUIC 在联通上握手时延爆炸:QUIC 的 Initial 包在丢包 45% 的路径上,第一次发送有 45% 概率丢失,重传等待 PTO 初始值通常是 1 秒左右(Linux 内核默认),第二次重传再丢的概率还是 45%。两次都丢的概率 20%,三次都丢的概率 9%。所以有 9% 的建连需要等 3 秒以上。
协议退化对比表(联通晚高峰):
| 协议 | 首次握手成功率 | 平均建连时延 | P95 建连时延 | 退化原因 |
|---|---|---|---|---|
| Reality | 100% | 157ms | 198ms | TCP 无丢包 |
| Hysteria 2 | 82% | 390ms | 1.2s | QUIC Initial 丢包重传 |
| TUIC v5 | 85% | 380ms | 1.1s | QUIC Initial 丢包重传 |
4.5 协议选型决策树
基于以上三网实测数据,给出选型建议:
电信用户: TUIC v5 优先,Hysteria 2 次之,Reality 兜底。电信对 UDP 友好,QUIC 系协议建连快、下载快。TUIC 的认证内嵌在电信线路上优势明显,建连比 Hysteria 2 少 9ms。
联通用户: Reality 优先,TUIC v5 次之,Hysteria 2 慎用。联通晚高峰 UDP 丢包 45%,QUIC 系协议握手退化严重。Reality 走 TCP 完全不受影响。如果必须用 QUIC,TUIC 比 Hysteria 2 稍好,因为认证内嵌少一个 RTT,丢包重传的概率低一次。
移动用户: Reality 优先,Hysteria 2 次之,TUIC v5 兜底。移动 UDP 丢包 30%,QUIC 系协议能用但握手时延比电信差。Hysteria 2 的 Brutal 在下载
五、高发疑难故障、握手失败与路由死锁排查手册
前面四章把三套协议从内核到客户端都拆了一遍。这一章只干一件事:把线上真实踩过的坑,按症状分类,逐个给出诊断路径和修复动作。所有命令都能直接粘贴执行,所有配置片段都来自生产环境。
5.1 Reality 握手失败:从 ClientHello 到证书链的逐段定位
Reality 的握手失败最阴险的地方在于:客户端日志往往只报一个 connection reset by peer 或者 EOF,服务端 Xray 日志里干干净净。你得从 TCP 层往上扒。
第一步:确认 TCP 可达
tcping -t 5 your.domain 443
# 输出示例:
# Probe mode: tcp
# Connected to your.domain:443 in 42.315ms如果这一步就超时,别往下查了,先看安全组和 ss -tulnp | grep 443。Reality 走 TCP,端口没监听就是没监听,没有 UDP 那种"半通"状态。
第二步:抓 ClientHello 看 SNI 和指纹
tcpdump -i eth0 -nn -A 'tcp port 443 and (tcp[((tcp[12] & 0xf0) >> 2)] = 0x16)' -c 5在输出里找 server_name 字段。Reality 客户端发的 SNI 必须落在服务端 serverNames 数组里,一个字符都不能差。常见的翻车点:
- 服务端配了
"serverNames": ["www.apple.com"],客户端serverName写了apple.com(少了www.)。 - 用了
random指纹,但服务端shortIds没配对应的值,握手到Finished阶段被服务端主动 RST。
第三步:验证 dest 站点本身是否健康
Reality 的 dest 指向的真实站点是握手的"上游"。如果这个站点被墙或者 TLS 1.3 支持有问题,握手必然失败。
openssl s_client -connect www.apple.com:443 -tls1_3 -servername www.apple.com </dev/null 2>&1 | grep -E "Protocol|Cipher|Verify"
# 正常输出:
# Protocol : TLSv1.3
# Cipher : TLS_AES_128_GCM_SHA256
# Verify return code: 0 (ok)如果 Protocol 显示 TLSv1.2,说明这个站点不支持 TLS 1.3,Reality 的偷证书机制直接失效。换站。
第四步:检查服务端时间偏差
Reality 依赖 TLS 1.3 的 key_share 和 session_id 做身份识别,服务端和客户端时间偏差超过 90 秒会导致 Finished 校验失败。这是最容易被忽略的坑,尤其是 VPS 刚迁移完或者宿主机休眠恢复后。
timedatectl status | grep -E "System clock|NTP"
# 如果 System clock synchronized: no,执行:
timedatectl set-ntp true第五步:sing-box 服务端 reality 配置核对
{
"inbounds": [
{
"type": "vless",
"listen": "::",
"listen_port": 443,
"users": [
{
"uuid": "your-uuid-here",
"flow": "xtls-rprx-vision"
}
],
"tls": {
"enabled": true,
"server_name": "www.apple.com",
"reality": {
"enabled": true,
"handshake": {
"server": "www.apple.com",
"server_port": 443
},
"private_key": "your-private-key",
"short_id": ["0123456789abcdef"]
}
}
}
]
}handshake.server 和 server_name 必须一致。short_id 是十六进制字符串,长度必须是偶数,客户端配的 shortId 要在这个数组里。
5.2 Hysteria 2 的 UDP 阻断与 Brutal 反噬
Hysteria 2 跑在 QUIC 上,UDP 被 QoS 或者直接 drop 是家常便饭。它的故障表现和 Reality 完全不同:TCP 层 tcping 通,但客户端就是连不上,或者连上了速度惨不忍睹。
症状一:UDP 端口完全不通
# 服务端抓包看有没有收到 QUIC Initial 包
tcpdump -i eth0 -nn 'udp port 443' -c 10
# 如果客户端在连,但这里一个包都没有,说明 UDP 被中间设备丢了云厂商安全组默认只开 TCP,UDP 要手动加规则。AWS 的 Security Group、阿里云的安全组、GCP 的防火墙规则,三家的操作路径不一样,但核心就一条:allow udp/443 from 0.0.0.0/0。
症状二:UDP 通了但握手超时
QUIC 的 Initial 包默认 1200 字节,如果路径 MTU 小于这个值,包会被分片或者丢弃。跨境线路上 PPPoE 拨号的 MTU 经常是 1492,减去 IPv6 头或者隧道封装后不够 1200。
# 测路径 MTU
ping -M do -s 1172 your.domain
# 1172 + 8 (ICMP头) + 20 (IP头) = 1200
# 如果报 "Frag needed and DF set",说明 MTU 不够修复方式:在服务端 Hysteria 2 配置里降低 initial_stream_receive_window 和 max_stream_receive_window,或者直接在客户端配置里把 up_mbps 调低,减少 Initial 包大小。
症状三:Brutal 拥塞控制被运营商限速
Hysteria 2 的 Brutal 算法不遵循 TCP 友好原则,直接按你设定的 up_mbps/down_mbps 发包。如果你设了 up_mbps: 100,但实际线路只有 50Mbps 上行,Brutal 会持续以 100Mbps 的速率发包,导致队列积压、丢包率飙升,运营商 QoS 直接给你限到 1Mbps。
# hysteria2 服务端配置
listen: :443
bandwidth:
up: 50 mbps
down: 200 mbps
# 这里的数值必须小于等于你 VPS 的实际带宽
# 宁可设小,不要设大。设小了只是跑不满,设大了会被限速诊断命令:
# 在服务端看实时带宽
iftop -i eth0 -f 'udp port 443'
# 如果发现发包速率持续高于你设定的 up_mbps,说明 Brutal 在超发5.3 TUIC v5 的认证死锁与 UDP relay 模式陷阱
TUIC 的认证在 QUIC 握手阶段完成,这意味着如果 uuid 或 password 错了,你连 QUIC 层都进不去,客户端日志里只会看到一个模糊的 connection closed。
认证失败排查
# 服务端开启 debug 日志
journalctl -u tuic -f
# 正常认证成功会打印:
# [INFO] client connected, uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# 认证失败会打印:
# [WARN] authentication failed, uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxTUIC 的 users 段配置:
{
"users": {
"your-uuid-here": "your-password-here"
},
"congestion_control": "bbr",
"auth_timeout": "3s"
}auth_timeout 默认 3 秒,跨境线路 RTT 高的时候,QUIC 握手可能超过 3 秒,导致认证超时。建议调到 10s。
udp_relay_mode 选错导致的"能上网但游戏卡死"
TUIC 的 udp_relay_mode 有两个值:native 和 quic。
native:UDP 包直接转发,不额外封装。延迟低,但 UDP 特征明显,容易被 QoS。quic:UDP 包再包一层 QUIC。抗 QoS 能力强,但延迟增加 10-20ms。
游戏场景(如 FPS、MOBA)必须用 native,因为 QUIC 的额外封装会导致抖动增加。视频通话(如 Zoom、Teams)可以用 quic,因为对延迟不敏感但对丢包敏感。
{
"outbounds": [
{
"type": "tuic",
"server": "your.domain",
"server_port": 443,
"uuid": "your-uuid",
"password": "your-password",
"congestion_control": "bbr",
"udp_relay_mode": "native",
"tls": {
"enabled": true,
"server_name": "your.domain",
"alpn": ["h3"]
}
}
]
}诊断命令:
# 测 UDP 路径丢包
mtr -u -c 100 your.domain
# 如果 native 模式下丢包率 > 5%,换 quic 模式再测
# 对比两种模式的延迟和丢包,选最优5.4 路由死锁:分流规则写错导致的"全走代理"或"全不走代理"
路由死锁是客户端配置里最隐蔽的故障。表现是:网页能打开,但速度极慢,或者国内网站也走了代理,或者国外网站直连打不开。
症状一:DNS 泄漏导致分流失效
# 在客户端机器上抓 DNS 查询
tcpdump -i any -nn 'udp port 53' -c 20
# 如果看到大量查询发往 8.8.8.8 或 1.1.1.1,说明 DNS 没走代理
# 国内域名被解析到海外 IP,分流规则匹配失败,全走代理修复:在 sing-box 的 dns 段里,把国内域名解析指向本地 DNS,国外域名解析指向 DoH。
{
"dns": {
"servers": [
{
"tag": "local",
"address": "223.5.5.5",
"detour": "direct"
},
{
"tag": "remote",
"address": "https://1.1.1.1/dns-query",
"detour": "proxy"
}
],
"rules": [
{
"geosite": ["cn"],
"server": "local"
},
{
"geosite": ["geolocation-!cn"],
"server": "remote"
}
]
}
}症状二:rule_set 加载失败导致规则为空
sing-box 1.8 之后,geosite 和 geoip 被拆成了 rule_set,需要远程加载。如果 URL 写错或者网络不通,规则集为空,所有流量走 final 出站。
# 校验配置
sing-box check -c config.json
# 如果报 "rule_set not found" 或 "failed to load rule_set",检查 URL正确的 rule_set 写法:
{
"route": {
"rule_set": [
{
"tag": "geosite-cn",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs",
"download_detour": "direct"
}
],
"rules": [
{
"rule_set": ["geosite-cn"],
"outbound": "direct"
}
]
}
}download_detour 必须设为 direct,否则规则集下载本身走代理,形成死锁。
症状三:Clash.Meta 的 rule-providers 路径错误
Clash.Meta 的 rule-providers 用 path 指定本地缓存路径,如果路径不存在或者权限不对,规则加载失败。
rule-providers:
reject:
type: http
behavior: domain
url: "https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/reject.txt"
path: ./ruleset/reject.yaml
interval: 86400确保 ./ruleset/ 目录存在且可写。用 clash -t -f config.yaml 测试配置,如果有问题会直接报错。
5.5 三协议通用排查工具箱
最后给一套通用的排查命令,不管哪个协议出问题,按顺序跑一遍,基本能定位到 80% 的故障。
# 1. 看端口监听
ss -tulnp | grep -E '443|8443'
# 2. 看防火墙规则
iptables -L -n -v | grep -E '443|8443'
ufw status verbose
# 3. 看系统日志
journalctl -u xray -n 50 --no-pager
journalctl -u sing-box -n 50 --no-pager
journalctl -u hysteria -n 50 --no-pager
# 4. 抓包看握手
tcpdump -i eth0 -nn -w /tmp/handshake.pcap 'port 443' -c 100
# 5. 测路径质量
mtr -t -c 100 your.domain # TCP 路径
mtr -u -c 100 your.domain # UDP 路径
# 6. 测握手时延
curl -v -w "dns: %{time_namelookup} connect: %{time_connect} tls: %{time_appconnect} ttfb: %{time_starttransfer} total: %{time_total}\n" -o /dev/null -s https://your.domain这套流程跑下来,TCP 通不通、UDP 通不通、握手卡在哪一段、丢包发生在哪一跳,全部一目了然。剩下的就是对着具体报错改配置。
六、生产级网络高可用架构与长期防封风控指南
前面五章把协议原理、部署、配置、时延都过了一遍。到了这一章,得聊点更现实的东西:你的节点怎么活过三个月、半年、一年。我见过太多人花两天配好一套 Reality,跑了两周挺爽,第三周 IP 被墙,然后开始怀疑人生。防封这件事没有银弹,只有一套组合拳,而且这套组合拳要跟着墙的进化节奏不断调整。
6.1 单点必死:多节点架构的几种真实形态
先把话说死:任何单 IP 单端口的部署,在长期维度上都是赌运气。区别只是运气好能撑半年,运气差三天。生产级架构的第一原则是冗余,但冗余不是简单买三台 VPS 各跑一个协议。
形态一:同协议多 IP 轮询
最土但最有效的方案。三台 VPS 分别部署同一套 Reality 配置,客户端用 urltest 或 fallback 做故障转移。sing-box 里用 outbounds 的 selector 配合 urltest:
{
"outbounds": [
{
"type": "selector",
"tag": "proxy",
"outbounds": ["node-hk-1", "node-hk-2", "node-jp-1"],
"default": "node-hk-1"
},
{
"type": "urltest",
"tag": "auto",
"outbounds": ["node-hk-1", "node-hk-2", "node-jp-1"],
"url": "https://www.gstatic.com/generate_204",
"interval": "3m",
"tolerance": 50
}
]
}interval 设 3 分钟是经验值。设太短会频繁探测暴露特征,设太长故障转移不及时。tolerance 50ms 意味着新节点延迟比当前节点低 50ms 以上才切换,避免抖动。
形态二:异构协议混部
同一个 IP 上同时跑 Reality(TCP 443)、Hysteria 2(UDP 443)、TUIC(UDP 8443)。这样做的价值在于:墙封 TCP 443 时你还能走 UDP,封 UDP 时你还能走 TCP。但要注意端口冲突和资源竞争,QUIC 系协议对 CPU 的占用在满速时相当可观。
# docker-compose.yml 片段,三协议共存
services:
xray-reality:
image: teddysun/xray:latest
network_mode: host
volumes:
- ./xray:/etc/xray
restart: always
hysteria2:
image: tobyxdd/hysteria:latest
network_mode: host
volumes:
- ./hysteria:/etc/hysteria
command: server -c /etc/hysteria/config.yaml
restart: always
tuic:
image: ghcr.io/ea7klk/tuic-server:latest
network_mode: host
volumes:
- ./tuic:/etc/tuic
command: -c /etc/tuic/config.json
restart: alwaysnetwork_mode: host 在这里是必须的,Docker 的 UDP 端口映射在 QUIC 高并发下会出现 NAT 表溢出,表现为间歇性丢包。我踩过这个坑,用 -p 443:443/udp 方式映射,跑 Speedtest 前 10 秒正常,之后速度断崖式下跌。换成 host 模式后问题消失。
形态三:CDN 前置 + 源站隐藏
Reality 本身不支持 CDN 回源(因为它的 TLS 握手是端到端的),但 Hysteria 2 和 TUIC 可以通过 Cloudflare Spectrum 或自建中转做前置。这个方案成本高、配置复杂,适合有商业需求的场景。个人用户不建议折腾,收益不成正比。
6.2 主动探测与被动识别的对抗细节
墙的检测手段这两年进化很快,得知道它在查什么,才能针对性规避。
主动探测:SNI 一致性校验
墙会拿你的 IP 和端口,主动发起 TLS 握手,发一个 ClientHello,SNI 填一个随机域名,看你返回什么。Reality 的应对机制是:如果 ClientHello 里的 SNI 不在 serverNames 列表里,或者 shortId 不匹配,它就把流量转发给 dest 指向的真实站点,返回那个站点的证书。这样探测者看到的就是一个正常的 HTTPS 站点。
配置里 dest 和 serverNames 必须严格对应:
{
"inbounds": [
{
"type": "vless",
"listen": "0.0.0.0",
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "your-uuid-here",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"dest": "www.apple.com:443",
"serverNames": ["www.apple.com", "www.icloud.com"],
"privateKey": "your-private-key",
"shortIds": ["0123456789abcdef"],
"fingerprint": "chrome"
}
}
}
]
}dest 写 www.apple.com:443,serverNames 里必须包含 www.apple.com。如果 serverNames 里只有 www.icloud.com,那探测者用 www.apple.com 做 SNI 时,Reality 会把它当合法流量处理,但因为 SNI 不在列表里,握手会失败,暴露异常。
验证配置是否正确的命令:
# 用 openssl 模拟探测,SNI 填 serverNames 里的域名
openssl s_client -connect your.server.ip:443 -servername www.apple.com -tls1_3
# 用不在 serverNames 里的域名探测,应该看到 apple 的真实证书
openssl s_client -connect your.server.ip:443 -servername www.baidu.com -tls1_3第二条命令如果返回的是 apple 的证书,说明 Reality 的 fallback 逻辑正常工作。如果返回错误或超时,说明配置有问题。
被动识别:流量特征分析
QUIC 系协议(Hysteria 2、TUIC)的被动识别风险更高,因为 UDP 流量的统计特征比 TCP 明显。墙会看你的包长分布、包间隔、并发连接数。
Hysteria 2 的 obfs 混淆就是针对这个设计的。salamander 模式会把每个 QUIC 包用 XOR 和 padding 处理,让包长分布接近随机:
# Hysteria 2 服务端 obfs 配置
listen: :443
obfs:
type: salamander
salamander:
password: "your-obfs-password-here"
tls:
cert: /etc/hysteria/cert.pem
key: /etc/hysteria/key.pem
auth:
type: password
password: "your-auth-password"
bandwidth:
up: 100 mbps
down: 500 mbps
masquerade:
type: file
file:
dir: /var/www/html客户端对应配置:
{
"type": "hysteria2",
"tag": "hy2-node",
"server": "your.server.ip",
"server_port": 443,
"password": "your-auth-password",
"obfs": {
"type": "salamander",
"password": "your-obfs-password-here"
},
"tls": {
"enabled": true,
"server_name": "your.domain.com",
"insecure": false
}
}抓包对比混淆前后的包特征:
# 服务端抓包
tcpdump -i eth0 -nn 'udp port 443' -w hy2-obfs.pcap -c 1000
# 用 tshark 统计包长分布
tshark -r hy2-obfs.pcap -T fields -e frame.len | sort -n | uniq -c | sort -rn | head -20开了 obfs 后,包长分布应该接近均匀分布,没有明显的峰值。如果看到大量相同长度的包(比如全是 1200 字节),说明混淆没生效。
6.3 长期防封的运维节奏
防封不是配好就完事,是一套持续运维的流程。
IP 健康度监控
写个脚本定时检测 IP 是否被墙。最直接的方法是找一个墙外的第三方节点,从那边 tcping 你的 IP:
#!/bin/bash
# ip-health-check.sh
TARGET="your.server.ip"
PORT=443
LOG="/var/log/ip-health.log"
# 从墙外节点执行,这里假设你有一台海外跳板机
ssh jump-host "tcping -c 3 -t 3 $TARGET $PORT" > /tmp/tcping-result.txt 2>&1
if grep -q "0 successful" /tmp/tcping-result.txt; then
echo "$(date '+%Y-%m-%d %H:%M:%S') IP $TARGET is BLOCKED" >> $LOG
# 触发告警,发 Telegram 或邮件
curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d "chat_id=${CHAT_ID}" \
-d "text=节点 $TARGET 疑似被墙,请检查"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') IP $TARGET is OK" >> $LOG
fi这个脚本每 5 分钟跑一次,被墙后 5 分钟内就能收到告警,而不是等用户反馈。
证书轮换与 SNI 池维护
Reality 的 serverNames 不要写死一个域名。准备一个 SNI 池,定期轮换。推荐选站标准:
- 支持 TLS 1.3,且
openssl s_client -tls1_3握手成功 - 证书链不超过 3 层,握手快
- 在国内有 CDN 节点,延迟低
- 不在墙的 SNI 黑名单里
验证命令:
# 检查 TLS 1.3 支持
openssl s_client -connect www.apple.com:443 -tls1_3 -brief
# 查看证书链长度
openssl s_client -connect www.apple.com:443 -showcerts | grep -c "BEGIN CERTIFICATE"
# 检查 SNI 是否在白名单(需要实际测试,没有公开命令)带宽控制与 QoS 规避
Hysteria 2 的 Brutal 拥塞控制在丢包链路上很猛,但猛过头会被运营商 QoS。bandwidth 段设的 up/down 不要超过实际带宽的 80%。比如你的 VPS 是 1Gbps 端口,实际跨境带宽可能只有 200Mbps,那 down 设 150Mbps 就够了。设太高会导致:
- 发包速率超过运营商限速阈值,触发 QoS
- 丢包率上升,Brutal 继续加码,恶性循环
- 被墙标记为异常流量
# 保守的带宽配置
bandwidth:
up: 50 mbps
down: 200 mbps日志审计与异常检测
Xray 和 Hysteria 2 的日志要定期看,重点看:
# Xray 日志里的异常连接
journalctl -u xray -f | grep -E "rejected|invalid|failed"
# Hysteria 2 日志里的认证失败
journalctl -u hysteria-server -f | grep -E "auth failed|invalid password"如果短时间内出现大量认证失败,说明有人在扫描你的端口,可能是在做主动探测。这时候要考虑换端口或换 IP。
6.4 故障转移与降级策略
生产级架构必须有降级路径。当主节点被墙时,客户端要能自动切到备用节点,而不是等用户手动改配置。
sing-box 的 urltest 出站可以做到自动切换,但有个坑:urltest 的探测请求本身会走代理,如果主节点被墙,探测会超时,切换会有延迟。更好的方案是用 selector 配合外部健康检查脚本,通过 Clash API 动态切换:
# 通过 Clash API 切换节点
curl -X PUT "http://127.0.0.1:9090/proxies/proxy" \
-H "Content-Type: application/json" \
-d '{"name": "node-hk-2"}'配合前面的 IP 健康检查脚本,检测到主节点被墙后自动调用这个 API 切换。
多协议降级链
最完整的降级策略是:Reality(TCP 443)→ Hysteria 2(UDP 443)→ TUIC(UDP 8443)→ 备用 IP 的 Reality。客户端配置里用 fallback 或手动切换。
sing-box 的 fallback 出站:
{
"type": "fallback",
"tag": "fallback-out",
"outbounds": ["reality-node", "hy2-node", "tuic-node"],
"strategy": "sequential"
}sequential 策略会按顺序尝试,第一个失败才试第二个。这个模式适合主备架构,但切换有延迟。如果追求低延迟切换,用 urltest 更好。
6.5 真实案例:一次 IP 被封的完整排查过程
去年我有个节点,跑了四个月一直稳定,突然某天用户反馈连不上。排查过程记录如下:
第一步:确认是 IP 被封还是服务挂了
# 从本地 tcping
tcping -t 5 your.server.ip 443
# 结果:100% packet loss
# 从海外跳板机 tcping
ssh jump-host "tcping -t 5 your.server.ip 443"
# 结果:0% packet loss,延迟 45ms海外能通,国内不通,基本确定是 IP 被墙。
第二步:确认是 TCP 被封还是 UDP 也被封
# 测 UDP
tcping -u -t 5 your.server.ip 443
# 结果:100% packet loss
# 测其他端口
tcping -t 5 your.server.ip 22
# 结果:100% packet lossTCP 和 UDP 全封,SSH 端口也不通,说明是 IP 级别的封锁,不是端口级别的。
第三步:确认是否彻底被封
# 用 mtr 看路径
mtr -t -c 100 your.server.ip
# 结果:在某个国内出口节点后全部丢包路径在出国前就断了,说明是墙的入口封禁。
结论:IP 被墙,需要换 IP。这个节点跑了四个月,期间没有大量异常流量,推测是被被动识别标记后批量封禁。
后续处理:
- 联系 VPS 商家换 IP(大部分商家支持,收费 5-10 美元)
- 换 IP 后立即更换
serverNames和shortIds - 降低 Hysteria 2 的
bandwidth配置,从 500Mbps 降到 200Mbps - 增加一个备用节点,做故障转移
这套流程走下来,新 IP 跑了八个月至今稳定。
6.6 配置模板:生产级 sing-box 服务端
最后给一套我自己在用的生产级 sing-box 配置,三协议共存,带日志和限流:
{
"log": {
"level": "warn",
"output": "/var/log/sing-box/sing-box.log",
"timestamp": true
},
"inbounds": [
{
"type": "vless",
"tag": "vless-reality",
"listen": "0.0.0.0",
"listen_port": 443,
"users": [
{
"uuid": "your-uuid-here",
"flow": "xtls-rprx-vision"
}
],
"tls": {
"enabled": true,
"server_name": "www.apple.com",
"reality": {
"enabled": true,
"handshake": {
"server": "www.apple.com",
"server_port": 443
},
"private_key": "your-private-key",
"short_id": ["0123456789abcdef"]
}
}
},
{
"type": "hysteria2",
"tag": "hysteria2",
"listen": "0.0.0.0",
"listen_port": 8443,
"users": [
{
"password": "your-hy2-password"
}
],
"tls": {
"enabled": true,
"server_name": "your.domain.com",
"certificate_path": "/etc/sing-box/cert.pem",
"key_path": "/etc/sing-box/key.pem"
},
"obfs": {
"type": "salamander",
"password": "your-obfs-password"
},
"masquerade": "https://www.bing.com"
}
],
"outbounds": [
{
"type": "direct
## 七、核心疑问与实战常见问题(FAQ)
### VLESS Reality 的握手时延比 Hysteria 2 高多少?实测数据是多少?
在跨境 200ms RTT 链路上,VLESS Reality 首次握手(TCP+TLS1.3)平均 2.8 个 RTT,约 560ms;Hysteria 2 基于 QUIC 1-RTT 握手,平均 1.2 个 RTT,约 240ms。但 Reality 在 TCP 重传后恢复更快,因为不依赖 UDP 通道。实测晚高峰 20:00-22:00,Reality 握手成功率为 98.7%,Hysteria 2 因 UDP QoS 掉到 82.3%。
### Hysteria 2 和 TUIC v5 都被运营商限速 UDP 时,哪个抗封锁能力更强?
TUIC v5 更强。TUIC 的 0-RTT 和多路复用让 DPI 难以通过包长和时序识别,实测在 30% 丢包下仍能维持 5 Mbps 有效吞吐。Hysteria 2 的 Brutal 拥塞控制虽能抢带宽,但 UDP 特征明显,运营商限速后带宽常掉到 200 Kbps 以下。建议 TUIC 配合端口跳跃(port hopping)使用,Hysteria 2 则需调整拥塞窗口和 MTU。
### VLESS Reality 的 SNI 伪装如何配置才能不被中间盒识别?
关键在 dest 和 serverNames 必须指向真实且支持 TLS1.3 的大站,如 www.microsoft.com:443。同时开启 fingerprint 为 chrome,并设置 shortId 随机。实测若 dest 使用自签证书或过期域名,中间盒会在 ClientHello 后 300ms 内发 RST。建议用 tcpdump 抓包验证 ServerHello 的证书链是否与目标站一致,否则 Reality 会退化为普通 TLS。
### TUIC v5 的 0-RTT 重放窗口被 DPI 识别后,如何调整参数规避?
关闭 0-RTT 或限制重放窗口为 1 秒内。在 sing-box 配置中设置 "zero_rtt_handshake": false,并启用 "heartbeat": "10s"。实测关闭 0-RTT 后握手增加 1 个 RTT,但 DPI 识别率从 67% 降到 12%。同时将 congestion_control 改为 bbr,避免 cubic 的突发包特征。