Skip to content

Releases: liyanan2016-stack/guanfang-youxuan

v1.22 · 稳定性修复

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 31 Aug 18:29

四项稳定性修复,无新功能。通读代码后找出的「平时碰不到、碰到很难受」的问题。

扫描出错不再卡死界面。 之前扫描线程抛异常只画了错误文字,但没把运行标志置回去、没复位按钮、没停进度轮询 —— 结果「开始扫描」永久停在「扫描中...」且点不动,取消按钮也点不动,只能杀进程重开。现在异常和正常结束走同一套收尾动作。

扫描中转屏幕不再泄漏。 原来转屏会重建界面,而销毁时只中断了 Java 线程,没告诉 Go 核心层停下 —— 中断标志对正卡在 JNI 调用里的线程没有作用。后果是旧界面被整个扫描时长钉住不能回收,扫完还会往已经销毁的视图上写结果;更麻烦的是新界面的状态是「空闲」,用户能再点一次扫描,两次扫描并发写同一批全局状态。现在转屏和切键盘不再重建界面,销毁时也会显式取消核心层任务。

填 IP 段不再卡顿。 IP 段是边打边校验的,而校验在核心层要真的把段展开成子网(上限两万个,还要去重排序),这件事发生在界面线程上。粘一个大段进去,每敲一个字符都要跑满这个量。现在改成停手 300 毫秒才校验一次,并且把重复的展开去掉了 —— 原来一次校验会展开两遍。

核心层崩溃不再闪退。 Go 层的 panic 不会变成 Java 异常,它让整个进程直接中止,用户看到「应用已停止运行」,没有任何线索。现在三个入口(扫描、更新数据、IP 段预检)都会把 panic 拦下来,转成界面上的一句错误说明,堆栈写进日志。

安装包架构不匹配会说清楚。 release 按 CPU 架构分包,拿错包时原来是启动即崩。现在启动时先探一次核心层,装不上就显示「核心组件加载失败」并告诉用户改用 universal 版本。


签名指纹 c2:d4:0f:52… 与 v1.21 一致,可直接覆盖安装,历史记录和设置保留。

不确定手机架构就装 universal。桌面版无需安装,双击即用(会打开浏览器界面)。

文件 SHA-256 前 8 位
universal.apk 196e8237
arm64-v8a.apk 00c98712
armeabi-v7a.apk 3a8532ba
windows-amd64.exe 63e2872c
windows-arm64.exe 5027633c
linux-amd64 018673ea

v1.21

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 31 Aug 03:43

v1.21

版本号露出界面。 顶栏标题右侧显示版本号,点一下弹「关于」:应用版本、build 号、核心库版本。之前只能去系统的应用信息里翻。

使用说明和更新日志进 App。 关于弹窗下方两个按钮。使用说明只放 7 条最容易卡住的地方;完整教程见 使用教程.md。

补上鸣谢。 股神(t.me/CF_NAT)、PoemMisty/CFData-WEB、cmliu/edgetunnel、Cloudflare。见 README 的鸣谢。

文档精简。 使用教程 428 → 143 行,README 454 → 186 行。原来把「怎么用」和「为什么这么实现」混在一起,现在使用教程只讲操作、README 只讲设计取舍、版本沿革全收进 更新日志.md。


签名指纹 c2:d4:0f:52…,与 v1.2 起所有版本一致,可直接覆盖安装,历史记录和设置保留。

不确定手机架构就下 universal;2016 年后的手机可以下 arm64-v8a 省一半空间。

官方优选 v1.20

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 31 Aug 02:48

新增:指定 IP 段优选

主界面新增「指定 IP 段」折叠区。留空是原来的行为(扫官方全部 6500+ 个 /24),
填了就只扫你给的段。

为什么值得做。 官方列表是 CF 全部对外宣告的段,其中绝大部分和你所在网络的实际
质量无关,而 104.16 / 104.21 这些段早被扫烂。有明确目标时(别人分享的优质段、
自己账户对应的段)直接指定,候选池从六千多压到几十个。实测 104.17.168.0/22
四秒出结果,同条件扫官方列表要 14 秒。

支持的写法(一行一条,或用逗号/空格分隔,可以混着来):

104.16.0.0/13                 CIDR,自动切成 2048 个 /24
104.17.168.20                 单个 IP
104.16.0.0-104.16.3.255       起止范围(IPv6 请用 CIDR 写法)
2606:4700::/48                IPv6
# 这行是注释,会被忽略

中文逗号、顿号、分号、制表、换行都认——从各种地方复制粘贴不该为分隔符格式报错。
上限 20000 个 /24,超了会截断并提示。

