Xray 的 Hysteria2 协商和官方不一样,服务端没填 brutalUp 就按 BBR 跑

本文依据 Xray-core issue #7353(2026 年 11 月 9 日提交)与双方源码整理。issue 作者在同一台机器的回环环境里实测了 8 组对照;官方一侧作者只读了源码、未跑官方程序——原文写明了这一点,本文照实转述,未在本站复现。

现象:只配一端,另一端就失效

Xray 的 Hysteria2 在默认 brutal 模式下,Brutal 的启用条件和官方 Hysteria 不一致:

  • 服务端没配 brutalUp 时,客户端声明的下行不起作用,下载方向用 BBR;
  • 服务端没配 brutalDown 时,Xray 客户端上传也用 BBR。

也就是说,你在客户端 finalmask.quicParams 里填了 brutalDown: 20 mbps,以为下载会按 20 Mbps 跑 Brutal,实际上只要服务端没填 brutalUp,这一行就等于没写。

代码在哪:两端都是同一行判断

问题出在 Xray 两端的同一行判断上。服务端在 transport/internet/hysteria/hub.go 第 78–83 行,客户端在 transport/internet/hysteria/dialer.go 第 207–212 行(v26.10.31,当前 main 里还是同一段代码):

1
2
3
4
5
if quicParams.BrutalUp == 0 || down == 0 {
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
} else {
congestion.UseBrutal(conn, min(quicParams.BrutalUp, down), quicParams.BrutalDisableLossCompensation)
}

两端都要求本端的 brutalUp 和对端声明的值同时不为 0,否则直接用 BBR。

官方是怎么做的:声明即用,上限才卡

官方 Hysteria v2.13.0 的做法不一样(依据官方源码,issue 作者未运行官方程序实测):

  • 服务端:客户端声明了下行就使用 Brutal,服务端配了上限时取较小值(core/server/server.go 第 178–196 行);
  • 客户端:服务端的 Rx 为 0 时,按客户端自己的上行使用 Brutal(core/client/client.go 第 149–162 行)。

差别就一句话:官方是“对端声明了就用 Brutal,己方配置只作上限”,Xray 是“两边都得填,不填一边就回落 BBR”。如果你是从官方 hy2 换到 Xray 的 hysteria 出站/入站,照着官方文档配单边带宽,速度表现会和预期不一样。

8 组实测对照

issue 作者用 Xray 26.10.31 在同一台机器上做了回环实测:本地起一个 HTTP 测速服务,分别测 4 秒下载和上传。这台机器上 BBR 约 150 Mbps,Brutal 组被限制在 20 Mbps 以下——所以表格里的速率本身就是判断依据:

服务端 quicParams 客户端 quicParams 官方预期 Xray 下载 Xray 上传 Xray 实际
无 无 两向 BBR 167.3 130.1 两向 BBR
无 brutalDown: 20 mbps 下载 Brutal 20 163.0 161.1 两向 BBR
brutalUp: 40 mbps brutalDown: 20 mbps 下载 Brutal 20 11.9 182.6 下载 Brutal
brutalUp: 20 mbps brutalDown: 80 mbps 下载 Brutal 20 11.6 156.3 下载 Brutal
brutalUp: 20 mbps 无 两向 BBR 145.0 154.8 两向 BBR
无 brutalUp: 20 mbps 上传 Brutal 20 171.0 163.2 两向 BBR
brutalDown: 20 mbps brutalUp: 40 mbps 上传 Brutal 20 149.5 18.2 上传 Brutal
无 brutalUp、brutalDown 均为 20 mbps 两向 Brutal 20 164.6 176.6 两向 BBR

(速率单位 Mbps;表格来自 issue #7353,作者回环实测,未经本站复现)

看第二行和第六行最清楚:客户端单边填了带宽,Xray 两边还是 BBR 的速度;只有两端都填了,对应方向才真的按 Brutal 跑。

怎么判断你是不是撞上了

如果你用的是 Xray 的 Hysteria2 出站或入站(finalmask.quicParams 里配 brutalUp/brutalDown),且出现下面这种现象,可以先按这条排查:

  1. 客户端填了带宽,速度却卡在 BBR 的水平,怎么调都不提速;
  2. 检查服务端有没有填对应的 brutalUp/brutalDown——只填一端是不够的;
  3. 两端都填上再测一次,如果对应方向的速度下来了(被限制在设定值附近),说明之前确实在跑 BBR。

代码里另有 force-brutal 分支,不经过这套协商、直接按 brutalUp 用 Brutal;这是代码里看到的选项,本文未实测,是否适合你先看 FinalMask 文档再决定。

注意这条只影响 Xray 的 Hysteria2,官方 hy2 程序不受影响。本站那篇Hysteria2 速度慢怎么办讲的是官方客户端的 bandwidth 写法,那篇的结论不受这条影响——两件事别混在一起。

文档也写错了

FinalMask 文档里写协商行为和 Hysteria brutal 一致,服务端为 0 表示不限制客户端——这和上面那段代码的行为是矛盾的。issue 作者的建议是二选一:要么改行为、要么改文档。如果当前行为是有意设计,至少文档应该写清楚。

现状与影响范围

  • issue #7353 于 2026 年 11 月 9 日提交,截至发稿仍是 open 状态,无官方回应、无修复;
  • 影响的是用 Xray 跑 Hysteria2 的人:入站和出站都受这套判断管;
  • 如果你一直两端都填带宽,这条对你没影响;如果只填一端还觉得速度不对,先按上面三步对照一次。

依据来源