173 Commits

Author SHA1 Message Date
xiu2
2de42f8d30 README.md 2026-03-02 23:43:02 +08:00
XIU2
bcbf7f0e2a Merge pull request #632 from jysz/patch-1
修复 Linux 安装步骤中的错误镜像链接文件名
2026-03-02 23:37:44 +08:00
jysz
f735c0f5b3 修复可能错误的链接
前面wget github下载写的是amd64,解压步骤写的也是amd64.但是镜像写的变成arm 64了,无脑复制运行会因为文件名不匹配导致错误
2026-03-02 23:25:26 +08:00
xiu2
7fbe1d1b56 README.md 2026-02-26 14:58:22 +08:00
xiu2
6eaacd6b2c 修复 CFST新版本下获取不到第一行 IP 的问题 2025-12-15 13:29:48 +08:00
xiu2
b955387787 README.md 2025-11-23 18:05:12 +08:00
xiu2
e57e9d3897 README.md 2025-10-22 17:23:34 +08:00
xiu2
801dd53774 README.md 2025-09-13 10:01:23 +08:00
xiu2
e03a371e19 README.me 2025-07-25 09:50:26 +08:00
xiu2
346b27f011 README.md 2025-07-22 19:51:54 +08:00
xiu2
4f9ca03008 修复 部分情况下无法显示彩色文字的问题 2025-07-22 19:51:05 +08:00
xiu2
d76a8bc749 更新 依赖版本及最低编译版本 2025-07-22 15:14:21 +08:00
xiu2
3cd9a35994 完善 注释说明 2025-07-21 22:42:17 +08:00
xiu2
574e419ee1 补充 调试模式下,更多下载测速失败报错原因 2025-07-21 22:18:39 +08:00
xiu2
d66b2aa1f0 README.md 2025-07-21 21:53:36 +08:00
xiu2
a392fddd3a 修复 没有设置下载速度下限条件时输出测速结果数量异常的问题 2025-07-21 21:13:20 +08:00
xiu2
2bb2f509d9 README.md 2025-07-21 15:00:01 +08:00
xiu2
3cf898701f README.md 2025-07-14 20:16:24 +08:00
xiu2
74267eb2ac README.md 2025-07-09 20:50:03 +08:00
xiu2
bf1bd561da README.md 2025-07-09 20:48:11 +08:00
xiu2
b5e741ea3e README.md 2025-07-09 20:42:44 +08:00
xiu2
6099b48007 README.md 2025-07-09 20:39:42 +08:00
xiu2
397cfd25d3 修复 调试模式下因重定向后的地址出错导致下载测速失败时输出的最终地址不准确的问题 2025-07-09 20:26:01 +08:00
xiu2
db40924a32 README.md 2025-07-09 11:53:41 +08:00
xiu2
1bacd92f6a 调整 帮助文本开头的一句话描述内容 2025-07-08 23:56:39 +08:00
xiu2
2fe4e41a9e 调整 CloudflareST 为简写的 cfst 2025-07-08 23:36:45 +08:00
xiu2
c14627725c 新增 地区码支持 Fastly、Gcore、CDN77、Bunny 等 CDN 2025-07-08 23:00:26 +08:00
xiu2
6c599ed1a6 新增 调试模式支持在 HTTPing 测速过程中输出更多日志 2025-07-08 23:00:18 +08:00
xiu2
e236a24a0c 完善 调试模式 2025-07-08 20:29:14 +08:00
xiu2
fc5cd11867 README.md 2025-07-03 20:04:17 +08:00
xiu2
e9699196ef 考虑 对其他 CDN 的地区码支持(先加上相关注释方便后续完善) 2025-07-03 20:04:12 +08:00
xiu2
66912dd657 新增 支持显示地区码(机场三字码,仅限 Cloudflare、AWS CloudFront,HTTPing 和 下载测速(无论是哪个测速模式)过程中都会自动获取);
新增 调试模式运行参数(-debug 方便排查下载测速过程中遇到的问题);
新增 彩色输出内容;
调整 当没找到符合速度条件的 IP 时,默认不再直接忽略条件输出所有 IP 测速结果了,而是只有在调试模式下才会输出;
2025-06-29 00:30:05 +08:00
XIU2
04da9f5659 Merge pull request #538 from ipcjs/patch-1
chore: 添加通过scoop安装的命令
2025-05-11 14:49:04 +08:00
xiu2
013c27c059 README.md 2025-03-25 13:17:30 +08:00
xiu2
c7f8170485 README.md 2025-01-22 23:58:37 +08:00
XIU2
d6818fa60c Merge pull request #570 from Sving1024/master
修复 cfst_dnsmasq.sh IPv6 的问题
2025-01-22 23:56:45 +08:00
Sving1024
b15fe2c899 fix IPv6 2025-01-22 23:45:04 +08:00
xiu2
551f753323 README.md 2025-01-08 09:14:32 +08:00
xiu2
8acc095cd9 README.md 2025-01-04 16:40:21 +08:00
xiu2
ee31cff5c3 README.md 2024-12-28 20:22:38 +08:00
XIU2
b85ae8edec Merge pull request #565 from Sving1024/master
add scripts to auto update config file for dnsmasq
2024-12-28 20:18:29 +08:00
Sving1024
25fc2921e2 reset version 2024-12-28 19:30:36 +08:00
Sving1024
5e39f2612f add scripts to auto update config file for dnsmasq 2024-12-28 19:02:31 +08:00
xiu2
dbceab3418 README.md 2024-12-17 09:10:30 +08:00
xiu2
6c9d336d01 README.md 2024-12-17 09:09:40 +08:00
xiu2
a438fc6c93 README.md 2024-12-10 23:06:17 +08:00
xiu2
1d9d742bca README.md 2024-12-06 21:49:26 +08:00
xiu2
209c7100ff README.md 2024-12-06 21:44:27 +08:00
ipcjs
c74168a10d Update README.md 2024-11-15 01:57:34 +08:00
xiu2
66c0923bf7 README.md 2024-10-09 10:10:22 +08:00
xiu2
bbe762c46f 新增 支持 API 令牌方式 2024-10-06 14:13:18 +08:00
xiu2
887a55ce77 README.md 2024-09-12 16:32:51 +08:00
ipcjs
570a710446 Update README.md 2024-08-22 19:31:31 +08:00
ipcjs
0033f96e5d Update README.md 2024-08-22 19:28:12 +08:00
ipcjs
b3036eb1ef Update README.md 2024-08-22 17:33:00 +08:00
xiu2
84cbd2995c README.md 2024-08-09 20:49:35 +08:00
xiu2
1995d088ec README.md 2024-08-06 20:55:22 +08:00
xiu2
13f0c322a8 README.md 2024-08-06 20:54:00 +08:00
Taylor Lottner
a5cf1fdc05 新增 cfst_dnspod.sh 脚本用于 dnspod 自动更新域名解析最优 IP (#533) 2024-08-06 20:13:15 +08:00
xiu2
8a6670b67a README.md 2024-07-27 23:46:38 +08:00
xiu2
e815a60867 README.md 2024-07-25 22:57:46 +08:00
xiu2
bf832c5a4e 更新 import "io/ioutil" -> "io" 2024-07-22 11:29:36 +08:00
xiu2
f2e87d38d7 README.md 2024-07-09 19:45:58 +08:00
xiu2
63234aeb98 Update issue templates 2024-07-09 19:45:51 +08:00
xiu2
23b4f260e7 Update issue templates 2024-06-20 07:14:31 +08:00
XIU2
dbb5864235 Update README.md 2024-06-17 17:59:44 +08:00
xiu2
d96281af1f README.md 2024-02-13 22:47:06 +08:00
xiu2
6d12e7bac9 README.md 2024-02-12 13:00:38 +08:00
xiu2
9d9d71fa04 README.md 2024-02-12 12:03:00 +08:00
xiu2
176271d5bb README.md 2024-01-20 10:26:48 +08:00
xiu2
216d0f5f6b README.md 2023-12-15 21:35:45 +08:00
xiu2
7534e34ae2 README.md 2023-12-15 21:33:21 +08:00
xiu2
b3010e21fd README.md 2023-11-30 16:30:05 +08:00
xiu2
aa52d607bc README.md 2023-11-30 16:24:13 +08:00
xiu2
ca5564c56e README.md 2023-11-26 12:35:30 +08:00
xiu2
2cfc1e6b69 README.md 2023-11-25 15:18:27 +08:00
xiu2
a4b39ecc23 调整 测速过程中的输出内容 2023-11-25 15:18:18 +08:00
xiu2
49992a763e README.md 2023-09-24 20:28:43 +08:00
xiu2
ffc0dafbae README.md 2023-09-24 19:58:59 +08:00
xiu2
4621c56370 README.md 2023-07-05 14:21:47 +08:00
xiu2
2d400783c9 README.md 2023-07-03 21:33:01 +08:00
xiu2
133924d169 README.md 2023-06-08 10:58:39 +08:00
xiu2
bef4476b25 README.md 2023-06-06 19:44:36 +08:00
xiu2
6c698a815d README.md 2023-06-06 19:43:07 +08:00
xiu2
5f9d28e4a0 README.md 2023-05-31 17:12:12 +08:00
xiu2
4222b6ce95 README.md 2023-05-31 17:09:09 +08:00
xiu2
7c36abc12f 新增 [丢包几率上限] 参数 2023-05-31 16:46:16 +08:00
xiu2
eae7d2eead 优化 当延迟上下限条件为默认值时不进行过滤 2023-05-31 16:46:08 +08:00
xiu2
b6f3ddcd4c README.md 2023-05-15 12:27:28 +08:00
xiu2
c6d49bc7cb README.md 2023-05-11 18:44:02 +08:00
xiu2
a07af2f58b 修复 测速大量子网掩码 /32 /128 的 IP 时小概率出现重复 IP 的问题 2023-04-29 23:32:29 +08:00
xiu2
9f000aa23a 优化 解析 IP 段数据时会去除首尾的空白字符并跳过空行 2023-04-28 14:52:59 +08:00
xiu2
1800c2e89e README.md 2023-04-25 12:36:49 +08:00
xiu2
39d1687875 README.md 2023-04-25 11:55:35 +08:00
xiu2
24a162ac46 README.md 2023-04-25 11:28:07 +08:00
xiu2
3125b49b63 README.md 2023-04-25 11:23:21 +08:00
xiu2
729631deef 调整 -cfcolo 参数支持小写 2023-04-25 11:18:49 +08:00
Mac_Zhou
fe0721f077 Add support for CloudFront x-amz-cf-pop (#376)
1. 增加了对CloudFront筛选节点
2. 节点地区码使用正则表达式
2023-04-25 11:00:46 +08:00
xiu2
201619ecf7 README.md 2023-04-24 10:36:48 +08:00
xiu2
12effc301f Update issue templates 2023-04-04 11:02:05 +08:00
xiu2
fede8a04e6 Update issue templates 2023-04-04 10:57:27 +08:00
xiu2
b63f368837 将 module 名改为 github.com/XIU2/CloudflareSpeedTest 2023-03-27 11:49:59 +08:00
xiu2
c9a8c2b8cf README.md 2023-03-16 19:20:16 +08:00
xiu2
e80d7fff50 README.md 2023-03-03 20:05:59 +08:00
xiu2
f61a94886b Update issue templates 2023-03-02 21:00:00 +08:00
xiu2
f3f2bafbe7 Update issue templates 2023-03-02 20:22:27 +08:00
xiu2
5cf3b4bb42 Update issue templates 2023-03-02 19:35:02 +08:00
xiu2
6667e82c84 Update issue templates 2023-03-02 16:05:06 +08:00
xiu2
af4e8cf8b2 Update issue templates 2023-03-02 16:03:12 +08:00
XIU2
f2ec0a447c Update issue templates 2023-03-02 15:55:20 +08:00
xiu2
45b604103e README.md 2023-03-01 10:42:41 +08:00
xiu2
05ba005960 README.md 2023-02-23 12:44:29 +08:00
xiu2
a0b194f1d0 README.md 2023-02-23 12:43:28 +08:00
xiu2
7e21b2f46b README.md 2023-02-16 12:05:33 +08:00
xiu2
efafc38db8 README.md 2023-02-13 20:26:00 +08:00
xiu2
b14b3657b7 README.md 2023-02-13 09:51:13 +08:00
xiu2
2634fe43ff README.md 2023-02-13 09:45:12 +08:00
xiu2
b26fa89447 README.md 2023-02-12 19:15:27 +08:00
xiu2
a6b08bb4b8 README.md 2023-02-12 18:47:35 +08:00
xiu2
bd6fbb9f1f 修复 使用文件大小未知的下载测速地址时太快下载完成会导致测速结果异常的问题(如 speed.cloudflare.com 这种实时生成的) 2023-02-12 17:57:53 +08:00
xiu2
465a83462b README.md 2023-02-12 16:53:35 +08:00
xiu2
c045b914a4 修复 因修复上个 BUG 而产生的新 BUG(下载速度为 0.00)... 2023-02-12 16:35:30 +08:00
xiu2
d8db541c1f README.md 2023-02-11 18:28:43 +08:00
xiu2
65018cd415 README.md 2023-02-11 13:08:08 +08:00
charSLee013
f5ce273688 修复 某些情况下下载测速数值过大的问题 (#290)
* fix the speed measurement value is too large

* fix break when err == io.EOF
2023-02-11 12:40:06 +08:00
xiu2
ed1d512d65 README.md 2023-02-04 19:08:20 +08:00
xiu2
00d3e21e1b README.md 2023-02-02 16:27:45 +08:00
xiu2
2662b1affb README.md 2023-02-02 15:40:43 +08:00
xiu2
fa1940fe91 README.md 2023-02-02 15:38:09 +08:00
xiu2
0a9fb30671 README.md 2023-02-02 11:30:13 +08:00
xiu2
741f080c54 README.md 2023-02-01 13:32:24 +08:00
xiu2
2c4d115b83 README.md 2023-01-31 20:50:48 +08:00
xiu2
bd16488cfb README.md 2023-01-31 20:43:09 +08:00
xiu2
a1ae4f8e45 新增 指定IP段数据(-ip) 参数; 新增 延迟测速时显示可用 IP 数量; 新增 有效状态代码(HTTPing 模式所用) 参数; 优化 HTTPing 延迟测速模式; 优化 匹配指定地区 功能 2023-01-31 20:11:13 +08:00
kaka
f4f1fdcd80 新增 HTTPing 延迟测速模式(beta); 新增 IP 机场三字码 colo 筛选功能(beta) (#282)
这两个功能目前仅为测试版,后续会大幅改动,以最终成品为准~
2023-01-31 12:48:28 +08:00
xiu2
562789aa15 更新 IP 段数据文件 2023-01-20 12:42:26 +08:00
xiu2
c54fb92537 README.md 2023-01-03 12:33:43 +08:00
xiu2
07ef3fbb8f 更新 IP 段数据文件 2023-01-02 12:47:11 +08:00
xiu2
12066eec6c README.md 2022-12-28 14:29:18 +08:00
xiu2
7079a6c7b7 README.md 2022-12-04 17:38:03 +08:00
xiu2
a2fa9dadb3 更新 IP 段数据文件 2022-12-04 17:30:42 +08:00
xiu2
f47ba5d06f README.md 2022-12-01 12:11:25 +08:00
xiu2
93430a08ef README.md 2022-12-01 12:09:16 +08:00
xiu2
295af17fc8 调整 help 文字细节 2022-11-24 11:51:31 +08:00
xiu2
25e8ad170b README.md 2022-11-23 10:35:16 +08:00
xiu2
b6e5438ec1 README.md 2022-11-20 01:07:40 +08:00
xiu2
5b7791b892 README.md 2022-11-20 00:58:59 +08:00
xiu2
0beea5df99 移除 软件参数介绍里的假墙字样(现在似乎已经没有了) 2022-11-20 00:58:50 +08:00
xiu2
882678e1eb README.md 2022-11-10 19:59:24 +08:00
xiu2
ffb2e54a7b README.md 2022-11-10 19:50:42 +08:00
xiu2
154cf5564d README.md 2022-11-08 16:34:37 +08:00
xiu2
c6449f6a4a 移除 自动检查版本更新 2022-11-08 15:52:23 +08:00
xiu2
021914f975 新增 支持 IPv4 与 IPv6 混合测速; 移除 -ipv6 参数(已无用) 2022-11-08 15:47:19 +08:00
xiu2
ce22f6b2a4 更新 IPv6 段 2022-11-07 23:15:06 +08:00
xiu2
81c6a70a5c 优化 [禁止检查更新] 细节 2022-10-04 11:05:55 +08:00
mikuru996
ce15d4741a 优化 Windows 系统下按键退出代码 (#236) 2022-10-04 10:51:12 +08:00
mikuru996
32b5afaa88 新增 [禁止检查更新] 参数 (#235) 2022-10-04 10:48:09 +08:00
xiu2
73362a1d4c README.md 2022-08-07 11:50:30 +08:00
xiu2
82cfd0123f 更新 IPv6.txt 2022-06-18 21:25:39 +08:00
xiu2
a6f627f5fb 更新 IPv6.txt 2022-06-16 15:42:32 +08:00
xiu2
12695563d7 README.md 2022-05-24 18:07:49 +08:00
xiu2
d8316525ff README.md 2022-05-24 18:07:08 +08:00
xiu2
3c8b51db36 README.md 2022-05-17 13:51:55 +08:00
xiu2
1ed7fb7726 README.md 2022-05-02 15:55:36 +08:00
xiu2
e8c847d2c9 README.md 2022-05-02 08:23:51 +08:00
xiu2
eaaaa53c10 README.md 2022-04-04 10:41:28 +08:00
xiu2
270dcab04b README.md 2022-03-28 10:17:30 +08:00
xiu2
c1ba72b3a3 README.md 2022-03-28 10:05:41 +08:00
xiu2
01b105d42d 修复 在使用 -allip 参数时 /32 子网掩码识别为两个 IP 的问题 2022-02-19 17:36:13 +08:00
xiu2
e3c0a3a742 README.md 2022-02-19 16:33:53 +08:00
XIU2
7f6e374ae4 Update .gitattributes 2022-02-10 08:11:22 +08:00
XIU2
b22e7fd4af Create .gitattributes 2022-02-10 08:09:05 +08:00
xiu2
2e20a7c279 README.md 2022-02-10 00:40:28 +08:00
26 changed files with 1757 additions and 574 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
script/*.* linguist-language=None

34
.github/ISSUE_TEMPLATE/01-bugReport.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: 反馈问题 (Bug report)
description: 软件报错等异常情况,或遇到预期之外的问题...
labels: 反馈问题
body:
- type: markdown
attributes:
value: |
发之前,请先搜下有没有类似的 [**Issues**](https://github.com/XIU2/CloudflareSpeedTest/issues)、[**Discussions**](https://github.com/XIU2/CloudflareSpeedTest/discussions) 问题(包括[**已关闭**](https://github.com/XIU2/CloudflareSpeedTest/issues?q=is%3Aissue+is%3Aclosed)的),请勿重复发起!
> [!NOTE]
> 注意!如果你反馈的问题和 CFST **软件本身功能无关**(如关于 Cloudflare CDN 的网络问题),请**前往讨论区** (💬 [**Discussions**](https://github.com/XIU2/CloudflareSpeedTest/discussions)) 发帖交流!(不合适的 Issues 会被转过去
****
- type: textarea
id: description
attributes:
label: 问题描述
description: 必填,最好写上 复现问题 的步骤,越详细越好,特别是一些复杂的问题
placeholder: 请输入...
validations:
required: true
- type: input
id: version
attributes:
label: 软件版本
description: 必填,可通过运行软件来获取版本信息(例如 v2.2.2
placeholder: 请输入...
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: 附加截图
description: 可选,也可以是一些错误代码
placeholder: 可在此粘贴图片,或点击下方 [Attach files by dragging & dropping, selecting or pasting them.] 文字来选择图片...

View File

@@ -0,0 +1,31 @@
name: 功能建议 (Feature request)
description: 有什么建议,或希望添加、完善某个功能...
labels: 功能建议
body:
- type: markdown
attributes:
value: |
发之前,请先搜下有没有类似的 [**Issues**](https://github.com/XIU2/CloudflareSpeedTest/issues)、[**Discussions**](https://github.com/XIU2/CloudflareSpeedTest/discussions) 问题(包括[**已关闭**](https://github.com/XIU2/CloudflareSpeedTest/issues?q=is%3Aissue+is%3Aclosed)的),请勿重复发起!
> [!NOTE]
> 注意!如果你提的功能建议和 CFST **软件本身功能无关**,请**前往讨论区** (💬 [**Discussions**](https://github.com/XIU2/CloudflareSpeedTest/discussions)) 发帖交流!
> [!TIP]
> 另外,不接受**个性化**的小众功能请求(即 **很少人** 或 **只有你自己** 才会用到的功能)
****
- type: textarea
id: description
attributes:
label: 功能需求
description: 必填,你要什么样的功能?
placeholder: 请输入...
validations:
required: true
- type: textarea
id: anticipation
attributes:
label: 预期目标
description: 必填,你希望该功能具体是什么样子的?如果能提供 示例/截图/代码 就更好了
placeholder: 请输入...
validations:
required: true

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: 前往讨论区 (💬 Discussions)
url: https://github.com/XIU2/CloudflareSpeedTest/discussions
about: Issues 仅用于对 CFST 本身的反馈问题、功能建议,其他话题请到 💬 Discussions 发帖讨论(不合适的 Issues 会被转过去

738
README.md

File diff suppressed because it is too large Load Diff

17
go.mod
View File

@@ -1,8 +1,17 @@
module CloudflareSpeedTest
module github.com/XIU2/CloudflareSpeedTest
go 1.14
go 1.18
require (
github.com/VividCortex/ewma v1.1.1
github.com/cheggaaa/pb/v3 v3.0.4
github.com/VividCortex/ewma v1.2.0
github.com/cheggaaa/pb/v3 v3.1.7
github.com/fatih/color v1.18.0
)
require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/sys v0.30.0 // indirect
)

35
go.sum
View File

@@ -1,17 +1,18 @@
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/cheggaaa/pb/v3 v3.0.4 h1:QZEPYOj2ix6d5oEg63fbHmpolrnNiwjUsk+h74Yt4bM=
github.com/cheggaaa/pb/v3 v3.0.4/go.mod h1:7rgWxLrAUcFMkvJuv09+DYi7mMUYi8nO9iOWcvGJPfw=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI=
github.com/cheggaaa/pb/v3 v3.1.7/go.mod h1:/Ji89zfVPeC/u5j8ukD0MBPHt2bzTYp74lQ7KlgFWTQ=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

3
ip.txt
View File

@@ -9,8 +9,7 @@
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/13
104.24.0.0/14
104.16.0.0/12
172.64.0.0/17
172.64.128.0/18
172.64.192.0/19

103
ipv6.txt
View File

@@ -1,13 +1,8 @@
2400:cb00:2049::/48
2400:cb00:f00e::/48
2606:4700::/32
2606:4700::6810:0/112
2606:4700:10::/96
2606:4700:10::6814:0/112
2606:4700:10::ac43:0/112
2606:4700:10::/48
2606:4700:130::/48
2606:4700:130:436c::/64
2606:4700:130:436c:6f75::/80
2606:4700:130:436c:6f75:6466::/96
2606:4700:130:436c:6f75:6466:6c61:0/112
2606:4700:3000::/48
2606:4700:3001::/48
2606:4700:3002::/48
@@ -48,43 +43,55 @@
2606:4700:3037::/48
2606:4700:3038::/48
2606:4700:3039::/48
2606:4700:3000::/96
2606:4700:3001::/96
2606:4700:3002::/96
2606:4700:3003::/96
2606:4700:3004::/96
2606:4700:3005::/96
2606:4700:3006::/96
2606:4700:3007::/96
2606:4700:3008::/96
2606:4700:3009::/96
2606:4700:3010::/96
2606:4700:3011::/96
2606:4700:3012::/96
2606:4700:3013::/96
2606:4700:3014::/96
2606:4700:3015::/96
2606:4700:3016::/96
2606:4700:3017::/96
2606:4700:3018::/96
2606:4700:3019::/96
2606:4700:3020::/96
2606:4700:3021::/96
2606:4700:3022::/96
2606:4700:3023::/96
2606:4700:3024::/96
2606:4700:3025::/96
2606:4700:3026::/96
2606:4700:3027::/96
2606:4700:3028::/96
2606:4700:3029::/96
2606:4700:3030::/96
2606:4700:3031::/96
2606:4700:3032::/96
2606:4700:3033::/96
2606:4700:3034::/96
2606:4700:3035::/96
2606:4700:3036::/96
2606:4700:3037::/96
2606:4700:3038::/96
2606:4700:3039::/96
2606:4700:a0::/48
2606:4700:a1::/48
2606:4700:a8::/48
2606:4700:a9::/48
2606:4700:a::/48
2606:4700:b::/48
2606:4700:c::/48
2606:4700:d0::/48
2606:4700:d1::/48
2606:4700:d::/48
2606:4700:e0::/48
2606:4700:e1::/48
2606:4700:e2::/48
2606:4700:e3::/48
2606:4700:e4::/48
2606:4700:e5::/48
2606:4700:e6::/48
2606:4700:e7::/48
2606:4700:e::/48
2606:4700:f1::/48
2606:4700:f2::/48
2606:4700:f3::/48
2606:4700:f4::/48
2606:4700:f5::/48
2606:4700:f::/48
2803:f800:50::/48
2803:f800:51::/48
2a06:98c1:3100::/48
2a06:98c1:3101::/48
2a06:98c1:3102::/48
2a06:98c1:3103::/48
2a06:98c1:3104::/48
2a06:98c1:3105::/48
2a06:98c1:3106::/48
2a06:98c1:3107::/48
2a06:98c1:3108::/48
2a06:98c1:3109::/48
2a06:98c1:310a::/48
2a06:98c1:310b::/48
2a06:98c1:310c::/48
2a06:98c1:310d::/48
2a06:98c1:310e::/48
2a06:98c1:310f::/48
2a06:98c1:3120::/48
2a06:98c1:3121::/48
2a06:98c1:3122::/48
2a06:98c1:3123::/48
2a06:98c1:3200::/48
2a06:98c1:50::/48
2a06:98c1:51::/48
2a06:98c1:54::/48
2a06:98c1:58::/48

327
main.go
View File

@@ -1,151 +1,176 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"runtime"
"time"
"CloudflareSpeedTest/task"
"CloudflareSpeedTest/utils"
)
var (
version, versionNew string
)
func init() {
var printVersion bool
var help = `
CloudflareSpeedTest ` + version + `
测试 Cloudflare CDN 所有 IP 的延迟和速度,获取最快 IP (IPv4+IPv6)
https://github.com/XIU2/CloudflareSpeedTest
参数:
-n 200
测速线程数量;越多测速越快,性能弱的设备 (如路由器) 请勿太高;(默认 200 最多 1000)
-t 4
延迟测速次数;单个 IP 延迟测速次数,为 1 时将过滤丢包的IPTCP协议(默认 4 次)
-tp 443
指定测速端口;延迟测速/下载测速时使用的端口;(默认 443 端口)
-dn 10
下载测速数量;延迟测速并排序后,从最低延迟起下载测速的数量(默认 10 )
-dt 10
下载测速时间;单个 IP 下载测速最长时间,不能太短;(默认 10 秒)
-url https://cf.xiu2.xyz/url
下载测速地址;用来下载测速的 Cloudflare CDN 文件地址,默认地址不保证可用性,建议自建;
-tl 200
平均延迟上限;只输出低于指定平均延迟的 IP可与其他上限/下限搭配;(默认 9999 ms)
-tll 40
平均延迟下限;只输出高于指定平均延迟的 IP可与其他上限/下限搭配、过滤假墙 IP(默认 0 ms)
-sl 5
下载速度下限;只输出高于指定下载速度的 IP凑够指定数量 [-dn] 才会停止测速;(默认 0.00 MB/s)
-p 10
显示结果数量;测速后直接显示指定数量的结果,为 0 时不显示结果直接退出;(默认 10 个)
-f ip.txt
IP段数据文件如路径含有空格请加上引号支持其他 CDN IP段(默认 ip.txt)
-o result.csv
写入结果文件;如路径含有空格请加上引号;值为空时不写入文件 [-o ""](默认 result.csv)
-dd
禁用下载测速;禁用后测速结果会按延迟排序 (默认按下载速度排序)(默认 启用)
-ipv6
IPv6测速模式确保 IP 段数据文件内只包含 IPv6 IP段软件不支持同时测速 IPv4+IPv6(默认 IPv4)
-allip
测速全部的IP对 IP 段中的每个 IP (仅支持 IPv4) 进行测速;(默认 每个 IP 段随机测速一个 IP)
-v
打印程序版本+检查版本更新
-h
打印帮助说明
`
var minDelay, maxDelay, downloadTime int
flag.IntVar(&task.Routines, "n", 200, "测速线程数量")
flag.IntVar(&task.PingTimes, "t", 4, "延迟测速次数")
flag.IntVar(&task.TCPPort, "tp", 443, "指定测速端口")
flag.IntVar(&maxDelay, "tl", 9999, "平均延迟上限")
flag.IntVar(&minDelay, "tll", 0, "平均延迟下限")
flag.IntVar(&downloadTime, "dt", 10, "下载测速时间")
flag.IntVar(&task.TestCount, "dn", 10, "下载测速数量")
flag.StringVar(&task.URL, "url", "https://cf.xiu2.xyz/url", "下载测速地址")
flag.BoolVar(&task.Disable, "dd", false, "禁用下载测速")
flag.BoolVar(&task.IPv6, "ipv6", false, "启用IPv6")
flag.BoolVar(&task.TestAll, "allip", false, "测速全部 IP")
flag.StringVar(&task.IPFile, "f", "ip.txt", "IP 数据文件")
flag.Float64Var(&task.MinSpeed, "sl", 0, "下载速度下限")
flag.IntVar(&utils.PrintNum, "p", 10, "显示结果数量")
flag.StringVar(&utils.Output, "o", "result.csv", "输出结果文件")
flag.BoolVar(&printVersion, "v", false, "打印程序版本")
flag.Usage = func() { fmt.Print(help) }
flag.Parse()
if task.MinSpeed > 0 && time.Duration(maxDelay)*time.Millisecond == utils.InputMaxDelay {
fmt.Println("[小提示] 在使用 [-sl] 参数时,建议搭配 [-tl] 参数,以避免因凑不够 [-dn] 数量而一直测速...")
}
utils.InputMaxDelay = time.Duration(maxDelay) * time.Millisecond
utils.InputMinDelay = time.Duration(minDelay) * time.Millisecond
task.Timeout = time.Duration(downloadTime) * time.Second
if printVersion {
println(version)
fmt.Println("检查版本更新中...")
checkUpdate()
if versionNew != "" {
fmt.Printf("*** 发现新版本 [%s]!请前往 [https://github.com/XIU2/CloudflareSpeedTest] 更新! ***", versionNew)
} else {
fmt.Println("当前为最新版本 [" + version + "]")
}
os.Exit(0)
}
}
func main() {
go checkUpdate() // 检查版本更新
task.InitRandSeed() // 置随机数种子
fmt.Printf("# XIU2/CloudflareSpeedTest %s \n\n", version)
// 开始延迟测速
pingData := task.NewPing().Run().FilterDelay()
// 开始下载测速
speedData := task.TestDownloadSpeed(pingData)
utils.ExportCsv(speedData)
speedData.Print(task.IPv6)
if versionNew != "" {
fmt.Printf("\n*** 发现新版本 [%s]!请前往 [https://github.com/XIU2/CloudflareSpeedTest] 更新! ***\n", versionNew)
}
endPrint()
}
func endPrint() {
if utils.NoPrintResult() {
return
}
if runtime.GOOS == "windows" { // 如果是 Windows 系统,则需要按下 回车键 或 Ctrl+C 退出(避免通过双击运行时,测速完毕后直接关闭)
fmt.Printf("按下 回车键 或 Ctrl+C 退出。")
var pause int
fmt.Scanln(&pause)
}
}
// 检查更新
func checkUpdate() {
timeout := 10 * time.Second
client := http.Client{Timeout: timeout}
res, err := client.Get("https://api.xiu2.xyz/ver/cloudflarespeedtest.txt")
if err != nil {
return
}
// 读取资源数据 body: []byte
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return
}
// 关闭资源流
defer res.Body.Close()
if string(body) != version {
versionNew = string(body)
}
}
package main
import (
"flag"
"fmt"
"io"
"net/http"
"os"
"runtime"
"time"
"github.com/XIU2/CloudflareSpeedTest/task"
"github.com/XIU2/CloudflareSpeedTest/utils"
)
var (
version, versionNew string
)
func init() {
var printVersion bool
var help = `
CloudflareSpeedTest ` + version + `
测试各个 CDN 或网站所有 IP 的延迟和速度,获取最快 IP (IPv4+IPv6)
https://github.com/XIU2/CloudflareSpeedTest
参数:
-n 200
延迟测速线程;越多延迟测速越快,性能弱的设备 (如路由器) 请勿太高;(默认 200 最多 1000)
-t 4
延迟测速次数;单个 IP 延迟测速次数;(默认 4 次)
-dn 10
下载测速数量;延迟测速并排序后,从最低延迟起下载测速的数量;(默认 10 个)
-dt 10
下载测速时间;单个 IP 下载测速最长时间,不能太短(默认 10 )
-tp 443
指定测速端口;延迟测速/下载测速时使用的端口;(默认 443 端口)
-url https://cf.xiu2.xyz/url
指定测速地址;延迟测速(HTTPing)/下载测速时使用的地址,默认地址不保证可用性,建议自建;
-httping
切换测速模式;延迟测速模式改为 HTTP 协议,所用测试地址为 [-url] 参数;(默认 TCPing)
-httping-code 200
有效状态代码HTTPing 延迟测速时网页返回的有效 HTTP 状态码,仅限一个;(默认 200 301 302)
-cfcolo HKG,KHH,NRT,LAX,SEA,SJC,FRA,MAD
匹配指定地区IATA 机场地区码或国家/城市码,英文逗号分隔,仅 HTTPing 模式可用;(默认 所有地区)
-tl 200
平均延迟上限;只输出低于指定平均延迟的 IP各上下限条件可搭配使用(默认 9999 ms)
-tll 40
平均延迟下限;只输出高于指定平均延迟的 IP(默认 0 ms)
-tlr 0.2
丢包几率上限;只输出低于/等于指定丢包率的 IP范围 0.00~1.000 过滤掉任何丢包的 IP(默认 1.00)
-sl 5
下载速度下限;只输出高于指定下载速度的 IP凑够指定数量 [-dn] 才会停止测速;(默认 0.00 MB/s)
-p 10
显示结果数量;测速后直接显示指定数量的结果,为 0 时不显示结果直接退出;(默认 10 个)
-f ip.txt
IP段数据文件如路径含有空格请加上引号支持其他 CDN IP段(默认 ip.txt)
-ip 1.1.1.1,2.2.2.2/24,2606:4700::/32
指定IP段数据直接通过参数指定要测速的 IP 段数据,英文逗号分隔;(默认 空)
-o result.csv
写入结果文件;如路径含有空格请加上引号;值为空时不写入文件 [-o ""](默认 result.csv)
-dd
禁用下载测速;禁用后测速结果会按延迟排序 (默认按下载速度排序)(默认 启用)
-allip
测速全部的IP对 IP 段中的每个 IP (仅支持 IPv4) 进行测速;(默认 每个 /24 段随机测速一个 IP)
-debug
调试输出模式;会在一些非预期情况下输出更多日志以便判断原因;(默认 关闭)
-v
打印程序版本 + 检查版本更新
-h
打印帮助说明
`
var minDelay, maxDelay, downloadTime int
var maxLossRate float64
flag.IntVar(&task.Routines, "n", 200, "延迟测速线程")
flag.IntVar(&task.PingTimes, "t", 4, "延迟测速次数")
flag.IntVar(&task.TestCount, "dn", 10, "下载测速数量")
flag.IntVar(&downloadTime, "dt", 10, "下载测速时间")
flag.IntVar(&task.TCPPort, "tp", 443, "指定测速端口")
flag.StringVar(&task.URL, "url", "https://cf.xiu2.xyz/url", "指定测速地址")
flag.BoolVar(&task.Httping, "httping", false, "切换测速模式")
flag.IntVar(&task.HttpingStatusCode, "httping-code", 0, "有效状态代码")
flag.StringVar(&task.HttpingCFColo, "cfcolo", "", "匹配指定地区")
flag.IntVar(&maxDelay, "tl", 9999, "平均延迟上限")
flag.IntVar(&minDelay, "tll", 0, "平均延迟下限")
flag.Float64Var(&maxLossRate, "tlr", 1, "丢包几率上限")
flag.Float64Var(&task.MinSpeed, "sl", 0, "下载速度下限")
flag.IntVar(&utils.PrintNum, "p", 10, "显示结果数量")
flag.StringVar(&task.IPFile, "f", "ip.txt", "IP段数据文件")
flag.StringVar(&task.IPText, "ip", "", "指定IP段数据")
flag.StringVar(&utils.Output, "o", "result.csv", "输出结果文件")
flag.BoolVar(&task.Disable, "dd", false, "禁用下载测速")
flag.BoolVar(&task.TestAll, "allip", false, "测速全部 IP")
flag.BoolVar(&utils.Debug, "debug", false, "调试输出模式")
flag.BoolVar(&printVersion, "v", false, "打印程序版本")
flag.Usage = func() { fmt.Print(help) }
flag.Parse()
if task.MinSpeed > 0 && time.Duration(maxDelay)*time.Millisecond == utils.InputMaxDelay {
utils.Yellow.Println("[提示] 在使用 [-sl] 参数时,建议搭配 [-tl] 参数,以避免因凑不够 [-dn] 数量而一直测速...")
}
utils.InputMaxDelay = time.Duration(maxDelay) * time.Millisecond
utils.InputMinDelay = time.Duration(minDelay) * time.Millisecond
utils.InputMaxLossRate = float32(maxLossRate)
task.Timeout = time.Duration(downloadTime) * time.Second
task.HttpingCFColomap = task.MapColoMap()
if printVersion {
println(version)
fmt.Println("检查版本更新中...")
checkUpdate()
if versionNew != "" {
utils.Yellow.Printf("*** 发现新版本 [%s]!请前往 [https://github.com/XIU2/CloudflareSpeedTest] 更新! ***", versionNew)
} else {
utils.Green.Println("当前为最新版本 [" + version + "]")
}
os.Exit(0)
}
}
func main() {
task.InitRandSeed() // 置随机数种子
fmt.Printf("# XIU2/CloudflareSpeedTest %s \n\n", version)
// 开始延迟测速 + 过滤延迟/丢包
pingData := task.NewPing().Run().FilterDelay().FilterLossRate()
// 开始下载测速
speedData := task.TestDownloadSpeed(pingData)
utils.ExportCsv(speedData) // 输出文件
speedData.Print() // 打印结果
endPrint() // 根据情况选择退出方式(针对 Windows
}
// 根据情况选择退出方式(针对 Windows
func endPrint() {
if utils.NoPrintResult() { // 如果不需要打印测速结果,则直接退出
return
}
if runtime.GOOS == "windows" { // 如果是 Windows 系统,则需要按下 回车键 或 Ctrl+C 退出(避免通过双击运行时,测速完毕后直接关闭)
fmt.Printf("按下 回车键 或 Ctrl+C 退出。")
fmt.Scanln()
}
}
// 检查更新
func checkUpdate() {
timeout := 10 * time.Second
client := http.Client{Timeout: timeout}
res, err := client.Get("https://api.xiu2.xyz/ver/cloudflarespeedtest.txt")
if err != nil {
return
}
// 读取资源数据 body: []byte
body, err := io.ReadAll(res.Body)
if err != nil {
return
}
// 关闭资源流
defer res.Body.Close()
if string(body) != version {
versionNew = string(body)
}
}

View File

@@ -1,23 +1,38 @@
# XIU2/CloudflareSpeedTest - Script
# XIU2/CloudflareSpeedTest - Script(脚本)
这里都是一些基于 **XIU2/CloudflareSpeedTest** 并**扩展更多功能**的脚本。
有什么现有脚本功能上的建议可以告诉我,如果你有一些自用好用的脚本也可以通过 [**Issues**](https://github.com/XIU2/CloudflareSpeedTest/issues) 或 Pull requests 发给我添加到这里让更多人用到(会标注作者的~
> 小提示:点击↖左上角的三横杠图标按钮即可查看目录~
这里都是一些通过调用 **CFST** 并**扩展实现更多个性化功能**的脚本。
****
## 📑 cfst_hosts.sh / cfst_hosts.bat (已内置)
> [!TIP]
> 我之所以将 CFST 制作为一个**命令行程序**,就是考虑到**通用性**,因为毕竟不可能把所有需求都塞到软件内(特别是一些**个性化、小众**的需求),这样增加维护难度和精力不说,还会导致软件异常臃肿(`“变成我讨厌的样子”`),而命令行程序的优势之一就在于**可以很方便的和其他软件、脚本搭配使用**。
运行 CloudflareST 获得最快 IP 后,脚本会替换 Hosts 文件中的旧 CDN IP。
比如像下面这些我写的几个脚本,就是把一些需求以外置脚本方式实现。
> **使用说明https://github.com/XIU2/CloudflareSpeedTest/issues/42**
> 即脚本调用 CFST 测速并获取结果,然后***按照自己的需求自由决定***如何处理得到的测速结果(比如修改 Hosts 等)。
总的来说,我写的这几个脚本都比较简单,功能也很单一,除了满足部分用户的需求外,***更像是一个 CFST 与脚本搭配使用的示例参考***,对于一些会写脚本、软件的用户来说,完全可以**自给自足**来实现一些个性化需求。
当然,如果你有一些自用好用的脚本也可以通过 [**Issues**](https://github.com/XIU2/CloudflareSpeedTest/issues)、[**Discussions**](https://github.com/XIU2/CloudflareSpeedTest/discussions) 或 **Pull requests** 发给我添加到这里让更多人用到!
> 小提示:点击↗右上角的三横杠图标按钮即可查看目录~
****
## 📑 cfst_hosts.sh / cfst_hosts.bat (已内置压缩包)
脚本会运行 CFST 获得最快 IP并替换掉 Hosts 文件中的旧 CDN IP。
> **作者:**[@XIU2](https://github.com/xiu2)
> **使用说明/问题反馈https://github.com/XIU2/CloudflareSpeedTest/discussions/312**
<details>
<summary><code><strong>「 更新日志」</strong></code></summary>
****
#### 2021年12月17日,版本 v1.0.6
#### 2025年12月15日,版本 v1.0.5 (cfst_hosts.bat)
- **1. 修复** CFST新版本下获取不到第一行 IP 的问题
#### 2021年12月17日版本 v1.0.4
- **1. 优化** [找不到满足条件的 IP 就一直循环测速] 功能,在指定下载测速下限时没有重新测速的问题(默认注释)
#### 2021年12月17日版本 v1.0.3
@@ -37,18 +52,22 @@
****
## 📑 cfst_3proxy.bat (已内置)
## 📑 cfst_3proxy.bat (已内置压缩包)
脚本的作用为 CloudflareST 测速后获取最快 IP 并替换 3Proxy 配置文件中的 Cloudflare CDN IP。
脚本会运行 CFST 测速后获取最快 IP 并替换 3Proxy 配置文件中的 Cloudflare CDN IP。
可以把所有 Cloudflare CDN IP 都重定向至最快 IP实现一劳永逸的加速所有使用 Cloudflare CDN 的网站(不需要一个个添加域名到 Hosts 了)。
> **使用说明:https://github.com/XIU2/CloudflareSpeedTest/discussions/71**
> **作者:**[@XIU2](https://github.com/xiu2)
> **使用说明/问题反馈https://github.com/XIU2/CloudflareSpeedTest/discussions/71**
<details>
<summary><code><strong>「 更新日志」</strong></code></summary>
****
#### 2025年12月15日版本 v1.0.6
- **1. 修复** CFST新版本下获取不到第一行 IP 的问题
#### 2021年12月17日版本 v1.0.5
- **1. 优化** [找不到满足条件的 IP 就一直循环测速] 功能,在指定下载测速下限时没有重新测速的问题(默认注释)
@@ -72,17 +91,45 @@
****
## 📑 cfst_ddns.sh / cfst_ddns.bat
## 📑 cfst_dnspod.sh
如果你的域名托管在 Cloudflare则可以通过 Cloudflare 官方提供的 API 来自动更新域名解析记录!
如果你的域名托管在 **dnspod**,则可以通过 dnspod 官方提供的 API 来自动更新域名解析记录!
脚本会运行 CFST 测速获得最快 IP并通过 Cloudflare API 来更新域名解析记录为这个最快 IP。
> **使用说明:https://github.com/XIU2/CloudflareSpeedTest/issues/40**
> **作者:**[@imashen](https://github.com/imashen)
> **使用说明/问题反馈https://github.com/XIU2/CloudflareSpeedTest/pull/533**
<details>
<summary><code><strong>「 更新日志」</strong></code></summary>
****
#### 2024年08月06日版本 v1.0.0
- **1. 发布** 第一个版本
</details>
****
## 📑 cfst_ddns.sh / cfst_ddns.bat
如果你的域名托管在 **Cloudflare**,则可以通过 Cloudflare 官方提供的 API 来自动更新域名解析记录!
脚本会运行 CFST 测速获得最快 IP并通过 Cloudflare API 来更新域名解析记录为这个最快 IP。
> **作者:**[@XIU2](https://github.com/xiu2)
> **使用说明/问题反馈https://github.com/XIU2/CloudflareSpeedTest/discussions/481**
<details>
<summary><code><strong>「 更新日志」</strong></code></summary>
****
#### 2025年12月15日版本 v1.0.6 (cfst_ddns.bat)
- **1. 修复** CFST新版本下获取不到第一行 IP 的问题
#### 2024年10月06日版本 v1.0.5
- **1. 新增** 支持 API 令牌方式(相比 API 密钥这种全局权限的API 令牌可以自由控制权限)
#### 2021年12月17日版本 v1.0.4
- **1. 新增** 找不到满足条件的 IP 就一直循环测速功能(默认注释)
- **2. 优化** 代码
@@ -103,7 +150,29 @@
****
## 📑 cfst_dnsmasq.sh
脚本会运行 CFST 测速后获取最快 IP 并替换 dnsmasq 配置文件中的旧 Cloudflare CDN IP。
> **作者:**[@Sving1024](https://github.com/Sving1024)
> **使用说明/问题反馈https://github.com/XIU2/CloudflareSpeedTest/discussions/566**
<details>
<summary><code><strong>「 更新日志」</strong></code></summary>
****
#### 2025年01月22日版本 v1.0.1
- **1. 修复** IPv6 的问题
#### 2024年12月28日版本 v1.0.0
- **1. 发布** 第一个版本
</details>
****
## 功能建议/问题反馈
如果你遇到什么问题,可以先去 [**Issues**](https://github.com/XIU2/CloudflareSpeedTest/issues) 里看看是否有别人问过了(记得去看下 [**Closed**](https://github.com/XIU2/CloudflareSpeedTest/issues?q=is%3Aissue+is%3Aclosed) 的)
如果没找到类似问题,请新开个 [**Issues**](https://github.com/XIU2/CloudflareSpeedTest/issues/new) 来告诉我!
如果这些脚本使用过程中你遇到什么问题,可以先去脚本对应的 **`使用说明`** 帖子里看看是否有别人问过了
如果没找到类似问题,那么就在脚本对应的 **`使用说明`** 帖子里直接评论问作者吧。

View File

@@ -1,6 +1,6 @@
:: --------------------------------------------------------------
:: <09><>Ŀ: CloudflareSpeedTest <20>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD> 3Proxy
:: <09>汾: 1.0.5
:: <09>汾: 1.0.6
:: <09><><EFBFBD><EFBFBD>: XIU2
:: <09><>Ŀ: https://github.com/XIU2/CloudflareSpeedTest
:: --------------------------------------------------------------
@@ -36,7 +36,7 @@ if '%errorlevel%' NEQ '0' (
::<3A><><EFBFBD><EFBFBD> nowip_3proxy.txt <20>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڣ<EFBFBD>˵<EFBFBD><CBB5><EFBFBD>ǵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD>иýű<C3BD>
if not exist "nowip_3proxy.txt" (
echo <20>ýű<C3BD><C5B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ CloudflareST <20><><EFBFBD>ٺ<EFBFBD><D9BA><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD> IP <20><><EFBFBD>滻 3Proxy <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>е<EFBFBD> Cloudflare CDN IP<49><50>
echo <20>ýű<C3BD><C5B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ CFST <20><><EFBFBD>ٺ<EFBFBD><D9BA><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD> IP <20><><EFBFBD>滻 3Proxy <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>е<EFBFBD> Cloudflare CDN IP<49><50>
echo <20><><EFBFBD>԰<EFBFBD><D4B0><EFBFBD><EFBFBD><EFBFBD> Cloudflare CDN IP <20><><EFBFBD>ض<EFBFBD><D8B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP<49><50>ʵ<EFBFBD><CAB5>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD>ݵļ<DDB5><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9> Cloudflare CDN <20><><EFBFBD><EFBFBD>վ<EFBFBD><D5BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫһ<D2AA><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Hosts <20>ˣ<EFBFBD><CBA3><EFBFBD>
echo ʹ<><CAB9>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD>Ķ<EFBFBD><C4B6><EFBFBD>https://github.com/XIU2/CloudflareSpeedTest/discussions/71
echo.
@@ -55,48 +55,46 @@ echo
:RESET
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD><D4BC><EFBFBD><EFBFBD>ӡ<EFBFBD><D3A1>޸<EFBFBD> CloudflareST <20><><EFBFBD><EFBFBD><EFBFBD>в<EFBFBD><D0B2><EFBFBD><EFBFBD><EFBFBD>echo.| <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6>س<EFBFBD><D8B3>˳<EFBFBD><CBB3><EFBFBD><EFBFBD>򣨲<EFBFBD><F2A3A8B2><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD> -p 0 <20><><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>
echo.|CloudflareST.exe -o "result_3proxy.txt"
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD><D4BC><EFBFBD><EFBFBD>ӡ<EFBFBD><D3A1>޸<EFBFBD> CFST <20><><EFBFBD><EFBFBD><EFBFBD>в<EFBFBD><D0B2><EFBFBD><EFBFBD><EFBFBD>echo.| <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6>س<EFBFBD><D8B3>˳<EFBFBD><CBB3><EFBFBD><EFBFBD>򣨲<EFBFBD><F2A3A8B2><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD> -p 0 <20><><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>
echo.|cfst.exe -o "result_3proxy.txt"
:: <20>жϽ<D0B6><CFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD>ڣ<EFBFBD><DAA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˵<EFBFBD><CBB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ 0
if not exist result_3proxy.txt (
echo.
echo CloudflareST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
echo CFST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
goto :STOP
)
:: <20><>ȡ<EFBFBD><C8A1>һ<EFBFBD>е<EFBFBD><D0B5><EFBFBD><EFBFBD><EFBFBD> IP
for /f "tokens=1 delims=," %%i in (result_3proxy.txt) do (
set /a n+=1
If !n!==2 (
set bestip=%%i
goto :END
)
for /f "skip=1 tokens=1 delims=," %%i in ('more result_3proxy.txt') do (
SET bestip=%%i
goto :END
)
:END
:: <20>жϸոջ<D5B8>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20>Ƿ<EFBFBD>Ϊ<EFBFBD>գ<EFBFBD><D5A3>Լ<EFBFBD><D4BC>Ƿ<EFBFBD><C7B7>;<EFBFBD> IP һ<><D2BB>
if "%bestip%"=="" (
echo.
echo CloudflareST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
echo CFST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
goto :STOP
)
if "%bestip%"=="%nowip%" (
echo.
echo CloudflareST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
echo CFST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
goto :STOP
)
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD><EFBFBD> "<22>Ҳ<EFBFBD><D2B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><>һֱѭ<D6B1><D1AD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȥ" <20><><EFBFBD><EFBFBD>Ҫ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD>
:: <20><><EFBFBD>ǵ<EFBFBD><C7B5><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٶ<EFBFBD><D9B6><EFBFBD><EFBFBD>ޣ<EFBFBD><DEA3><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><>û<EFBFBD>ҵ<EFBFBD>ʱ<EFBFBD><CAB1>CloudflareST <20>ͻ<EFBFBD><CDBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><><EFBFBD><EFBFBD>
:: <20><><EFBFBD>ǵ<EFBFBD><C7B5><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٶ<EFBFBD><D9B6><EFBFBD><EFBFBD>ޣ<EFBFBD><DEA3><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><>û<EFBFBD>ҵ<EFBFBD>ʱ<EFBFBD><CAB1>CFST <20>ͻ<EFBFBD><CDBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><><EFBFBD><EFBFBD>
:: <20><><EFBFBD>˵<EFBFBD><CBB5><EFBFBD>ָ<EFBFBD><D6B8> -sl <20><><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>Ҫ<EFBFBD>Ƴ<EFBFBD><C6B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD><CEB4>뿪ͷ<EBBFAA><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD> :: ð<><C3B0>ע<EFBFBD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>жϣ<D0B6><CFA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD><D8B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>10 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ 11<31><31>
::set /a v=0
::for /f %%a in ('type result_3proxy.txt') do set /a v+=1
::if %v% GTR 11 (
:: echo.
:: echo CloudflareST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD>û<EFBFBD><C3BB><EFBFBD>ҵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP<49><50><EFBFBD><EFBFBD><EFBFBD>²<EFBFBD><C2B2><EFBFBD>...
:: echo CFST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD>û<EFBFBD><C3BB><EFBFBD>ҵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP<49><50><EFBFBD><EFBFBD><EFBFBD>²<EFBFBD><C2B2><EFBFBD>...
:: goto :RESET
::)

View File

@@ -1,38 +1,42 @@
:: --------------------------------------------------------------
:: 项目: CloudflareSpeedTest 自动更新域名解析记录
:: 版本: 1.0.4
:: 版本: 1.0.6
:: 作者: XIU2
:: 项目: https://github.com/XIU2/CloudflareSpeedTest
:: --------------------------------------------------------------
@echo off
Setlocal Enabledelayedexpansion
:: 这里可以自己添加、修改 CloudflareST 的运行参数echo.| 的作用是自动回车退出程序(不再需要加上 -p 0 参数了)
echo.|CloudflareST.exe -o "result_ddns.txt"
:: 这里可以自己添加、修改 CFST 的运行参数echo.| 的作用是自动回车退出程序(不再需要加上 -p 0 参数了)
echo.|cfst.exe -o "result_ddns.txt"
:: 判断结果文件是否存在,如果不存在说明结果为 0
if not exist result_ddns.txt (
echo.
echo CloudflareST 测速结果 IP 数量为 0跳过下面步骤...
echo CFST 测速结果 IP 数量为 0跳过下面步骤...
goto :END
)
for /f "tokens=1 delims=," %%i in (result_ddns.txt) do (
Set /a n+=1
If !n!==2 (
Echo %%i
if "%%i"=="" (
echo.
echo CloudflareST 测速结果 IP 数量为 0跳过下面步骤...
goto :END
)
curl -X PUT "https://api.cloudflare.com/client/v4/zones/域名ID/dns_records/域名解析记录ID" ^
-H "X-Auth-Email: 账号邮箱" ^
-H "X-Auth-Key: 前面获取的 API 令牌" ^
-H "Content-Type: application/json" ^
--data "{\"type\":\"A\",\"name\":\"完整域名\",\"content\":\"%%i\",\"ttl\":1,\"proxied\":true}"
for /f "skip=1 tokens=1 delims=," %%i in (result_ddns.txt) do (
Echo %%i
if "%%i"=="" (
echo.
echo CFST 测速结果 IP 数量为 0跳过下面步骤...
goto :END
)
:: API 密钥方式(全局权限)
curl -X PUT "https://api.cloudflare.com/client/v4/zones/域名ID/dns_records/域名解析记录ID" ^
-H "X-Auth-Email: 账号邮箱" ^
-H "X-Auth-Key: 前面获取的 API 密钥" ^
-H "Content-Type: application/json" ^
--data "{\"type\":\"A\",\"name\":\"完整域名\",\"content\":\"%%i\",\"ttl\":1,\"proxied\":true}"
:: API 令牌方式(自定义权限),如果要使用这种方式,可以把上面的删除或注释,然后把下面的行首 "::" 注释符删除即可。
:: curl -X PUT "https://api.cloudflare.com/client/v4/zones/域名ID/dns_records/域名解析记录ID" ^
:: -H "Authorization: Bearer 前面获取的 API 令牌" ^
:: -H "Content-Type: application/json" ^
:: --data "{\"type\":\"A\",\"name\":\"完整域名\",\"content\":\"%%i\",\"ttl\":1,\"proxied\":true}"
goto :END
)
:END
pause

View File

@@ -3,7 +3,7 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# --------------------------------------------------------------
# 项目: CloudflareSpeedTest 自动更新域名解析记录
# 版本: 1.0.4
# 版本: 1.0.5
# 作者: XIU2
# 项目: https://github.com/XIU2/CloudflareSpeedTest
# --------------------------------------------------------------
@@ -17,10 +17,10 @@ _READ() {
[[ -z "${ZONE_ID}" ]] && echo -e "[错误] 缺少配置项 [ZONE_ID] !" && exit 1
DNS_RECORDS_ID=$(echo "${CONFIG}"|grep 'DNS_RECORDS_ID='|awk -F '=' '{print $NF}')
[[ -z "${DNS_RECORDS_ID}" ]] && echo -e "[错误] 缺少配置项 [DNS_RECORDS_ID] !" && exit 1
EMAIL=$(echo "${CONFIG}"|grep 'EMAIL='|awk -F '=' '{print $NF}')
[[ -z "${EMAIL}" ]] && echo -e "[错误] 缺少配置项 [EMAIL] !" && exit 1
KEY=$(echo "${CONFIG}"|grep 'KEY='|awk -F '=' '{print $NF}')
[[ -z "${KEY}" ]] && echo -e "[错误] 缺少配置项 [KEY] !" && exit 1
EMAIL=$(echo "${CONFIG}"|grep 'EMAIL='|awk -F '=' '{print $NF}')
[[ -z "${EMAIL}" ]] && echo -e "[信息] 缺少配置项 [EMAIL],由 [API 密钥] 方式转为 [API 令牌] 方式!"
TYPE=$(echo "${CONFIG}"|grep 'TYPE='|awk -F '=' '{print $NF}')
[[ -z "${TYPE}" ]] && echo -e "[错误] 缺少配置项 [TYPE] !" && exit 1
NAME=$(echo "${CONFIG}"|grep 'NAME='|awk -F '=' '{print $NF}')
@@ -32,22 +32,32 @@ _READ() {
}
_UPDATE() {
# 这里可以自己添加、修改 CloudflareST 的运行参数
./CloudflareST -o "result_ddns.txt"
# 这里可以自己添加、修改 CFST 的运行参数
./cfst -o "result_ddns.txt"
# 判断结果文件是否存在,如果不存在说明结果为 0
[[ ! -e "result_ddns.txt" ]] && echo "CloudflareST 测速结果 IP 数量为 0跳过下面步骤..." && exit 0
[[ ! -e "result_ddns.txt" ]] && echo "CFST 测速结果 IP 数量为 0跳过下面步骤..." && exit 0
CONTENT=$(sed -n "2,1p" result_ddns.txt | awk -F, '{print $1}')
if [[ -z "${CONTENT}" ]]; then
echo "CloudflareST 测速结果 IP 数量为 0跳过下面步骤..."
echo "CFST 测速结果 IP 数量为 0跳过下面步骤..."
exit 0
fi
curl -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${DNS_RECORDS_ID}" \
-H "X-Auth-Email: ${EMAIL}" \
-H "X-Auth-Key: ${KEY}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"${TYPE}\",\"name\":\"${NAME}\",\"content\":\"${CONTENT}\",\"ttl\":${TTL},\"proxied\":${PROXIED}}"
# 如果 EMAIL 变量是空的,那么就代表要使用 API 令牌方式
if [[ -n "${EMAIL}" ]]; then
# API 密钥方式(全局权限)
curl -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${DNS_RECORDS_ID}" \
-H "X-Auth-Email: ${EMAIL}" \
-H "X-Auth-Key: ${KEY}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"${TYPE}\",\"name\":\"${NAME}\",\"content\":\"${CONTENT}\",\"ttl\":${TTL},\"proxied\":${PROXIED}}"
else
# API 令牌方式(自定义权限)
curl -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${DNS_RECORDS_ID}" \
-H "Authorization: Bearer ${KEY}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"${TYPE}\",\"name\":\"${NAME}\",\"content\":\"${CONTENT}\",\"ttl\":${TTL},\"proxied\":${PROXIED}}"
fi
}
_READ

56
script/cfst_dnsmasq.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# --------------------------------------------------------------
# 项目: CloudflareSpeedTest 自动更新 dnsmasq 配置文件
# 版本: 1.0.1
# 作者: XIU2,Sving1024
# 项目: https://github.com/XIU2/CloudflareSpeedTest
# --------------------------------------------------------------
_UPDATE() {
echo -e "开始测速..."
BESTIP=""
BESTIP_IPV6="::"
# 这里可以自己添加、修改 CFST 的运行参数
./cfst -o "result_hosts.txt"
# 需要测速 IPv6 请取消注释
#./cfst -o "result_hosts_ipv6.txt" -f ipv6.txt
# 如果需要 "找不到满足条件的 IP 就一直循环测速下去",那么可以将下面的两个 exit 0 改为 _UPDATE 即可
[[ ! -e "result_hosts.txt" ]] && echo "CFST 测速结果 IP 数量为 0跳过下面步骤..." && exit 0
# 下面这行代码是 "找不到满足条件的 IP 就一直循环测速下去" 才需要的代码
# 考虑到当指定了下载速度下限,但一个满足全部条件的 IP 都没找到时CFST 就会输出所有 IP 结果
# 因此当你指定 -sl 参数时,需要移除下面这段代码开头的 # 井号注释符来做文件行数判断比如下载测速数量10 个,那么下面的值就设在为 11
#[[ $(cat result_hosts.txt|wc -l) > 11 ]] && echo "CFST 测速结果没有找到一个完全满足条件的 IP重新测速..." && _UPDATE
BESTIP=$(sed -n "2,1p" result_hosts.txt | awk -F, '{print $1}')
# 需要测速 IPv6 请取消注释
#BESTIP_IPV6=$(sed -n "2,1p" result_hosts_ipv6.txt | awk -F, '{print $1}')
if [[ -z "${BESTIP}" ]]; then
echo "CFST 测速结果 IP 数量为 0跳过下面步骤..."
exit 0
fi
echo ${BESTIP} > nowip_hosts.txt
echo -e "最优 IPv4 IP 为 ${BESTIP}\n"
# 需要测速 IPv6 请取消注释
#echo -e "最优 IPv6 IP 为 ${BESTIP_IPV6}\n"
[[ -f cloudflare.conf ]] && rm cloudflare.conf
cat site.conf | while read domain
do
if [[ ${domain:0:1} != "#" && ${domain} != "" ]]; then
echo "address=/${domain}/${BESTIP}" >> "cloudflare.conf"
echo "address=/${domain}/${BESTIP_IPV6}" >> "cloudflare.conf"
fi
done
[[ -f /etc/dnsmasq.d/cloudflare.conf ]] && rm /etc/dnsmasq.d/cloudflare.conf
cp cloudflare.conf /etc/dnsmasq.d/cloudflare.conf
systemctl restart dnsmasq.service
}
_UPDATE

106
script/cfst_dnspod.sh Normal file
View File

@@ -0,0 +1,106 @@
#!/bin/bash
# --------------------------------------------------------------
# 项目: CloudflareSpeedTest 自动更新Dnspod优选解析
# 版本: 1.0.0
# 作者: imashen
# --------------------------------------------------------------
# 清理历史残留
rm -f result4.csv result6.csv
# DNSPod API 凭据
dnspod_token="${API_TOKEN}"
dnspod_domain="${DOMAIN}"
dnspod_record="${SUB_DOMAIN}"
# DNSPod API URL
dnspod_api_url="https://dnsapi.cn"
# 获取记录 ID
get_record_id() {
local record_type=$1
local response
response=$(curl -s -X POST -d "login_token=$dnspod_token&format=json&domain=$dnspod_domain&record_type=$record_type" "$dnspod_api_url/Record.List")
local record_id
record_id=$(echo "$response" | jq -r --arg type "$record_type" '.records[] | select(.type == $type) | .id')
echo "$record_id"
}
# 创建 DNS 记录
create_dns_record() {
local record_type=$1
local ip_address=$2
local response
response=$(curl -s -X POST -d "login_token=$dnspod_token&format=json&domain=$dnspod_domain&sub_domain=$dnspod_record&record_type=$record_type&record_line=默认&value=$ip_address" "$dnspod_api_url/Record.Create")
local record_id
record_id=$(echo "$response" | jq -r '.record.id')
echo "$record_id"
}
# 更新 DNS 记录
update_dns_record() {
local record_id=$1
local record_type=$2
local ip_address=$3
curl -s -X POST -d "login_token=$dnspod_token&format=json&domain=$dnspod_domain&record_id=$record_id&sub_domain=$dnspod_record&record_type=$record_type&record_line=默认&value=$ip_address" "$dnspod_api_url/Record.Modify"
}
# 运行 CFST v4
./cfst -f ip.txt -n 500 -o result4.csv
# 读取 CSV 文件并提取优选 IPv4 地址
preferred_ipv4=$(awk -F, 'NR==2 {print $1}' result4.csv)
# 检查是否获取到了 IPv4 地址
if [ -z "$preferred_ipv4" ]; then
echo "Failed to get the preferred IPv4 address."
else
echo "BETTER IPv4: $preferred_ipv4"
# 获取 IPv4 记录 ID
ipv4_record_id=$(get_record_id "A")
if [ -n "$ipv4_record_id" ]; then
# 更新 IPv4 记录
update_dns_record "$ipv4_record_id" "A" "$preferred_ipv4"
echo "Updated DNSPod record with IPv4: $preferred_ipv4"
else
# 创建 IPv4 记录
new_ipv4_record_id=$(create_dns_record "A" "$preferred_ipv4")
if [ -n "$new_ipv4_record_id" ]; then
echo "Created DNSPod record with IPv4: $preferred_ipv4"
else
echo "Failed to create DNSPod record with IPv4."
fi
fi
fi
# 运行 CFST v6
./cfst -f ipv6.txt -n 500 -o result6.csv
# 读取 CSV 文件并提取优选 IPv6 地址
preferred_ipv6=$(awk -F, 'NR==2 {print $1}' result6.csv)
# 检查是否获取到了 IPv6 地址
if [ -z "$preferred_ipv6" ]; then
echo "Failed to get the preferred IPv6 address."
else
echo "BETTER IPv6: $preferred_ipv6"
# 获取 IPv6 记录 ID
ipv6_record_id=$(get_record_id "AAAA")
if [ -n "$ipv6_record_id" ]; then
# 更新 IPv6 记录
update_dns_record "$ipv6_record_id" "AAAA" "$preferred_ipv6"
echo "Updated DNSPod record with IPv6: $preferred_ipv6"
else
# 创建 IPv6 记录
new_ipv6_record_id=$(create_dns_record "AAAA" "$preferred_ipv6")
if [ -n "$new_ipv6_record_id" ]; then
echo "Created DNSPod record with IPv6: $preferred_ipv6"
else
echo "Failed to create DNSPod record with IPv6."
fi
fi
fi

View File

@@ -1,6 +1,6 @@
:: --------------------------------------------------------------
:: <09><>Ŀ: CloudflareSpeedTest <20>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD> Hosts
:: <09>汾: 1.0.4
:: <09>汾: 1.0.5
:: <09><><EFBFBD><EFBFBD>: XIU2
:: <09><>Ŀ: https://github.com/XIU2/CloudflareSpeedTest
:: --------------------------------------------------------------
@@ -36,7 +36,7 @@ if '%errorlevel%' NEQ '0' (
::<3A><><EFBFBD><EFBFBD> nowip_hosts.txt <20>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڣ<EFBFBD>˵<EFBFBD><CBB5><EFBFBD>ǵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD>иýű<C3BD>
if not exist "nowip_hosts.txt" (
echo <20>ýű<C3BD><C5B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ CloudflareST <20><><EFBFBD>ٺ<EFBFBD><D9BA><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD> IP <20><><EFBFBD>滻 Hosts <20>е<EFBFBD> Cloudflare CDN IP<49><50>
echo <20>ýű<C3BD><C5B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ CFST <20><><EFBFBD>ٺ<EFBFBD><D9BA><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD> IP <20><><EFBFBD>滻 Hosts <20>е<EFBFBD> Cloudflare CDN IP<49><50>
echo ʹ<><CAB9>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD>Ķ<EFBFBD><C4B6><EFBFBD>https://github.com/XIU2/CloudflareSpeedTest/issues/42#issuecomment-768273768
echo.
echo <20><>һ<EFBFBD><D2BB>ʹ<EFBFBD>ã<EFBFBD><C3A3><EFBFBD><EFBFBD>Ƚ<EFBFBD> Hosts <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Cloudflare CDN IP ͳһ<CDB3><D2BB>Ϊһ<CEAA><D2BB> IP<49><50>
@@ -55,48 +55,46 @@ echo
:RESET
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD><D4BC><EFBFBD><EFBFBD>ӡ<EFBFBD><D3A1>޸<EFBFBD> CloudflareST <20><><EFBFBD><EFBFBD><EFBFBD>в<EFBFBD><D0B2><EFBFBD><EFBFBD><EFBFBD>echo.| <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6>س<EFBFBD><D8B3>˳<EFBFBD><CBB3><EFBFBD><EFBFBD>򣨲<EFBFBD><F2A3A8B2><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD> -p 0 <20><><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>
echo.|CloudflareST.exe -o "result_hosts.txt"
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD><D4BC><EFBFBD><EFBFBD>ӡ<EFBFBD><D3A1>޸<EFBFBD> CFST <20><><EFBFBD><EFBFBD><EFBFBD>в<EFBFBD><D0B2><EFBFBD><EFBFBD><EFBFBD>echo.| <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6>س<EFBFBD><D8B3>˳<EFBFBD><CBB3><EFBFBD><EFBFBD>򣨲<EFBFBD><F2A3A8B2><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD> -p 0 <20><><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>
echo.|cfst.exe -o "result_hosts.txt"
:: <20>жϽ<D0B6><CFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD>ڣ<EFBFBD><DAA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˵<EFBFBD><CBB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ 0
if not exist result_hosts.txt (
echo.
echo CloudflareST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
echo CFST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
goto :STOP
)
:: <20><>ȡ<EFBFBD><C8A1>һ<EFBFBD>е<EFBFBD><D0B5><EFBFBD><EFBFBD><EFBFBD> IP
for /f "tokens=1 delims=," %%i in (result_hosts.txt) do (
SET /a n+=1
If !n!==2 (
SET bestip=%%i
goto :END
)
for /f "skip=1 tokens=1 delims=," %%i in ('more result_hosts.txt') do (
SET bestip=%%i
goto :END
)
:END
:: <20>жϸոջ<D5B8>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20>Ƿ<EFBFBD>Ϊ<EFBFBD>գ<EFBFBD><D5A3>Լ<EFBFBD><D4BC>Ƿ<EFBFBD><C7B7>;<EFBFBD> IP һ<><D2BB>
if "%bestip%"=="" (
echo.
echo CloudflareST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
echo CFST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
goto :STOP
)
if "%bestip%"=="%nowip%" (
echo.
echo CloudflareST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
echo CFST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD> IP <20><><EFBFBD><EFBFBD>Ϊ 0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B2BD>...
goto :STOP
)
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD><EFBFBD> "<22>Ҳ<EFBFBD><D2B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><>һֱѭ<D6B1><D1AD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȥ" <20><><EFBFBD><EFBFBD>Ҫ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD>
:: <20><><EFBFBD>ǵ<EFBFBD><C7B5><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٶ<EFBFBD><D9B6><EFBFBD><EFBFBD>ޣ<EFBFBD><DEA3><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><>û<EFBFBD>ҵ<EFBFBD>ʱ<EFBFBD><CAB1>CloudflareST <20>ͻ<EFBFBD><CDBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><><EFBFBD><EFBFBD>
:: <20><><EFBFBD>ǵ<EFBFBD><C7B5><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٶ<EFBFBD><D9B6><EFBFBD><EFBFBD>ޣ<EFBFBD><DEA3><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><>û<EFBFBD>ҵ<EFBFBD>ʱ<EFBFBD><CAB1>CFST <20>ͻ<EFBFBD><CDBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP <20><><EFBFBD><EFBFBD>
:: <20><><EFBFBD>˵<EFBFBD><CBB5><EFBFBD>ָ<EFBFBD><D6B8> -sl <20><><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>Ҫ<EFBFBD>Ƴ<EFBFBD><C6B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD><CEB4>뿪ͷ<EBBFAA><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD> :: ð<><C3B0>ע<EFBFBD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>жϣ<D0B6><CFA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD><D8B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>10 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ 11<31><31>
::set /a v=0
::for /f %%a in ('type result_hosts.txt') do set /a v+=1
::if %v% GTR 11 (
:: echo.
:: echo CloudflareST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD>û<EFBFBD><C3BB><EFBFBD>ҵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP<49><50><EFBFBD><EFBFBD><EFBFBD>²<EFBFBD><C2B2><EFBFBD>...
:: echo CFST <20><><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD>û<EFBFBD><C3BB><EFBFBD>ҵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> IP<49><50><EFBFBD><EFBFBD><EFBFBD>²<EFBFBD><C2B2><EFBFBD>...
:: goto :RESET
::)

View File

@@ -12,7 +12,7 @@ _CHECK() {
while true
do
if [[ ! -e "nowip_hosts.txt" ]]; then
echo -e "该脚本的作用为 CloudflareST 测速后获取最快 IP 并替换 Hosts 中的 Cloudflare CDN IP。\n使用前请先阅读https://github.com/XIU2/CloudflareSpeedTest/issues/42#issuecomment-768273848"
echo -e "该脚本的作用为 CFST 测速后获取最快 IP 并替换 Hosts 中的 Cloudflare CDN IP。\n使用前请先阅读https://github.com/XIU2/CloudflareSpeedTest/issues/42#issuecomment-768273848"
echo -e "第一次使用,请先将 Hosts 中所有 Cloudflare CDN IP 统一改为一个 IP。"
read -e -p "输入该 Cloudflare CDN IP 并回车(后续不再需要该步骤):" NOWIP
if [[ ! -z "${NOWIP}" ]]; then
@@ -31,21 +31,21 @@ _UPDATE() {
echo -e "开始测速..."
NOWIP=$(head -1 nowip_hosts.txt)
# 这里可以自己添加、修改 CloudflareST 的运行参数
./CloudflareST -o "result_hosts.txt"
# 这里可以自己添加、修改 CFST 的运行参数
./cfst -o "result_hosts.txt"
# 如果需要 "找不到满足条件的 IP 就一直循环测速下去",那么可以将下面的两个 exit 0 改为 _UPDATE 即可
[[ ! -e "result_hosts.txt" ]] && echo "CloudflareST 测速结果 IP 数量为 0跳过下面步骤..." && exit 0
[[ ! -e "result_hosts.txt" ]] && echo "CFST 测速结果 IP 数量为 0跳过下面步骤..." && exit 0
# 下面这行代码是 "找不到满足条件的 IP 就一直循环测速下去" 才需要的代码
# 考虑到当指定了下载速度下限,但一个满足全部条件的 IP 都没找到时CloudflareST 就会输出所有 IP 结果
# 考虑到当指定了下载速度下限,但一个满足全部条件的 IP 都没找到时CFST 就会输出所有 IP 结果
# 因此当你指定 -sl 参数时,需要移除下面这段代码开头的 # 井号注释符来做文件行数判断比如下载测速数量10 个,那么下面的值就设在为 11
#[[ $(cat result_hosts.txt|wc -l) > 11 ]] && echo "CloudflareST 测速结果没有找到一个完全满足条件的 IP重新测速..." && _UPDATE
#[[ $(cat result_hosts.txt|wc -l) > 11 ]] && echo "CFST 测速结果没有找到一个完全满足条件的 IP重新测速..." && _UPDATE
BESTIP=$(sed -n "2,1p" result_hosts.txt | awk -F, '{print $1}')
if [[ -z "${BESTIP}" ]]; then
echo "CloudflareST 测速结果 IP 数量为 0跳过下面步骤..."
echo "CFST 测速结果 IP 数量为 0跳过下面步骤..."
exit 0
fi
echo ${BESTIP} > nowip_hosts.txt

View File

@@ -12,7 +12,7 @@ _CHECK() {
while true
do
if [[ ! -e "nowip_hosts.txt" ]]; then
echo -e "该脚本的作用为 CloudflareST 测速后获取最快 IP 并替换 Hosts 中的 Cloudflare CDN IP。\n使用前请先阅读https://github.com/XIU2/CloudflareSpeedTest/issues/42#issuecomment-768273848"
echo -e "该脚本的作用为 CFST 测速后获取最快 IP 并替换 Hosts 中的 Cloudflare CDN IP。\n使用前请先阅读https://github.com/XIU2/CloudflareSpeedTest/issues/42#issuecomment-768273848"
echo -e "第一次使用,请先将 Hosts 中所有 Cloudflare CDN IP 统一改为一个 IP。"
read -e -p "输入该 Cloudflare CDN IP 并回车(后续不再需要该步骤):" NOWIP
if [[ ! -z "${NOWIP}" ]]; then
@@ -31,21 +31,21 @@ _UPDATE() {
echo -e "开始测速..."
NOWIP=$(head -1 nowip_hosts.txt)
# 这里可以自己添加、修改 CloudflareST 的运行参数
./CloudflareST -o "result_hosts.txt"
# 这里可以自己添加、修改 CFST 的运行参数
./cfst -o "result_hosts.txt"
# 如果需要 "找不到满足条件的 IP 就一直循环测速下去",那么可以将下面的两个 exit 0 改为 _UPDATE 即可
[[ ! -e "result_hosts.txt" ]] && echo "CloudflareST 测速结果 IP 数量为 0跳过下面步骤..." && exit 0
[[ ! -e "result_hosts.txt" ]] && echo "CFST 测速结果 IP 数量为 0跳过下面步骤..." && exit 0
# 下面这行代码是 "找不到满足条件的 IP 就一直循环测速下去" 才需要的代码
# 考虑到当指定了下载速度下限,但一个满足全部条件的 IP 都没找到时CloudflareST 就会输出所有 IP 结果
# 考虑到当指定了下载速度下限,但一个满足全部条件的 IP 都没找到时CFST 就会输出所有 IP 结果
# 因此当你指定 -sl 参数时,需要移除下面这段代码开头的 # 井号注释符来做文件行数判断比如下载测速数量10 个,那么下面的值就设在为 11
#[[ $(cat result_hosts.txt|wc -l) > 11 ]] && echo "CloudflareST 测速结果没有找到一个完全满足条件的 IP重新测速..." && _UPDATE
#[[ $(cat result_hosts.txt|wc -l) > 11 ]] && echo "CFST 测速结果没有找到一个完全满足条件的 IP重新测速..." && _UPDATE
BESTIP=$(sed -n "2,1p" result_hosts.txt | awk -F, '{print $1}')
if [[ -z "${BESTIP}" ]]; then
echo "CloudflareST 测速结果 IP 数量为 0跳过下面步骤..."
echo "CFST 测速结果 IP 数量为 0跳过下面步骤..."
exit 0
fi
echo ${BESTIP} > nowip_hosts.txt

View File

@@ -7,9 +7,10 @@ import (
"net"
"net/http"
"sort"
"strconv"
"time"
"CloudflareSpeedTest/utils"
"github.com/XIU2/CloudflareSpeedTest/utils"
"github.com/VividCortex/ewma"
)
@@ -24,11 +25,8 @@ const (
)
var (
// download test url
URL = defaultURL
// download timeout
URL = defaultURL
Timeout = defaultTimeout
// disable download
Disable = defaultDisableDownload
TestCount = defaultTestNum
@@ -55,26 +53,35 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
if Disable {
return utils.DownloadSpeedSet(ipSet)
}
if len(ipSet) <= 0 { // IP数组长度(IP数量) 大于 0 时才会继续下载测速
fmt.Println("\n[信息] 延迟测速结果 IP 数量为 0跳过下载测速。")
if len(ipSet) <= 0 { // IP 数组长度(IP数量) 大于 0 时才会继续下载测速
utils.Yellow.Println("[信息] 延迟测速结果 IP 数量为 0跳过下载测速。")
return
}
testNum := TestCount
if len(ipSet) < TestCount || MinSpeed > 0 { // 如果IP数组长度(IP数量) 小于下载测速数量-dn则次数修正为IP
testNum := TestCount // 等待下载测速的队列数量 先默认等于 下载测速数量(-dn
if len(ipSet) < TestCount || MinSpeed > 0 { // 如果延迟测速并过滤后的 IP 数组长度(IP数量) 小于 下载测速数量(-dn即 -dn 预期数量是不够的),或者指定了 下载测速下限 (-sl) 条件(这就可能要全部下载测速一遍,直到找齐预期数量或测完为止),则 等待下载测速的队列数量 修正为 IP 数量
testNum = len(ipSet)
}
if testNum < TestCount {
if testNum < TestCount { // 如果 等待下载测速的队列数量 小于 下载测速数量(-dn显然 -dn 预期数量是不够的),所以 下载测速数量(-dn修正为 等待下载测速的队列数量
TestCount = testNum
}
fmt.Printf("开始下载测速(下载速度下限:%.2f MB/s,下载测速数量:%d下载测速队列:%d\n", MinSpeed, TestCount, testNum)
bar := utils.NewBar(TestCount)
utils.Cyan.Printf("开始下载测速(下限:%.2f MB/s, 数量:%d, 队列:%d\n", MinSpeed, TestCount, testNum)
// 控制 下载测速进度条 与 延迟测速进度条 长度一致(强迫症)
bar_a := len(strconv.Itoa(len(ipSet)))
bar_b := " "
for i := 0; i < bar_a; i++ {
bar_b += " "
}
bar := utils.NewBar(TestCount, bar_b, "")
for i := 0; i < testNum; i++ {
speed := downloadHandler(ipSet[i].IP)
speed, colo := downloadHandler(ipSet[i].IP)
ipSet[i].DownloadSpeed = speed
if ipSet[i].Colo == "" { // 只有当 Colo 是空的时候,才写入,否则代表之前是 httping 测速并获取过了
ipSet[i].Colo = colo
}
// 在每个 IP 下载测速后,以 [下载速度下限] 条件过滤结果
if speed >= MinSpeed*1024*1024 {
bar.Grow(1)
bar.Grow(1, "")
speedSet = append(speedSet, ipSet[i]) // 高于下载速度下限时,添加到新数组中
if len(speedSet) == TestCount { // 凑够满足条件的 IP 时(下载测速数量 -dn就跳出循环
break
@@ -82,7 +89,10 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
}
}
bar.Done()
if len(speedSet) == 0 { // 没有符合速度限制的数据,返回所有测数据
if MinSpeed == 0.00 { // 如果没有指定下载速度下限,则直接返回所有测数据
speedSet = utils.DownloadSpeedSet(ipSet)
} else if utils.Debug && len(speedSet) == 0 { // 如果指定了下载速度下限,且是调试模式下,且没有找到任何一个满足条件的 IP 时,返回所有测速数据,供用户查看当前的测速结果,以便适当调低预期测速条件
utils.Yellow.Println("[调试] 没有满足 下载速度下限 条件的 IP忽略条件返回所有测速数据方便下次测速时调整条件。")
speedSet = utils.DownloadSpeedSet(ipSet)
}
// 按速度排序
@@ -91,22 +101,52 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
}
func getDialContext(ip *net.IPAddr) func(ctx context.Context, network, address string) (net.Conn, error) {
fakeSourceAddr := ip.String() + ":" + fmt.Sprintf("%d", TCPPort)
if IPv6 { // IPv6 需要加上 []
fakeSourceAddr = "[" + ip.String() + "]:" + fmt.Sprintf("%d", TCPPort)
var fakeSourceAddr string
if isIPv4(ip.String()) {
fakeSourceAddr = fmt.Sprintf("%s:%d", ip.String(), TCPPort)
} else {
fakeSourceAddr = fmt.Sprintf("[%s]:%d", ip.String(), TCPPort)
}
return func(ctx context.Context, network, address string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, network, fakeSourceAddr)
}
}
// 统一的请求报错调试输出
func printDownloadDebugInfo(ip *net.IPAddr, err error, statusCode int, url, lastRedirectURL string, response *http.Response) {
finalURL := url // 默认的最终 URL这样当 response 为空时也能输出
if lastRedirectURL != "" {
finalURL = lastRedirectURL // 如果 lastRedirectURL 不是空,说明重定向过,优先输出最后一次要重定向至的目标
} else if response != nil && response.Request != nil && response.Request.URL != nil {
finalURL = response.Request.URL.String() // 如果 response 不为 nil且 Request 和 URL 都不为 nil则获取最后一次成功的响应地址
}
if url != finalURL { // 如果 URL 和最终地址不一致,说明有重定向,是该重定向后的地址引起的错误
if statusCode > 0 { // 如果状态码大于 0说明是后续 HTTP 状态码引起的错误
utils.Red.Printf("[调试] IP: %s, 下载测速终止HTTP 状态码: %d, 下载测速地址: %s, 出错的重定向后地址: %s\n", ip.String(), statusCode, url, finalURL)
} else {
utils.Red.Printf("[调试] IP: %s, 下载测速失败,错误信息: %v, 下载测速地址: %s, 出错的重定向后地址: %s\n", ip.String(), err, url, finalURL)
}
} else { // 如果 URL 和最终地址一致,说明没有重定向
if statusCode > 0 { // 如果状态码大于 0说明是后续 HTTP 状态码引起的错误
utils.Red.Printf("[调试] IP: %s, 下载测速终止HTTP 状态码: %d, 下载测速地址: %s\n", ip.String(), statusCode, url)
} else {
utils.Red.Printf("[调试] IP: %s, 下载测速失败,错误信息: %v, 下载测速地址: %s\n", ip.String(), err, url)
}
}
}
// return download Speed
func downloadHandler(ip *net.IPAddr) float64 {
func downloadHandler(ip *net.IPAddr) (float64, string) {
var lastRedirectURL string // 用于记录最后一次重定向目标,以便在访问错误时输出
client := &http.Client{
Transport: &http.Transport{DialContext: getDialContext(ip)},
Timeout: Timeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) > 10 { // 限制最多重定向 10 次
lastRedirectURL = req.URL.String() // 记录每次重定向的目标,以便在访问错误时输出
if len(via) > 10 { // 限制最多重定向 10 次
if utils.Debug { // 调试模式下,输出更多信息
utils.Red.Printf("[调试] IP: %s, 下载测速地址重定向次数过多,终止测速,下载测速地址: %s\n", ip.String(), req.URL.String())
}
return http.ErrUseLastResponse
}
if req.Header.Get("Referer") == defaultURL { // 当使用默认下载测速地址时,重定向不携带 Referer
@@ -117,19 +157,32 @@ func downloadHandler(ip *net.IPAddr) float64 {
}
req, err := http.NewRequest("GET", URL, nil)
if err != nil {
return 0.0
if utils.Debug { // 调试模式下,输出更多信息
utils.Red.Printf("[调试] IP: %s, 下载测速请求创建失败,错误信息: %v, 下载测速地址: %s\n", ip.String(), err, URL)
}
return 0.0, ""
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36")
response, err := client.Do(req)
if err != nil {
return 0.0
if utils.Debug { // 调试模式下,输出更多信息
printDownloadDebugInfo(ip, err, 0, URL, lastRedirectURL, response)
}
return 0.0, ""
}
defer response.Body.Close()
if response.StatusCode != 200 {
return 0.0
if utils.Debug { // 调试模式下,输出更多信息
printDownloadDebugInfo(ip, nil, response.StatusCode, URL, lastRedirectURL, response)
}
return 0.0, ""
}
// 通过头部参数获取地区码
colo := getHeaderColo(response.Header)
timeStart := time.Now() // 开始时间(当前)
timeEnd := timeStart.Add(Timeout) // 加上下载测速时间得到的结束时间
@@ -161,12 +214,17 @@ func downloadHandler(ip *net.IPAddr) float64 {
}
bufferRead, err := response.Body.Read(buffer)
if err != nil {
if err != io.EOF { // 文件下载完了,或因网络等问题导致链接中断,则退出循环(终止测速)
if err != io.EOF { // 如果文件下载过程中遇到报错(如 Timeout且并不是因为文件下载完了,则退出循环(终止测速)
break
} else if contentLength == -1 { // 文件下载完成 且 文件大小未知则退出循环终止测速例如https://speed.cloudflare.com/__down?bytes=200000000 这样的,如果在 10 秒内就下载完成了,会导致测速结果明显偏低甚至显示为 0.00(下载速度太快时)
break
}
e.Add(float64(contentRead-lastContentRead) / (float64(nextTime.Sub(currentTime)) / float64(timeSlice)))
// 获取上个时间片
last_time_slice := timeStart.Add(timeSlice * time.Duration(timeCounter-1))
// 下载数据量 / (用当前时间 - 上个时间片/ 时间片)
e.Add(float64(contentRead-lastContentRead) / (float64(currentTime.Sub(last_time_slice)) / float64(timeSlice)))
}
contentRead += int64(bufferRead)
}
return e.Value() / (Timeout.Seconds() / 120)
return e.Value() / (Timeout.Seconds() / 120), colo
}

208
task/httping.go Normal file
View File

@@ -0,0 +1,208 @@
package task
import (
//"crypto/tls"
"io"
"log"
"net"
"net/http"
"regexp"
"strings"
"sync"
"time"
"github.com/XIU2/CloudflareSpeedTest/utils"
)
var (
Httping bool
HttpingStatusCode int
HttpingCFColo string
HttpingCFColomap *sync.Map
RegexpColoIATACode = regexp.MustCompile(`[A-Z]{3}`) // 匹配 IATA 机场地区码(俗称 机场三字码)的正则表达式
RegexpColoCountryCode = regexp.MustCompile(`[A-Z]{2}`) // 匹配国家地区码的正则表达式(如 US、CN、UK 等)
RegexpColoGcore = regexp.MustCompile(`^[a-z]{2}`) // 匹配城市地区码的正则表达式(小写,如 us、cn、uk 等)
)
// pingReceived pingTotalTime
func (p *Ping) httping(ip *net.IPAddr) (int, time.Duration, string) {
hc := http.Client{
Timeout: time.Second * 2,
Transport: &http.Transport{
DialContext: getDialContext(ip),
//TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过证书验证
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // 阻止重定向
},
}
// 先访问一次获得 HTTP 状态码 及 地区码
var colo string
{
request, err := http.NewRequest(http.MethodHead, URL, nil)
if err != nil {
if utils.Debug { // 调试模式下,输出更多信息
utils.Red.Printf("[调试] IP: %s, 延迟测速请求创建失败,错误信息: %v, 测速地址: %s\n", ip.String(), err, URL)
}
return 0, 0, ""
}
request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36")
response, err := hc.Do(request)
if err != nil {
if utils.Debug { // 调试模式下,输出更多信息
utils.Red.Printf("[调试] IP: %s, 延迟测速失败,错误信息: %v, 测速地址: %s\n", ip.String(), err, URL)
}
return 0, 0, ""
}
defer response.Body.Close()
//fmt.Println("IP:", ip, "StatusCode:", response.StatusCode, response.Request.URL)
// 如果未指定的 HTTP 状态码,或指定的状态码不合规,则默认只认为 200、301、302 才算 HTTPing 通过
if HttpingStatusCode == 0 || HttpingStatusCode < 100 && HttpingStatusCode > 599 {
if response.StatusCode != 200 && response.StatusCode != 301 && response.StatusCode != 302 {
if utils.Debug { // 调试模式下,输出更多信息
utils.Red.Printf("[调试] IP: %s, 延迟测速终止HTTP 状态码: %d, 测速地址: %s\n", ip.String(), response.StatusCode, URL)
}
return 0, 0, ""
}
} else {
if response.StatusCode != HttpingStatusCode {
if utils.Debug { // 调试模式下,输出更多信息
utils.Red.Printf("[调试] IP: %s, 延迟测速终止HTTP 状态码: %d, 指定的 HTTP 状态码 %d, 测速地址: %s\n", ip.String(), response.StatusCode, HttpingStatusCode, URL)
}
return 0, 0, ""
}
}
io.Copy(io.Discard, response.Body)
// 通过头部参数获取地区码
colo = getHeaderColo(response.Header)
// 只有指定了地区才匹配机场地区码
if HttpingCFColo != "" {
// 判断是否匹配指定的地区码
colo = p.filterColo(colo)
if colo == "" { // 没有匹配到地区码或不符合指定地区则直接结束该 IP 测试
if utils.Debug { // 调试模式下,输出更多信息
utils.Red.Printf("[调试] IP: %s, 地区码不匹配: %s\n", ip.String(), colo)
}
return 0, 0, ""
}
}
}
// 循环测速计算延迟
success := 0
var delay time.Duration
for i := 0; i < PingTimes; i++ {
request, err := http.NewRequest(http.MethodHead, URL, nil)
if err != nil {
log.Fatal("意外的错误,情报告:", err)
return 0, 0, ""
}
request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36")
if i == PingTimes-1 {
request.Header.Set("Connection", "close")
}
startTime := time.Now()
response, err := hc.Do(request)
if err != nil {
continue
}
success++
io.Copy(io.Discard, response.Body)
_ = response.Body.Close()
duration := time.Since(startTime)
delay += duration
}
return success, delay, colo
}
func MapColoMap() *sync.Map {
if HttpingCFColo == "" {
return nil
}
// 将 -cfcolo 参数指定的地区地区码转为大写并格式化
colos := strings.Split(strings.ToUpper(HttpingCFColo), ",")
colomap := &sync.Map{}
for _, colo := range colos {
colomap.Store(colo, colo)
}
return colomap
}
// 从响应头中获取 地区码 值
func getHeaderColo(header http.Header) (colo string) {
if header.Get("server") != "" {
// 如果是 Cloudflare CDN
// server: cloudflare
// cf-ray: 7bd32409eda7b020-SJC
if header.Get("server") == "cloudflare" {
if colo = header.Get("cf-ray"); colo != "" {
return RegexpColoIATACode.FindString(colo)
}
}
// 如果是 CDN77 CDN测试地址 https://www.cdn77.com
// server: CDN77-Turbo
// x-77-pop: losangelesUSCA // 美国的会显示为 USCA 不知道什么情况,暂时没做兼容,只提取 US
// x-77-pop: frankfurtDE
// x-77-pop: amsterdamNL
// x-77-pop: singaporeSG
if header.Get("server") == "CDN77-Turbo" {
if colo = header.Get("x-77-pop"); colo != "" {
return RegexpColoCountryCode.FindString(colo)
}
}
// 如果是 Bunny CDN测试地址 https://bunny.net
// server: BunnyCDN-TW1-1121
if colo = header.Get("server"); strings.Contains(colo, "BunnyCDN-") {
return RegexpColoCountryCode.FindString(strings.TrimPrefix(colo, "BunnyCDN-")) // 去掉 BunnyCDN- 前缀再去匹配
}
}
// 如果是 AWS CloudFront CDN测试地址 https://d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips
// x-amz-cf-pop: SIN52-P1
if colo = header.Get("x-amz-cf-pop"); colo != "" {
return RegexpColoIATACode.FindString(colo)
}
// 如果是 Fastly CDN测试地址 https://fastly.jsdelivr.net/gh/XIU2/CloudflareSpeedTest@master/go.mod
// x-served-by: cache-qpg1275-QPG
// x-served-by: cache-fra-etou8220141-FRA, cache-hhr-khhr2060043-HHR最后一个为实际位置
if colo = header.Get("x-served-by"); colo != "" {
if matches := RegexpColoIATACode.FindAllString(colo, -1); len(matches) > 0 {
return matches[len(matches)-1] // 因为 Fastly 的 x-served-by 可能包含多个地区码,所以只取最后一个
}
}
// Gcore CDN 的头部信息(注意均为城市代码而非国家代码),测试地址 https://assets.gcore.pro/assets/icons/shield-lock.svg
// x-id-fe: fr5-hw-edge-gc17
// x-shard: fr5-shard0-default
// x-id: fr5-hw-edge-gc28
if colo = header.Get("x-id-fe"); colo != "" {
if colo = RegexpColoGcore.FindString(colo); colo != "" {
return strings.ToUpper(colo) // 将小写的地区码转换为大写
}
}
// 如果没有获取到头部信息,说明不是支持的 CDN则直接返回空字符串
return ""
}
// 处理地区码
func (p *Ping) filterColo(colo string) string {
if colo == "" {
return ""
}
// 如果没有指定 -cfcolo 参数,则直接返回
if HttpingCFColomap == nil {
return colo
}
// 匹配 机场地区码 是否为指定的地区
_, ok := HttpingCFColomap.Load(colo)
if ok {
return colo
}
return ""
}

View File

@@ -14,19 +14,25 @@ import (
const defaultInputFile = "ip.txt"
var (
// IPv6 IP version is 6
IPv6 = false
// TestAll test all ip
TestAll = false
// IPFile is the filename of IP Rangs
IPFile = defaultInputFile
IPText string
)
func InitRandSeed() {
rand.Seed(time.Now().UnixNano())
}
func isIPv4(ip string) bool {
return strings.Contains(ip, ".")
}
func randIPEndWith(num byte) byte {
if num == 0 { // 对于 /32 这种单独的 IP
return byte(0)
}
return byte(rand.Intn(int(num)))
}
@@ -43,11 +49,13 @@ func newIPRanges() *IPRanges {
}
}
// 如果是单独 IP 则加上子网掩码,反之则获取子网掩码(r.mask)
func (r *IPRanges) fixIP(ip string) string {
// 如果不含有 '/' 则代表不是 IP 段,而是一个单独的 IP因此需要加上 /32 /128 子网掩码
if i := strings.IndexByte(ip, '/'); i < 0 {
r.mask = "/32"
if IPv6 {
if isIPv4(ip) {
r.mask = "/32"
} else {
r.mask = "/128"
}
ip += r.mask
@@ -57,6 +65,7 @@ func (r *IPRanges) fixIP(ip string) string {
return ip
}
// 解析 IP 段,获得 IP、IP 范围、子网掩码
func (r *IPRanges) parseCIDR(ip string) {
var err error
if r.firstIP, r.ipNet, err = net.ParseCIDR(r.fixIP(ip)); err != nil {
@@ -86,74 +95,96 @@ func (r *IPRanges) getIPRange() (minIP, hosts byte) {
hosts = 255
return
}
if total == 0 {
hosts = 1
return
}
hosts = byte(total)
return
}
func (r *IPRanges) chooseIPv4() {
minIP, hosts := r.getIPRange()
for r.ipNet.Contains(r.firstIP) {
if TestAll { // 如果是测速全部 IP
for i := 0; i <= int(hosts); i++ { // 遍历 IP 最后一段最小值到最大值
r.appendIPv4(byte(i) + minIP)
if r.mask == "/32" { // 单个 IP 则无需随机,直接加入自身即可
r.appendIP(r.firstIP)
} else {
minIP, hosts := r.getIPRange() // 返回第四段 IP 的最小值及可用数目
for r.ipNet.Contains(r.firstIP) { // 只要该 IP 没有超出 IP 网段范围,就继续循环随机
if TestAll { // 如果是测速全部 IP
for i := 0; i <= int(hosts); i++ { // 遍历 IP 最后一段最小值到最大值
r.appendIPv4(byte(i) + minIP)
}
} else { // 随机 IP 的最后一段 0.0.0.X
r.appendIPv4(minIP + randIPEndWith(hosts))
}
} else { // 随机 IP 的最后一段 0.0.0.X
r.appendIPv4(minIP + randIPEndWith(hosts))
}
r.firstIP[14]++ // 0.0.(X+1).X
if r.firstIP[14] == 0 {
r.firstIP[13]++ // 0.(X+1).X.X
if r.firstIP[13] == 0 {
r.firstIP[12]++ // (X+1).X.X.X
r.firstIP[14]++ // 0.0.(X+1).X
if r.firstIP[14] == 0 {
r.firstIP[13]++ // 0.(X+1).X.X
if r.firstIP[13] == 0 {
r.firstIP[12]++ // (X+1).X.X.X
}
}
}
}
}
func (r *IPRanges) chooseIPv6() {
var tempIP uint8
for r.ipNet.Contains(r.firstIP) {
//fmt.Println(firstIP)
//fmt.Println(firstIP[0], firstIP[1], firstIP[2], firstIP[3], firstIP[4], firstIP[5], firstIP[6], firstIP[7], firstIP[8], firstIP[9], firstIP[10], firstIP[11], firstIP[12], firstIP[13], firstIP[14], firstIP[15])
if r.mask != "/128" {
if r.mask == "/128" { // 单个 IP 则无需随机,直接加入自身即可
r.appendIP(r.firstIP)
} else {
var tempIP uint8 // 临时变量,用于记录前一位的值
for r.ipNet.Contains(r.firstIP) { // 只要该 IP 没有超出 IP 网段范围,就继续循环随机
r.firstIP[15] = randIPEndWith(255) // 随机 IP 的最后一段
r.firstIP[14] = randIPEndWith(255) // 随机 IP 的最后一段
}
targetIP := make([]byte, len(r.firstIP))
copy(targetIP, r.firstIP)
r.appendIP(targetIP)
for i := 13; i >= 0; i-- {
tempIP = r.firstIP[i]
r.firstIP[i] += randIPEndWith(255)
if r.firstIP[i] >= tempIP {
break
targetIP := make([]byte, len(r.firstIP))
copy(targetIP, r.firstIP)
r.appendIP(targetIP) // 加入 IP 地址池
for i := 13; i >= 0; i-- { // 从倒数第三位开始往前随机
tempIP = r.firstIP[i] // 保存前一位的值
r.firstIP[i] += randIPEndWith(255) // 随机 0~255加到当前位上
if r.firstIP[i] >= tempIP { // 如果当前位的值大于等于前一位的值,说明随机成功了,可以退出该循环
break
}
}
}
}
}
func loadIPRanges() []*net.IPAddr {
if IPFile == "" {
IPFile = defaultInputFile
}
file, err := os.Open(IPFile)
if err != nil {
log.Fatal(err)
}
defer file.Close()
ranges := newIPRanges()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
ranges.parseCIDR(scanner.Text())
if IPv6 {
ranges.chooseIPv6()
continue
if IPText != "" { // 从参数中获取 IP 段数据
IPs := strings.Split(IPText, ",") // 以逗号分隔为数组并循环遍历
for _, IP := range IPs {
IP = strings.TrimSpace(IP) // 去除首尾的空白字符(空格、制表符、换行符等)
if IP == "" { // 跳过空的(即开头、结尾或连续多个 ,, 的情况)
continue
}
ranges.parseCIDR(IP) // 解析 IP 段,获得 IP、IP 范围、子网掩码
if isIPv4(IP) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部)
ranges.chooseIPv4()
} else {
ranges.chooseIPv6()
}
}
} else { // 从文件中获取 IP 段数据
if IPFile == "" {
IPFile = defaultInputFile
}
file, err := os.Open(IPFile)
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() { // 循环遍历文件每一行
line := strings.TrimSpace(scanner.Text()) // 去除首尾的空白字符(空格、制表符、换行符等)
if line == "" { // 跳过空行
continue
}
ranges.parseCIDR(line) // 解析 IP 段,获得 IP、IP 范围、子网掩码
if isIPv4(line) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部)
ranges.chooseIPv4()
} else {
ranges.chooseIPv6()
}
}
ranges.chooseIPv4()
}
return ranges.ips
}

View File

@@ -4,10 +4,11 @@ import (
"fmt"
"net"
"sort"
"strconv"
"sync"
"time"
"CloudflareSpeedTest/utils"
"github.com/XIU2/CloudflareSpeedTest/utils"
)
const (
@@ -54,7 +55,7 @@ func NewPing() *Ping {
ips: ips,
csv: make(utils.PingDelaySet, 0),
control: make(chan bool, Routines),
bar: utils.NewBar(len(ips)),
bar: utils.NewBar(len(ips), "可用:", ""),
}
}
@@ -62,11 +63,11 @@ func (p *Ping) Run() utils.PingDelaySet {
if len(p.ips) == 0 {
return p.csv
}
ipVersion := "IPv4"
if IPv6 { // IPv6 模式判断
ipVersion = "IPv6"
if Httping {
utils.Cyan.Printf("开始延迟测速模式HTTP, 端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
} else {
utils.Cyan.Printf("开始延迟测速模式TCP, 端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
}
fmt.Printf("开始延迟测速模式TCP %s端口%d平均延迟上限%v ms平均延迟下限%v ms)\n", ipVersion, TCPPort, utils.InputMaxDelay.Milliseconds(), utils.InputMinDelay.Milliseconds())
for _, ip := range p.ips {
p.wg.Add(1)
p.control <- false
@@ -84,12 +85,13 @@ func (p *Ping) start(ip *net.IPAddr) {
<-p.control
}
//bool connectionSucceed float32 time
// bool connectionSucceed float32 time
func (p *Ping) tcping(ip *net.IPAddr) (bool, time.Duration) {
startTime := time.Now()
fullAddress := fmt.Sprintf("%s:%d", ip.String(), TCPPort)
//fmt.Println(ip.String())
if IPv6 { // IPv6 需要加上 []
var fullAddress string
if isIPv4(ip.String()) {
fullAddress = fmt.Sprintf("%s:%d", ip.String(), TCPPort)
} else {
fullAddress = fmt.Sprintf("[%s]:%d", ip.String(), TCPPort)
}
conn, err := net.DialTimeout("tcp", fullAddress, tcpConnectTimeout)
@@ -101,8 +103,13 @@ func (p *Ping) tcping(ip *net.IPAddr) (bool, time.Duration) {
return true, duration
}
//pingReceived pingTotalTime
func (p *Ping) checkConnection(ip *net.IPAddr) (recv int, totalDelay time.Duration) {
// pingReceived pingTotalTime
func (p *Ping) checkConnection(ip *net.IPAddr) (recv int, totalDelay time.Duration, colo string) {
if Httping {
recv, totalDelay, colo = p.httping(ip)
return
}
colo = "" // TCPing 不获取 colo
for i := 0; i < PingTimes; i++ {
if ok, delay := p.tcping(ip); ok {
recv++
@@ -122,8 +129,12 @@ func (p *Ping) appendIPData(data *utils.PingData) {
// handle tcping
func (p *Ping) tcpingHandler(ip *net.IPAddr) {
recv, totalDlay := p.checkConnection(ip)
p.bar.Grow(1)
recv, totalDlay, colo := p.checkConnection(ip)
nowAble := len(p.csv)
if recv != 0 {
nowAble++
}
p.bar.Grow(1, strconv.Itoa(nowAble))
if recv == 0 {
return
}
@@ -132,6 +143,7 @@ func (p *Ping) tcpingHandler(ip *net.IPAddr) {
Sended: PingTimes,
Received: recv,
Delay: totalDlay / time.Duration(recv),
Colo: colo,
}
p.appendIPData(data)
}

16
utils/color.go Normal file
View File

@@ -0,0 +1,16 @@
package utils
import (
"github.com/fatih/color"
)
// 由专业的库来处理多平台的颜色输出效果
var (
Red = color.New(color.FgRed) // 红色 31
Green = color.New(color.FgGreen) // 绿色 32
Yellow = color.New(color.FgYellow) // 黄色 33
Blue = color.New(color.FgBlue, color.Bold) // 蓝色 34
Magenta = color.New(color.FgMagenta) // 紫红色 35
Cyan = color.New(color.FgHiCyan, color.Bold) // 青色 36
White = color.New(color.FgWhite) // 白色 37
)

View File

@@ -11,16 +11,19 @@ import (
)
const (
defaultOutput = "result.csv"
maxDelay = 9999 * time.Millisecond
minDelay = 0 * time.Millisecond
defaultOutput = "result.csv"
maxDelay = 9999 * time.Millisecond
minDelay = 0 * time.Millisecond
maxLossRate float32 = 1.0
)
var (
InputMaxDelay = maxDelay
InputMinDelay = minDelay
Output = defaultOutput
PrintNum = 10
InputMaxDelay = maxDelay
InputMinDelay = minDelay
InputMaxLossRate = maxLossRate
Output = defaultOutput
PrintNum = 10
Debug = false // 是否开启调试模式
)
// 是否打印测试结果
@@ -38,30 +41,38 @@ type PingData struct {
Sended int
Received int
Delay time.Duration
Colo string
}
type CloudflareIPData struct {
*PingData
recvRate float32
lossRate float32
DownloadSpeed float64
}
func (cf *CloudflareIPData) getRecvRate() float32 {
if cf.recvRate == 0 {
// 计算丢包率
func (cf *CloudflareIPData) getLossRate() float32 {
if cf.lossRate == 0 {
pingLost := cf.Sended - cf.Received
cf.recvRate = float32(pingLost) / float32(cf.Sended)
cf.lossRate = float32(pingLost) / float32(cf.Sended)
}
return cf.recvRate
return cf.lossRate
}
func (cf *CloudflareIPData) toString() []string {
result := make([]string, 6)
result := make([]string, 7)
result[0] = cf.IP.String()
result[1] = strconv.Itoa(cf.Sended)
result[2] = strconv.Itoa(cf.Received)
result[3] = strconv.FormatFloat(float64(cf.getRecvRate()), 'f', 2, 32)
result[3] = strconv.FormatFloat(float64(cf.getLossRate()), 'f', 2, 32)
result[4] = strconv.FormatFloat(cf.Delay.Seconds()*1000, 'f', 2, 32)
result[5] = strconv.FormatFloat(cf.DownloadSpeed/1024/1024, 'f', 2, 32)
// 如果 Colo 为空,则使用 "N/A" 表示
if cf.Colo == "" {
result[6] = "N/A"
} else {
result[6] = cf.Colo
}
return result
}
@@ -76,7 +87,7 @@ func ExportCsv(data []CloudflareIPData) {
}
defer fp.Close()
w := csv.NewWriter(fp) //创建一个新的写入文件流
_ = w.Write([]string{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)"})
_ = w.Write([]string{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度(MB/s)", "地区码"})
_ = w.WriteAll(convertToString(data))
w.Flush()
}
@@ -89,17 +100,22 @@ func convertToString(data []CloudflareIPData) [][]string {
return result
}
// 延迟丢包排序
type PingDelaySet []CloudflareIPData
// 延迟条件过滤
func (s PingDelaySet) FilterDelay() (data PingDelaySet) {
if InputMaxDelay > maxDelay || InputMinDelay < minDelay {
if InputMaxDelay > maxDelay || InputMinDelay < minDelay { // 当输入的延迟条件不在默认范围内时,不进行过滤
return s
}
if InputMaxDelay == maxDelay && InputMinDelay == minDelay { // 当输入的延迟条件为默认值时,不进行过滤
return s
}
for _, v := range s {
if v.Delay > InputMaxDelay { // 平均延迟上限
if v.Delay > InputMaxDelay { // 平均延迟上限,延迟大于条件最大值时,后面的数据都不满足条件,直接跳出循环
break
}
if v.Delay < InputMinDelay { // 平均延迟下限
if v.Delay < InputMinDelay { // 平均延迟下限,延迟小于条件最小值时,不满足条件,跳过
continue
}
data = append(data, v) // 延迟满足条件时,添加到新数组中
@@ -107,18 +123,30 @@ func (s PingDelaySet) FilterDelay() (data PingDelaySet) {
return
}
// 丢包条件过滤
func (s PingDelaySet) FilterLossRate() (data PingDelaySet) {
if InputMaxLossRate >= maxLossRate { // 当输入的丢包条件为默认值时,不进行过滤
return s
}
for _, v := range s {
if v.getLossRate() > InputMaxLossRate { // 丢包几率上限
break
}
data = append(data, v) // 丢包率满足条件时,添加到新数组中
}
return
}
func (s PingDelaySet) Len() int {
return len(s)
}
func (s PingDelaySet) Less(i, j int) bool {
iRate, jRate := s[i].getRecvRate(), s[j].getRecvRate()
iRate, jRate := s[i].getLossRate(), s[j].getLossRate()
if iRate != jRate {
return iRate < jRate
}
return s[i].Delay < s[j].Delay
}
func (s PingDelaySet) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
@@ -129,16 +157,14 @@ type DownloadSpeedSet []CloudflareIPData
func (s DownloadSpeedSet) Len() int {
return len(s)
}
func (s DownloadSpeedSet) Less(i, j int) bool {
return s[i].DownloadSpeed > s[j].DownloadSpeed
}
func (s DownloadSpeedSet) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s DownloadSpeedSet) Print(ipv6 bool) {
func (s DownloadSpeedSet) Print() {
if NoPrintResult() {
return
}
@@ -150,15 +176,18 @@ func (s DownloadSpeedSet) Print(ipv6 bool) {
if len(dateString) < PrintNum { // 如果IP数组长度(IP数量) 小于 打印次数则次数改为IP数量
PrintNum = len(dateString)
}
headFormat := "%-16s%-5s%-5s%-5s%-6s%-11s\n"
dataFormat := "%-18s%-8s%-8s%-8s%-10s%-15s\n"
if ipv6 { // IPv6 太长了,所以需要调整一下间隔
headFormat = "%-40s%-5s%-5s%-5s%-6s%-11s\n"
dataFormat = "%-42s%-8s%-8s%-8s%-10s%-15s\n"
headFormat := "%-16s%-5s%-5s%-5s%-6s%-12s%-5s\n"
dataFormat := "%-18s%-8s%-8s%-8s%-10s%-16s%-8s\n"
for i := 0; i < PrintNum; i++ { // 如果要输出的 IP 中包含 IPv6那么就需要调整一下间隔
if len(dateString[i][0]) > 15 {
headFormat = "%-40s%-5s%-5s%-5s%-6s%-12s%-5s\n"
dataFormat = "%-42s%-8s%-8s%-8s%-10s%-16s%-8s\n"
break
}
}
fmt.Printf(headFormat, "IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)")
Cyan.Printf(headFormat, "IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度(MB/s)", "地区码")
for i := 0; i < PrintNum; i++ {
fmt.Printf(dataFormat, dateString[i][0], dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5])
fmt.Printf(dataFormat, dateString[i][0], dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5], dateString[i][6])
}
if !noOutput() {
fmt.Printf("\n完整测速结果已写入 %v 文件,可使用记事本/表格软件查看。\n", Output)

View File

@@ -1,19 +1,25 @@
package utils
import "github.com/cheggaaa/pb/v3"
import (
"fmt"
"github.com/cheggaaa/pb/v3"
)
type Bar struct {
pb *pb.ProgressBar
}
func NewBar(count int) *Bar {
return &Bar{pb.Simple.Start(count)}
func NewBar(count int, MyStrStart, MyStrEnd string) *Bar {
tmpl := fmt.Sprintf(`{{counters . }} {{ bar . "[" "-" (cycle . "↖" "↗" "↘" "↙" ) "_" "]"}} %s {{string . "MyStr" | green}} %s `, MyStrStart, MyStrEnd)
bar := pb.ProgressBarTemplate(tmpl).Start(count)
return &Bar{pb: bar}
}
func (b *Bar) Grow(num int) {
b.pb.Add(num)
func (b *Bar) Grow(num int, MyStrVal string) {
b.pb.Set("MyStr", MyStrVal).Add(num)
}
func (b *Bar) Done() {
b.pb.Finish()
}
}