边打边校验。 填错(少个斜杠、粘进中文标点、v4/v6 混填)本来只在点了扫描、等完
初始化之后才暴露。现在输入时就显示「已识别 2048 个 IPv4 /24 子网」或者具体哪一条
错了。收起折叠区后标题右侧显示「v4 2048 段」,结果里也会标明数据源。

几处行为联动

  • 指定段时测完全部子网。 官方列表模式下覆盖率到 30% 就够了,但你给的池子本来就是
    精挑的,测 30% 就收手等于把大部分跳过了。
  • 指定段时不下载官方列表。 那个 6500 行的文件根本不会被读,首次使用的人没必要为
    它干等,数据源挂掉也不该因此扫不了。
  • 填了 v4 段却扫 v6 会直接提示切换协议,而不是让你对着「未找到可用 IP」猜。
  • 大段自动切成 /24(v6 切 /48)。这是能跑起来的前提而非优化:抽样器按「子网」
    批次推进、每个子网出 2 个候选,直接把一个 /13 当成一个子网塞进去,整轮只测 2 个 IP。

顺带修的两个 bug

都是新的输入形态暴露出来的,官方列表清一色 /24 时碰不到:

  • 前缀比 /24 细时仍随机整个末位八位组,填 1.2.3.0/28 会拼出 1.2.3.200 去测——
    那既不是你指定的,也可能根本不是 CF 的地址。
  • /31 只有 2 个地址,原来「固定 4 次重试躲重复」很容易 4 次全撞,导致一个 IP 都不产出。

下载:Android 装 universal(通用)或 arm64-v8a(2016 年后的手机,省一半空间);
Windows 装 windows-amd64.exe;Linux 用 linux-amd64。

签名与 v1.15 一致,可直接覆盖安装。

官方优选 v1.19

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 31 Aug 02:48

测速源可选 + 按运营商自动挑选 + 速率限制熔断

继续处理「优选很快、实际很慢」,这次动的是根因。

测速源可选(5 档)

旧版固定用 url.txt 下发的公共镜像静态 ISO。那条链路和你自己的节点没关系:
吃边缘缓存(没命中时回源那段与被测 IP 无关)、上游随时改版 404、字节数不可控。
更关键的是单一域名等于单一运营商 QoS 策略——中国移动国际出口对不同域名的限速
差别很大。

现在换成按需生成字节流的端点,高级选项里 5 档可选:

档位 地址
自动选择(默认) 探运营商后决定
Cloudflare speed.cloudflare.com/__down?bytes=99999999
CM提供 cf.090227.xyz/__down?bytes=99999999
移动专属 speed.okl.abrdns.com
手动输入 你自己的域名,最准

自动档探 cf.090227.xyz/cf.json 拿 asn + asOrganization,判定是移动就随机换用
两个移动友好源之一(随机是为了别把社区资源打挂)。探测请求禁用代理——挂着代理探
出来的是代理出口的 AS,据此选源等于按别人的网络环境优化。

结果里会回显本次用的测速地址和探测到的运营商。

速率限制熔断

测速端点会返回 HTTP 429,旧代码把它混进普通状态失败:连续撞限流时整轮候选白测,
还让限制窗口一直续期。现在连续 3 次就停止继续测速,候选之间留 1200ms 间隔,
成功一次即清连续计数(避免「429、成功、429」被误判成连续限流)。
收尾提示区分「全部 429」和「部分 429 导致速度偏低」。

测速预算改成硬边界

Read 原本在主循环里同步调用,一个「TCP 通、TLS 握手过、但数据一个字节都不来」的
半死连接能把单次 Read 挂到 client.Timeout(预算 + 3 秒)——15 秒档最坏 18 秒卡在
一个候选上,一轮 10 个就是三分钟。现在读取在独立 goroutine 里跑,主循环用 select
等 deadline,到点立刻收工。

其他

  • 测速请求禁用 gzip。 Go 默认会加 Accept-Encoding: gzip 并透明解压,累计的是
    解压后的字节数——遇到高度可压缩的内容速度会被放大几十倍。
  • 测速地址解析改用 net/url,现在支持带端口、带查询串、只填域名等写法。
    旧版手工切字符串,把带 : 的域名一律判非法,__down?bytes= 这类端点根本填不进来。
  • 探 colo 的默认 SNI 改取实际测速域名,保证「探通的域名」和「测速的域名」一致。

顺便推翻了一个旧结论:代码里曾注明「speed.cloudflare.com/__down 只服务直连边缘
节点,非直连一律 403」。指定任意 CF IP 实测三个源全部返回 200,那个结论是当时
测法有误。


下载:Android 装 universal(通用)或 arm64-v8a(2016 年后的手机,省一半空间);
Windows 装 windows-amd64.exe;Linux 用 linux-amd64。

签名与 v1.15 一致,可直接覆盖安装。

官方优选 v1.18

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 31 Aug 02:48

节点域名 / 自定义测速地址 / 测速时长收进「高级选项」折叠区

纯界面整理,功能没变。

v1.17 把两个输入框加两段长说明直接摊在主界面上,加上原有的节点域名和一个分段控件,
这几块加起来能占掉一屏多——「开始扫描」被挤到要划一下才看得见的地方。而这三项都是
「不动也能用、动了才更准」的东西,绝大多数人不填也能出结果。

现在收进一个折叠区,收起时标题行右侧写摘要(已填域名 · 自定义测速 · 测速 5 秒),
所以折叠没有藏掉信息——一眼能看出自己改过什么。开合状态会记住。

桌面版把测速时长也移进了已有的高级选项区,两端保持一致。


下载:Android 装 universal(通用)或 arm64-v8a(2016 年后的手机,省一半空间);
Windows 装 windows-amd64.exe;Linux 用 linux-amd64。

签名与 v1.15 一致,可直接覆盖安装。

官方优选 v1.17

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 31 Aug 02:47

自定义测速地址 + 测速时长可调 + 测速配置回显

三项都是为了回答同一个问题:为什么优选出来很快、实际用起来慢。

自定义测速地址。 默认测速地址是别人的域名(url.txt 下发的公共镜像),
边缘缓存命中率、CF 账户等级、有没有回源全都和你自己的节点无关——测出 25 MB/s
说的是那个镜像站的速度。填自己域名下的一个大文件后,测的就是「CF 边缘 → 回源到
你的服务器」这条实际会用到的链路。填了它,RTT 阶段的默认 SNI 也跟着走同一个域名。

填错会直接停下报错,不会悄悄回落默认地址——那样你会以为测的是自己的域名。

测速时长 5 / 10 / 15 秒可选。 运营商国际出口对新连接常有「突发不限速」窗口,
前几秒飞快、之后掉一个数量级。5 秒测速整段都落在窗口里,测出来的数字比你能持续
用到的带宽高得多。默认仍是 5 秒(不改变老用户的结果),怀疑虚高时调到 10 或 15。

测速配置回显 + 诊断。 结果里现在会显示本次用的测速地址和时长。另外加了诊断:
全部候选返回同一个非 200 状态码就提示「检查路径是否正确、文件是否可公开下载」,
下载量全都小于阈值就提示「文件太小,请换 100MB 以上的」。以前这两种情况都只显示
「未找到可用 IP」,无从下手。


下载:Android 装 universal(通用)或 arm64-v8a(2016 年后的手机,省一半空间);
Windows 装 windows-amd64.exe;Linux 用 linux-amd64。

签名与 v1.15 一致,可直接覆盖安装。

官方优选 v1.16

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 31 Aug 02:47

修「选地区后一直停在第一轮」

指定落地地区后,进度长时间停在第一轮不动,看起来像卡死。

实质原因。 regionSampler.next() 原本要攒满 sampleSize=100 个命中子网才交货。
窄地区的命中率不到 1%,意味着要探完六千多个子网才凑够一批,这段时间 RTT 阶段
一个字都不跑。改成:已经有命中时最多探 3 刀(600 个子网)就提前交货,
完全没有命中时配额不生效(否则会空转)。

观感原因。 进度文案不带时间也不带批次,用户无法判断是在推进还是卡住。
现在全链路附「已用 X 秒」,换批提示带「第 N 轮 · 第 M 批」与累计侦察数,
侦察提示补上命中率,RTT 刷新粒度从 10 个收到 5 个。

顺带同步了 Anim.pulse 的 null 检查与 alpha 兜底。


下载:Android 装 universal(通用)或 arm64-v8a(2016 年后的手机,省一半空间);
Windows 装 windows-amd64.exe;Linux 用 linux-amd64。

签名与 v1.15 一致,可直接覆盖安装。

官方优选 v1.15

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 30 Aug 12:56

官方优选 v1.15

这一版只动界面动效,优选逻辑没变。

分段控件改滑动指示块

IP 协议、输出数量原来是 RadioGroup。它的选中反馈只能是背景 selector 的瞬间替换——高亮块「消失在这里、出现在那里」,中间没有过程。之前在它之上加过放大回落,但那治不了根本:动效时长再怎么调,看起来还是「啪」一下。

改成自绘的 SegmentedBar,指示块从旧位置滑到新位置:

  • 340ms,略微过冲再回落(纯减速曲线滑过去像抽屉滑到底,有一点过冲才有咬合感)
  • 位置和宽度都插值——「1 / 5 / 10」三格宽度不等,只插位置会在宽度突变时闪一下
  • 文字颜色按指示块的实时距离插值,不是跟着状态突变:指示块压在两格之间时,两边文字都该是中间色

顺带消掉一类 bug:RadioGroup 靠 view id 追踪选中项,动态创建的子项忘了设 id 就会出现两个同时高亮(v1.13 的「输出数量」正是这么坏的)。现在选中项是一个 int 下标,没有这个隐患。

惯性滑到顶/底改为反弹

SpringScrollView 原来只接手手指拖动,惯性滑动(fling)撞到边界时直接交给父类,滚动位置走到 0 或 scrollRangeY 就硬生生停住——那个「突然顿一下」就是这么来的。

现在按 fling 的剩余速度先冲出去形变、再弹回来:

  • 深度上限压在 maxPull / 3。惯性撞边的幅度应该明显小于手动拉拽,否则快滑翻页时每次到底都晃一大下
  • 低于 800 px/s 完全不弹。慢慢滑到底本来就不该有撞击感,弹了反而像手抖
  • 手指按下立即中断反弹,避免和新的拖动打架

文档

使用教程.md 补了「界面长什么样」一节——原来没提两页结构、底部导航、顶栏的更新数据与主题按钮,新用户装完不知道东西在哪。另补上 v1.13 的「输出数量」与「备选地址」列表说明(备选不写进历史记录,想留着备用要当场复制),以及两条常见疑问:主题切换会记住、滑到边界弹一下不是 bug。

下载

文件 说明
guanfang-youxuan-v1.15-universal.apk 通用,体积大但一定能装
guanfang-youxuan-v1.15-arm64-v8a.apk 2016 年后的手机,省一半空间
guanfang-youxuan-v1.15-armeabi-v7a.apk 老设备
guanfang-youxuan-v1.15-windows-amd64.exe Windows
guanfang-youxuan-v1.15-windows-arm64.exe Windows ARM
guanfang-youxuan-v1.15-linux-amd64 Linux

签名与 v1.2 起各版本一致,可直接覆盖安装,历史记录不丢。

d41b444b29c5a7fe343576b8c53990bda44983eb06fe0586798cb51512ee3c13  universal.apk
a12d5d2f03d7d498c6db50c67d8c218fb8adf53952457cd5f432122970a5579b  arm64-v8a.apk
20da03bc953dfd06407ba4c40f0b9f196dccd10c134fbf45253ebe63f04b08e0  armeabi-v7a.apk
c094788a8e53c07d7e4cb3738372210b02692f595c492cc1953390c2ad987886  windows-amd64.exe
75da3e1579ddaa8e49f12530cd1b23fa68686cb3153e70501ef47e1017227da0  windows-arm64.exe
5e6bba718abd8e82224d409217c11d6c94c86aafa14fd761a6d007be63017de4  linux-amd64

v1.7 修复回源不通的 IP 被误判为可用

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 30 Aug 07:04
文档:下载指向 v1.4,新增「优选逻辑(v1.4 重写)」章节

官方优选 v1.4

Choose a tag to compare

@liyanan2016-stack liyanan2016-stack released this 29 Aug 11:42

这一版重写了「优选」本身。v1.2 之前的版本虽然叫优选,但默认参数下实际选出来的是延迟最低的 IP,不是最快的。这版把测速真正变成了决定因素,同时把耗时压回可接受范围。

(v1.3 只在内部构建,改动一并包含在这一版里。)

之前的「优选」名不副实

按延迟排序后逐个测速,遇到第一个达标的就返回。而期望带宽默认 1 Mbps —— 几乎任何活着的 CF 节点都能过,于是结果实际等于「延迟最低的那个 IP」,测速只是走了个形式。

延迟低不代表带宽高。 同城机房延迟 20ms 但可能已被打满,300ms 的远端反而跑得开。现在测完一批候选再选实测最快的。

但「测完再选」会很慢,所以做了两阶段测速

直接测完 10 个候选 × 每个 5 秒 = 50 秒一轮,质量上去了却慢得没人愿意用。

现在先用 1.5 秒粗测全部候选,再只对最快的 3 个做完整 5 秒测速:10×1.5 + 3×5 ≈ 30 秒,比全量快近一半,选出的仍是实测最快的。

1.5 秒足够区分「几百 kB/s」和「几 MB/s」这种量级差异 —— 而量级差异正是要找的东西。粗测的绝对值不准(TCP 窗口还没涨满,会系统性偏低),所以只用来排序,绝不用来判定达标。

同一个机房最多 3 个名额

按延迟排出来的 top 10 经常全在同一个 colo —— 都是离你最近的那个机房。一旦那个机房正好拥塞,整轮 10 次测速测的其实是同一条链路,全部白费。

现在每个机房最多 3 个候选。名额凑不满也不回填:同机房再多测几个 IP 测的还是那条链路,不如快点进下一轮换新子网。

每个子网测 2 个 IP

之前每个 /24 只随机拼 1 个地址。CF 的 /24 通常整段都是活的边缘节点,但随机挑中的那一个恰好不响应(防火墙、未启用、临时故障),整段就被放弃了。取 2 个把「整段误判为死」的概率降到接近平方级。

RTT 少做 2/3 的无用功

之前对每个目标做 3 次完整的 TCP + TLS 握手 + HTTP 请求,但延迟统计只用了 TCP 握手时间 —— TLS 和 HTTP 那部分纯属白做。

现在只有第 1 次做完整流程(验 CF-RAY 确认是真 CF 节点),后 2 次只做握手计时。TLS 模式下 RTT 阶段直接快约 2/3。

地区筛选按子网跳过

同一个 /24 里的 IP 基本落在同一个 colo。测到某个 IP 的 colo 不匹配时,整段直接标记跳过,不再逐个测完再逐个丢弃。

筛地区时这个优化决定了能不能在可接受时间内出结果。

覆盖率从 15% 提到至少 30%

ips-v4.txt 有 6500+ 个子网,10 轮 × 每批 100 只覆盖约 15%,剩下 85% 从来没机会被测到 —— 而「最快的 IP」很可能就在里面。

现在轮次用尽后若覆盖率不足 30% 会继续扫描。达标结果仍然立即返回,所以这个下限只在一直没达标时才延长扫描。

慢 IP 提前放弃

一个跑 200 kB/s 的 IP 原本要占满整个 5 秒预算。现在观察 2 秒后若速度不到目标的 40% 就换下一个。阈值留得宽是为了不误杀慢启动后追上来的 IP。

抖动参与排序

3 次测量之前只取了平均值。低延迟高抖动的 IP 实际体验很差(表现为卡顿),只看平均值挑不出来。

现在按 20ms 分档,同档内优先选抖动小的 —— 5ms 的差别感知不到,但抖动大的会明显卡顿。

下哪个

文件 适用
guanfang-youxuan-v1.4-universal.apk 不确定手机架构就下这个(28MB)
guanfang-youxuan-v1.4-arm64-v8a.apk 2016 年后的手机,省一半空间(8.6MB)
guanfang-youxuan-v1.4-armeabi-v7a.apk 老 32 位手机(8.6MB)
guanfang-youxuan-v1.4-windows-amd64.exe Windows 64 位
guanfang-youxuan-v1.4-windows-arm64.exe Windows ARM
guanfang-youxuan-v1.4-linux-amd64 Linux 64 位

Android 最低 7.0,只申请 INTERNET 一个权限。装好后主界面应显示 v1.4。

签名与 v1.1 / v1.2 一致,可直接覆盖安装,历史记录不丢。

校验

6648f1e86e15dddf4a8d07c3608bd7805bafc1e4ecf58c41576eeac85bc184c2  guanfang-youxuan-v1.4-universal.apk
40523cd6103bc8cc0027fec4f711a187af6c57c2f9eaa5d58ddbd206c873af3c  guanfang-youxuan-v1.4-arm64-v8a.apk
7321874dba241ceac97672b18243e0132a7af3b2e62aacce13ca1d3b710b87e3  guanfang-youxuan-v1.4-armeabi-v7a.apk
8cf1d08f6902521d5648ba13d8f45fe1bd79102885d1c4e9709a9d93a81aa4fc  guanfang-youxuan-v1.4-windows-amd64.exe
b581730e09af220b803ae0c3a310ae57d5bbd2360478099dc5ce75ee1da040e1  guanfang-youxuan-v1.4-windows-arm64.exe
50a5b515cf1bd225e1ae85150cd8a01f58eaa81b241dabe1b2ee725845230a0f  guanfang-youxuan-v1.4-linux-amd64

APK 签名证书 SHA-256:

c2d40f52d616dd8e7e822025d417d20821fb00ee5f350981dc652e44bdaa5e7f

桌面版 exe 无代码签名证书,Windows SmartScreen 首次运行会拦,选「更多信息 → 仍要运行」。