361 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
xiu2
195f95ed80 README.md 2022-02-10 00:27:30 +08:00
xiu2
8c720eb137 README.md 2022-02-09 21:06:23 +08:00
xiu2
d412f8645d README.md 2022-02-09 21:00:47 +08:00
xiu2
04ed493e9d 调整 默认的 UA 为浏览器 UA; 调整 默认下载测速地址; 调整 默认下载测速地址重定向时不再携带 Referer 信息 2022-02-09 20:58:59 +08:00
xiu2
794c387b50 README.md 2022-02-06 12:50:21 +08:00
xiu2
20708746c3 README.md 2022-02-06 12:46:59 +08:00
xiu2
cace4572f1 README.md 2022-02-06 12:42:45 +08:00
xiu2
80c3c7bdad README.md 2022-02-06 11:25:44 +08:00
xiu2
a6f72ade0a README.md 2022-02-06 11:25:08 +08:00
xiu2
0f721fc44b README.md 2022-02-06 11:18:23 +08:00
xiu2
8d8a8df123 README.md 2022-02-05 21:19:45 +08:00
xiu2
1173bcda06 README.md 2022-02-05 13:13:10 +08:00
xiu2
ce6b494996 README.md 2022-02-05 13:07:04 +08:00
xiu2
a24d393a11 README.md 2022-02-04 17:07:18 +08:00
xiu2
01d796fb90 README.md 2022-02-04 17:04:52 +08:00
xiu2
78513f977a README.md 2022-02-04 17:01:42 +08:00
xiu2
9878cb56d4 README.md 2022-02-03 17:59:15 +08:00
xiu2
aeb1474e5a README.md 2022-02-03 16:42:22 +08:00
xiu2
c88577a183 README.md 2022-02-01 17:02:18 +08:00
xiu2
60c9902177 移除 已不能用于自选的 1.1.1.0/24 1.0.0.0/24 IP 段 2022-01-21 12:25:18 +08:00
xiu2
b2422ca12d 补充 各个脚本 2022-01-04 10:34:12 +08:00
xiu2
91e6ae5add README.md 2021-12-24 16:59:28 +08:00
xiu2
a08c2c35c4 README.md 2021-12-22 10:20:34 +08:00
xiu2
6629fdbdea README.md 2021-12-22 10:18:08 +08:00
xiu2
81de6c28ed README.md 2021-12-14 22:39:04 +08:00
xiu2
70ce3f8cbb README.md 2021-12-06 17:55:20 +08:00
xiu2
f8230942a3 README.md 2021-12-06 17:10:16 +08:00
xiu2
638e5ffaba README.md 2021-12-02 18:13:03 +08:00
xiu2
533b140c9c README.md 2021-11-29 23:59:51 +08:00
xiu2
3895479b35 README.md 2021-11-28 09:20:05 +08:00
xiu2
8264138dd8 README.md 2021-11-27 23:24:42 +08:00
xiu2
6cb94db59e 新增 下载测速也会使用 -tp 参数指定的端口; 调整 下载测速进度条数字 2021-11-27 23:21:02 +08:00
xiu2
85923cf335 README.md 2021-11-27 21:57:14 +08:00
xiu2
60631a958f Update 2021-11-20 10:32:11 +08:00
xiu2
ea46fc5e04 修复 IP 解析不够随机的问题 2021-11-17 15:40:53 +08:00
xiu2
9471094d27 README.md 2021-11-15 17:29:10 +08:00
xiu2
be4634ee9d Update 2021-11-15 17:09:23 +08:00
xiu2
e5bbd07aea 调整 文字格式细节 2021-11-14 15:16:32 +08:00
XIU2
445ca36877 Merge pull request #132 from seek4self/dev
重构 代码 (感谢 @seek4self )
优化 TCPing 代码
调整 `-dt / -dn / -p` 参数的默认值为 10 (原为 20)
2021-11-14 14:42:44 +08:00
mz
f6288f4e52 tcphandler just test one ip 2021-11-14 14:10:44 +08:00
mz
cfe30c2f6a fix ipv4 last segment = 0 bug 2021-11-14 14:04:31 +08:00
mz
115bd2af03 fix -o=""/" " don't write to file 2021-11-14 12:59:46 +08:00
mz
272eb40d5b fix -p=0 don't print result bug 2021-11-14 12:47:57 +08:00
mz
2c46cfcd0f the speed progress bar shows test count 2021-11-13 22:57:18 +08:00
mz
8eea8cdd0e fix filter delay time bug 2021-11-13 22:20:16 +08:00
mz
f82425bbb6 fix filter delay time invalid bug 2021-11-13 20:27:36 +08:00
mz
2fa023c7f3 fix full ip lost 255 bug 2021-11-13 18:27:11 +08:00
mz
ed0a8bbfea fix allip deadloop bug 2021-11-13 18:19:17 +08:00
mz
7301d32cbe update print format 2021-11-13 16:49:47 +08:00
mz
ab6390a4a4 change the input time parameter to a number 2021-11-13 16:49:16 +08:00
mazhuang
fdbf9ca131 update download bar num 2021-11-12 17:58:37 +08:00
mazhuang
49c14f3e0e replace ipv6 repeat code 2021-11-12 17:58:13 +08:00
mazhuang
80eadc8df2 fix zero result print 2021-11-12 11:58:21 +08:00
mz
fbaa312622 rebuild ipranges 2021-11-12 00:14:10 +08:00
mz
48012f513a rebuild ip and rm old file 2021-11-10 23:58:40 +08:00
mazhuang
28d5d89e85 rebuild load ip 2021-11-10 18:47:21 +08:00
mazhuang
f1a9b5c966 rebuild download 2021-11-10 17:12:12 +08:00
mazhuang
4d64abb94d tcping add progressbar 2021-11-10 12:25:10 +08:00
mz
71671ebe66 rebuild tcping 2021-11-09 23:39:42 +08:00
mazhuang
83c63e975e rebuild tcping 2021-11-09 18:44:17 +08:00
mazhuang
6f74e60444 rebuild main 2021-11-09 17:52:08 +08:00
xiu2
921bb5ed62 移除 一个 IP 段(被限制无法用于自选 IP) 2021-11-05 19:39:33 +08:00
xiu2
337de75d11 README.md 2021-10-26 09:32:44 +08:00
xiu2
169af2afe9 README.md 2021-10-24 16:12:15 +08:00
xiu2
ccfca867d3 更新 IPv6.txt 数据 2021-10-07 14:32:33 +08:00
xiu2
897977a11d 恢复 ip.txt 为完整 IP 段 2021-10-02 12:06:25 +08:00
xiu2
7212110dde 优化 IPv6 测速功能(支持 /128 子网掩码) 2021-09-23 19:16:08 +08:00
xiu2
d651fdeb54 新增 ipall.txt(完整版的 IPv4 数据文件,没有过滤不可用的 IP) 2021-08-29 09:07:42 +08:00
xiu2
d453124943 README.md 2021-08-27 09:41:30 +08:00
xiu2
e92badf8be 调低 [测速线程数量] 默认值为 200 2021-08-26 20:43:22 +08:00
xiu2
01dc5df491 README.md 2021-08-20 11:37:08 +08:00
xiu2
b428775cc5 README.md 2021-08-16 13:42:43 +08:00
xiu2
5202f73108 README.md 2021-08-11 11:52:39 +08:00
xiu2
1bb45af3f1 README.md 2021-08-11 11:03:03 +08:00
xiu2
3736d81dda README.md 2021-08-11 10:48:12 +08:00
xiu2
a42059737b 新增 [平均延迟下限] 参数(用于过滤被假蔷的 IP) 2021-08-11 10:46:36 +08:00
xiu2
46da45b25f 新增 [平均延迟下限] 参数(用于过滤被假蔷的 IP) 2021-08-11 10:37:59 +08:00
xiu2
acd47ee96d README.md 2021-08-10 01:14:08 +08:00
xiu2
698108f453 优化 文本显示 2021-07-24 13:45:11 +08:00
xiu2
2963b61910 README.md 2021-07-22 19:20:39 +08:00
xiu2
9bab2944b1 README.md 2021-07-17 07:21:54 +08:00
xiu2
fa5e4f34f0 README.md 2021-07-04 08:04:59 +08:00
xiu2
db9d092010 README.md 2021-07-01 14:34:53 +08:00
xiu2
180097b044 删除 多余的空格 2021-04-09 12:53:45 +08:00
xiu2
c115249811 更新 ip.txt(官方改动) 2021-04-09 07:23:04 +08:00
xiu2
3b7851f77c README.md 2021-04-03 10:19:26 +08:00
xiu2
80c201f160 README.md 2021-03-21 21:00:03 +08:00
xiu2
85546abb23 新增 测速单个 IP 时可以省略 /32 子网掩码(允许和 IP 段混杂使用) 2021-03-19 21:43:29 +08:00
xiu2
1d46334a6b 移除 ip.txt 中不可用的 IP 段(约占三分之一,如回源 IP) 2021-03-16 20:57:26 +08:00
xiu2
afd8736268 README.md 2021-03-13 09:35:26 +08:00
xiu2
17ff85954f README.md 2021-03-13 09:34:22 +08:00
xiu2
7ece9d6cda README.md 2021-02-18 23:51:05 +08:00
xiu2
d67b9bc86d README.md 2021-02-18 23:48:07 +08:00
xiu2
73ae874645 README.md 2021-02-18 23:47:11 +08:00
xiu2
0441c27cec README.md 2021-02-18 23:39:37 +08:00
xiu2
7e5804b7ba 新增 支持仅指定 [平均延迟上限] 条件 2021-02-18 23:31:01 +08:00
xiu2
cee772547b README.md 2021-02-17 11:38:01 +08:00
xiu2
1a939f752b README.md 2021-02-10 19:48:01 +08:00
xiu2
5b45f400a2 补充 README.md 2021-02-06 20:24:35 +08:00
xiu2
3b43b21b83 补充 README.md 2021-02-05 22:08:34 +08:00
xiu2
2f284efddd 补充 README.md 2021-02-04 22:59:43 +08:00
xiu2
f960ce4a4b 补充 README.md 2021-02-04 18:45:56 +08:00
xiu2
8ae1d495af 补充 README.md 2021-02-04 17:32:14 +08:00
xiu2
3b957cb1a4 优化 [延迟测速上限/下载速度下限] 支持小数 #51 2021-02-04 16:16:29 +08:00
XIU2
faee85edb8 Merge pull request #53 from CrazyBoyFeng/master
优化 [延迟测速上限/下载速度下限] 支持小数  #51
2021-02-04 15:37:41 +08:00
CrazyBoyFeng
b901003dd1 优化 XIU2/CloudflareSpeedTest#51 2021-02-04 14:46:24 +08:00
xiu2
873200de80 补充 README.md 2021-02-03 13:28:28 +08:00
xiu2
ac29c9666e 补充 README.md 2021-02-02 18:14:56 +08:00
xiu2
a540789180 补充 README.md 2021-02-02 18:12:19 +08:00
xiu2
012865f02d 补充 README.md 2021-01-28 12:19:49 +08:00
xiu2
74c1744ca6 补充 README.md 2021-01-27 13:48:50 +08:00
xiu2
e66d44882e 补充 README.md 2021-01-27 13:12:43 +08:00
xiu2
0d3af2d5f8 补充 README.md 2021-01-27 13:10:05 +08:00
xiu2
70d11e3bc1 补充 README.md 2021-01-27 09:54:32 +08:00
xiu2
ec650d3084 补充 README.md 2021-01-26 19:24:31 +08:00
xiu2
4038a5b0aa 优化 README.md 2021-01-16 16:31:50 +08:00
xiu2
1ff4c24c59 优化 README.md 2021-01-16 16:14:24 +08:00
xiu2
ffbb161f79 优化 README.md 2021-01-11 10:25:32 +08:00
xiu2
3efabb5661 优化 README.md 2021-01-08 08:28:49 +08:00
xiu2
e2f23aeb48 优化 README.md 2021-01-08 08:11:37 +08:00
xiu2
97bbb8b5e9 优化 README.md 2021-01-07 07:37:26 +08:00
xiu2
5d75cb861e 优化 README.md 2021-01-07 07:36:30 +08:00
xiu2
eed4f4fa0b 优化 README.md 2021-01-06 09:35:03 +08:00
xiu2
e8537fb0ae 优化 指定测速条件时的进度条显示内容 2021-01-05 19:24:23 +08:00
xiu2
f1147c5cbf 添加 单独的 1.1.1.1 和 1.0.0.1(字位掩码 /32) 2021-01-05 16:03:30 +08:00
xiu2
8a7b0f12f5 优化 使用说明 2021-01-05 12:00:54 +08:00
xiu2
6a3504e98c 优化 帮助中的参数说明 2021-01-05 11:26:07 +08:00
xiu2
78a8f9c6c4 update 2021-01-05 11:24:58 +08:00
xiu2
aa11024026 update 2021-01-02 22:14:03 +08:00
xiu2
cf1a01b614 update 2021-01-02 21:10:42 +08:00
xiu2
b9159db975 update 2021-01-01 19:30:42 +08:00
xiu2
157b89d88e update 2021-01-01 19:27:38 +08:00
xiu2
d1c304ff59 update 2021-01-01 02:02:41 +08:00
xiu2
ceff5971d2 补充 README.md 2020-12-31 09:26:43 +08:00
xiu2
32d544184b update 2020-12-31 09:21:09 +08:00
xiu2
f82471671c update 2020-12-31 09:16:11 +08:00
xiu2
638273b7e7 修复 下载测速时间不准确、卡住的问题 2020-12-24 23:09:07 +08:00
xiu2
dc68529244 update 2020-12-23 11:01:37 +08:00
xiu2
29c927d3cd update 2020-12-22 23:57:46 +08:00
xiu2
fb190c661d update 2020-12-21 11:30:10 +08:00
xiu2
9e39be140a update 2020-12-21 11:15:11 +08:00
xiu2
09a578decf 优化 参数说明 2020-12-19 14:36:25 +08:00
xiu2
8ef6b3b7c2 优化 参数说明 2020-12-19 14:14:16 +08:00
xiu2
cc6b5dd7a6 update 2020-12-19 10:11:43 +08:00
xiu2
6c1166fc5e 新增 下载速度排序;修复 下载测速时间 -dt 参数自定义值无效的问题;回调 下载测速时间默认值为 10 秒 2020-12-19 09:54:18 +08:00
xiu2
f9ac05a072 update 2020-12-14 10:36:40 +08:00
xiu2
976dd79913 优化 IP 段子网掩码解析 2020-12-11 12:12:09 +08:00
xiu2
c8ef175207 优化 IP 段子网掩码解析 2020-12-11 03:12:28 +08:00
xiu2
31dc7aed3c update 2020-12-10 10:13:26 +08:00
xiu2
e3a6f80a14 update 2020-12-10 10:05:05 +08:00
xiu2
38e1d26341 update 2020-12-10 10:02:35 +08:00
xiu2
166d9abe7c 修复 上个版本更新导致的 IPv6 测速报错的问题。 2020-12-09 16:42:50 +08:00
xiu2
f9c310bfb4 update 2020-12-06 08:20:57 +08:00
xiu2
0d54b65f33 新增 测速全部 IP、检查版本更新 2020-12-05 16:03:21 +08:00
xiu2
9f2e5b5b5e add ipv6.txt 2020-11-30 18:10:41 +08:00
XIU2
7b4f6944be 优化 打印帮助时末尾换行(避免在命令行和下一行命令混在一起)
Merge pull request #13 from zhangsean/master
2020-11-30 17:10:07 +08:00
zhangsean
00b569d649 打印帮助换行 2020-11-30 17:05:07 +08:00
xiu2
4b4426c195 新增 IPv6 支持 2020-11-30 16:41:01 +08:00
xiu2
ff3a6d1d56 update 2020-11-25 12:31:57 +08:00
xiu2
a78c6e6270 update 2020-11-15 18:30:39 +08:00
xiu2
6b52fbf5ea update 2020-11-13 14:42:06 +08:00
xiu2
25fa4b65d8 优化 仅 Windows 系统才需要按下 回车键 或 Ctrl+C 退出 2020-11-12 08:11:38 +08:00
xiu2
dca761ec72 update 2020-11-11 22:56:53 +08:00
xiu2
0f5b18b305 update 2020-11-11 22:48:03 +08:00
xiu2
4e1678edc3 update 2020-11-11 19:35:47 +08:00
xiu2
65b451ec4d update 2020-11-11 19:28:14 +08:00
xiu2
72ecee9e26 update 2020-11-11 19:26:31 +08:00
xiu2
1d9f64a4a2 update 2020-11-11 18:19:12 +08:00
xiu2
40b22f660a 新增 指定延迟时间上限、下载速度下限条件 2020-11-11 18:10:53 +08:00
xiu2
12039f4850 修复 -p 0 时没有直接退出程序的问题;优化 代码 2020-11-10 21:03:09 +08:00
xiu2
129deeaf71 update 2020-11-10 19:50:43 +08:00
xiu2
3de6b38e00 优化 IP最后一段完全随机 2020-11-10 19:22:55 +08:00
xiu2
8c0e8732cc 新增 版本号标识 2020-11-10 15:55:52 +08:00
xiu2
8820c5f982 update 2020-11-10 15:35:24 +08:00
xiu2
d50c4806a6 调整 默认下载测速地址为自建地址 2020-11-08 16:46:27 +08:00
xiu2
306ce709c9 update 2020-11-08 10:48:24 +08:00
xiu2
956a35cab0 update 2020-11-08 10:43:26 +08:00
xiu2
3d49bb13ed update 2020-11-08 10:33:46 +08:00
xiu2
3ddd66b3c1 update 2020-11-07 10:38:36 +08:00
xiu2
9aa64db555 新增 自定义下载测速地址功能(-url https://xxx) 2020-11-07 10:07:00 +08:00
xiu2
9654cb8ea6 优化 下载测速文件大小 2020-11-06 12:32:23 +08:00
xiu2
13bae9c6f8 update 2020-11-06 10:01:35 +08:00
xiu2
f0fa3e4d0a update 2020-11-05 23:30:27 +08:00
xiu2
b83734b426 update 2020-11-05 23:26:18 +08:00
xiu2
c1348df16e update 2020-11-05 08:56:36 +08:00
xiu2
c52750ad9c update 2020-11-05 08:52:09 +08:00
xiu2
0e9461f3b7 update 2020-10-22 13:29:03 +08:00
xiu2
07e20028cc 修复 下载测速失效的问题 2020-10-07 02:27:56 +08:00
30 changed files with 3110 additions and 582 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 会被转过去

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
dist
Releases
*.exe
*.csv

View File

@@ -1,39 +0,0 @@
package main
import (
"bufio"
"log"
"net"
"os"
)
func loadFirstIPOfRangeFromFile(ipFile string) []net.IPAddr {
file, err := os.Open(ipFile)
if err != nil {
log.Fatal(err)
}
firstIPs := make([]net.IPAddr, 0)
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
IPString := scanner.Text()
firstIP, IPRange, err := net.ParseCIDR(IPString)
if err != nil {
log.Fatal(err)
}
firstIP[15] = ipEndWith
for IPRange.Contains(firstIP) {
firstIPCopy := make([]byte, len(firstIP))
copy(firstIPCopy, firstIP)
firstIPs = append(firstIPs, net.IPAddr{IP: firstIPCopy})
firstIP[14]++
if firstIP[14] == 0 {
firstIP[13]++
if firstIP[13] == 0 {
firstIP[12]++
}
}
}
}
return firstIPs
}

1020
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=

13
ip.txt
View File

@@ -10,5 +10,16 @@
198.41.128.0/17
162.158.0.0/15
104.16.0.0/12
172.64.0.0/13
172.64.0.0/17
172.64.128.0/18
172.64.192.0/19
172.64.224.0/22
172.64.229.0/24
172.64.230.0/23
172.64.232.0/21
172.64.240.0/21
172.64.248.0/21
172.65.0.0/16
172.66.0.0/16
172.67.0.0/16
131.0.72.0/22

97
ipv6.txt Normal file
View File

@@ -0,0 +1,97 @@
2400:cb00:2049::/48
2400:cb00:f00e::/48
2606:4700::/32
2606:4700:10::/48
2606:4700:130::/48
2606:4700:3000::/48
2606:4700:3001::/48
2606:4700:3002::/48
2606:4700:3003::/48
2606:4700:3004::/48
2606:4700:3005::/48
2606:4700:3006::/48
2606:4700:3007::/48
2606:4700:3008::/48
2606:4700:3009::/48
2606:4700:3010::/48
2606:4700:3011::/48
2606:4700:3012::/48
2606:4700:3013::/48
2606:4700:3014::/48
2606:4700:3015::/48
2606:4700:3016::/48
2606:4700:3017::/48
2606:4700:3018::/48
2606:4700:3019::/48
2606:4700:3020::/48
2606:4700:3021::/48
2606:4700:3022::/48
2606:4700:3023::/48
2606:4700:3024::/48
2606:4700:3025::/48
2606:4700:3026::/48
2606:4700:3027::/48
2606:4700:3028::/48
2606:4700:3029::/48
2606:4700:3030::/48
2606:4700:3031::/48
2606:4700:3032::/48
2606:4700:3033::/48
2606:4700:3034::/48
2606:4700:3035::/48
2606:4700:3036::/48
2606:4700:3037::/48
2606:4700:3038::/48
2606:4700:3039::/48
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

271
main.go
View File

@@ -3,173 +3,174 @@ package main
import (
"flag"
"fmt"
"io"
"net/http"
"os"
"sort"
"strconv"
"sync"
"runtime"
"time"
"github.com/cheggaaa/pb/v3"
"github.com/XIU2/CloudflareSpeedTest/task"
"github.com/XIU2/CloudflareSpeedTest/utils"
)
var version string
var disableDownload bool
var tcpPort int
var ipFile string
var outputFile string
var printResult int
var (
version, versionNew string
)
func init() {
var downloadSecond int64
var printVersion bool
const help = `
CloudflareSpeedTest
测试 Cloudflare CDN 所有 IP 的延迟和速度,获取最快 IP
var help = `
CloudflareSpeedTest ` + version + `
测试各个 CDN 或网站所有 IP 的延迟和速度,获取最快 IP (IPv4+IPv6)
https://github.com/XIU2/CloudflareSpeedTest
参数:
-n 500
测速线程数量;数值越大速度越快,请勿超过 1000(结果误差大)(默认 500)
-n 200
延迟测速线程;越多延迟测速越快,性能弱的设备 (如路由器) 请勿太高;(默认 200 最多 1000)
-t 4
延迟测速次数;单个 IP 测速次数,为 1 时将过滤丢包的IPTCP协议(默认 4)
-tp 443
延迟测速端口;延迟测速 TCP 协议的端口(默认 443)
-dn 20
下载测速数量;延迟测速并排序后,从最低延迟起下载测速数量,请勿太多(速度慢)(默认 20)
延迟测速次数;单个 IP 延迟测速次数;(默认 4)
-dn 10
下载测速数量;延迟测速并排序后,从最低延迟起下载测速的数量(默认 10 个)
-dt 10
下载测速时间;单个 IP 测速最长时间,单位:秒(默认 10)
-p 20
直接显示结果;测速后直接显示指定数量的结果,为 -1 时不显示结果直接退出;(默认 20)
下载测速时间;单个 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数据文件;如路径含有空格请加上引号;支持其他 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)
写入结果文件;如路径含有空格请加上引号;为空时不写入文件 [-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", "指定测速地址")
示例:
CloudflareST.exe -n 500 -t 4 -dn 20 -dt 10
CloudflareST.exe -n 500 -t 4 -dn 20 -dt 10 -p 20 -f "ip.txt" -o " " -dd
CloudflareST.exe -n 500 -t 4 -dn 20 -dt 10 -f "ip.txt" -o "result.csv" -dd
CloudflareST.exe -n 500 -t 4 -dn 20 -dt 10 -f "C:\abc\ip.txt" -o "C:\abc\result.csv" -dd`
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.IntVar(&pingRoutine, "n", 500, "测速线程数量")
flag.IntVar(&pingTime, "t", 4, "延迟测速次数")
flag.IntVar(&tcpPort, "tp", 443, "延迟测速端口")
flag.IntVar(&downloadTestCount, "dn", 20, "下载测速数量")
flag.Int64Var(&downloadSecond, "dt", 10, "下载测速时间")
flag.IntVar(&printResult, "p", 20, "直接显示结果")
flag.BoolVar(&disableDownload, "dd", false, "禁用下载测速")
flag.StringVar(&ipFile, "f", "ip.txt", "IP 数据文件")
flag.StringVar(&outputFile, "o", "result.csv", "输出结果文件")
flag.BoolVar(&printVersion, "v", false, "打印程序版本")
downloadTestTime = time.Duration(downloadSecond) * time.Second
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)
}
if pingRoutine <= 0 {
pingRoutine = 500
}
if pingTime <= 0 {
pingTime = 4
}
if tcpPort < 1 || tcpPort > 65535 {
tcpPort = 443
}
if downloadTestCount <= 0 {
downloadTestCount = 20
}
if downloadSecond <= 0 {
downloadSecond = 10
}
if printResult == 0 {
printResult = 20
}
if ipFile == "" {
ipFile = "ip.txt"
}
if outputFile == " " {
outputFile = ""
}
}
func main() {
initipEndWith() // 随机数
failTime = pingTime // 设置接收次数
ips := loadFirstIPOfRangeFromFile(ipFile) // 读入IP
pingCount := len(ips) * pingTime // 计算进度条总数IP*测试次数)
bar := pb.Full.Start(pingCount) // 进度条总数
var wg sync.WaitGroup
var mu sync.Mutex
var data = make([]CloudflareIPData, 0)
task.InitRandSeed() // 随机数种子
fmt.Println("开始延迟测速模式TCP端口" + strconv.Itoa(tcpPort) + "")
control := make(chan bool, pingRoutine)
for _, ip := range ips {
wg.Add(1)
control <- false
handleProgress := handleProgressGenerator(bar) // 多线程进度条
go tcpingGoroutine(&wg, &mu, ip, tcpPort, pingTime, &data, control, handleProgress)
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
}
wg.Wait()
bar.Finish()
sort.Sort(CloudflareIPDataSet(data)) // 排序
// 下载测速
if !disableDownload { // 如果禁用下载测速就跳过
if len(data) > 0 { // IP数组长度(IP数量) 大于 0 时继续
if len(data) < downloadTestCount { // 如果IP数组长度(IP数量) 小于 下载测速次数则次数改为IP数
downloadTestCount = len(data)
fmt.Println("\n[信息] IP数量小于下载测速次数下载测速次数改为IP数。\n")
}
bar = pb.Simple.Start(downloadTestCount)
fmt.Println("开始下载测速:")
for i := 0; i < downloadTestCount; i++ {
_, speed := DownloadSpeedHandler(data[i].ip)
data[i].downloadSpeed = speed
bar.Add(1)
}
bar.Finish()
} else {
fmt.Println("\n[信息] IP数量为 0跳过下载测速。")
}
}
if outputFile != "" {
ExportCsv(outputFile, data) // 输出结果到文件
}
// 直接输出结果
if printResult > 0 { // 如果禁用下载测速就跳过
dateString := convertToString(data) // 转为多维数组 [][]String
if len(dateString) > 0 { // IP数组长度(IP数量) 大于 0 时继续
if len(dateString) < printResult { // 如果IP数组长度(IP数量) 小于 打印次数则次数改为IP数量
printResult = len(dateString)
fmt.Println("\n[信息] IP数量小于显示结果数量显示结果数量改为IP数量。\n")
}
fmt.Printf("%-16s%-5s%-5s%-5s%-6s%-11s\n", "IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)")
for i := 0; i < printResult; i++ {
fmt.Printf("%-18s%-8s%-8s%-8s%-10s%-15s\n", dateString[i][0], dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5])
}
if outputFile != "" {
fmt.Printf("\n完整内容请查看 %v 文件。请按 回车键 或 Ctrl+C 退出。", outputFile)
} else {
fmt.Printf("\n请按 回车键 或 Ctrl+C 退出。")
}
var pause int
fmt.Scanln(&pause)
} else {
fmt.Println("\n[信息] IP数量为 0跳过输出结果。")
}
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)
}
}

178
script/README.md Normal file
View File

@@ -0,0 +1,178 @@
# XIU2/CloudflareSpeedTest - Script(脚本)
这里都是一些通过调用 **CFST** 并**扩展实现更多个性化功能**的脚本。
****
> [!TIP]
> 我之所以将 CFST 制作为一个**命令行程序**,就是考虑到**通用性**,因为毕竟不可能把所有需求都塞到软件内(特别是一些**个性化、小众**的需求),这样增加维护难度和精力不说,还会导致软件异常臃肿(`“变成我讨厌的样子”`),而命令行程序的优势之一就在于**可以很方便的和其他软件、脚本搭配使用**。
比如像下面这些我写的几个脚本,就是把一些需求以外置脚本方式实现。
> 即脚本调用 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>
****
#### 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
- **1. 新增** 找不到满足条件的 IP 就一直循环测速功能(默认注释)
- **2. 优化** 代码
#### 2021年09月29日版本 v1.0.2
- **1. 修复** 当测速结果 IP 数量为 0 时,脚本没有退出的问题
#### 2021年04月29日版本 v1.0.1
- **1. 优化** 不再需要加上 -p 0 参数来避免回车键退出了(现在可以即显示结果,又不用担心回车键退出程序)
#### 2021年01月28日版本 v1.0.0
- **1. 发布** 第一个版本
</details>
****
## 📑 cfst_3proxy.bat (已内置压缩包)
脚本会运行 CFST 测速后获取最快 IP 并替换 3Proxy 配置文件中的旧 Cloudflare CDN IP。
可以把所有 Cloudflare CDN IP 都重定向至最快 IP实现一劳永逸的加速所有使用 Cloudflare CDN 的网站(不需要一个个添加域名到 Hosts 了)。
> **作者:**[@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 就一直循环测速] 功能,在指定下载测速下限时没有重新测速的问题(默认注释)
#### 2021年12月17日版本 v1.0.4
- **1. 新增** 找不到满足条件的 IP 就一直循环测速功能(默认注释)
- **2. 优化** 代码
#### 2021年09月29日版本 v1.0.3
- **1. 修复** 当测速结果 IP 数量为 0 时,脚本没有退出的问题
#### 2021年04月29日版本 v1.0.2
- **1. 优化** 不再需要加上 -p 0 参数来避免回车键退出了(现在可以即显示结果,又不用担心回车键退出程序)
#### 2021年03月16日版本 v1.0.1
- **1. 优化** 代码及注释内容
#### 2021年03月13日版本 v1.0.0
- **1. 发布** 第一个版本
</details>
****
## 📑 cfst_dnspod.sh
如果你的域名托管在 **dnspod**,则可以通过 dnspod 官方提供的 API 来自动更新域名解析记录!
脚本会运行 CFST 测速获得最快 IP并通过 Cloudflare API 来更新域名解析记录为这个最快 IP。
> **作者:**[@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. 优化** 代码
#### 2021年09月29日版本 v1.0.3
- **1. 修复** 当测速结果 IP 数量为 0 时,脚本没有退出的问题
#### 2021年04月29日版本 v1.0.2
- **1. 优化** 不再需要加上 -p 0 参数来避免回车键退出了(现在可以即显示结果,又不用担心回车键退出程序)
#### 2021年01月27日版本 v1.0.1
- **1. 优化** 配置从文件中读取
#### 2021年01月26日版本 v1.0.0
- **1. 发布** 第一个版本
</details>
****
## 📑 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>
****
## 功能建议/问题反馈
如果这些脚本使用过程中你遇到了什么问题,可以先去脚本对应的 **`使用说明`** 帖子里看看是否有别人问过了。
如果没找到类似问题,那么就在脚本对应的 **`使用说明`** 帖子里直接评论问作者吧。

134
script/cfst_3proxy.bat Normal file
View File

@@ -0,0 +1,134 @@
:: --------------------------------------------------------------
:: <09><>Ŀ: CloudflareSpeedTest <20>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD> 3Proxy
:: <09>汾: 1.0.6
:: <09><><EFBFBD><EFBFBD>: XIU2
:: <09><>Ŀ: https://github.com/XIU2/CloudflareSpeedTest
:: --------------------------------------------------------------
@echo off
Setlocal Enabledelayedexpansion
::<3A>ж<EFBFBD><D0B6>Ƿ<EFBFBD><C7B7>ѻ<EFBFBD><D1BB>ù<EFBFBD><C3B9><EFBFBD>ԱȨ<D4B1><C8A8>
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
if '%errorlevel%' NEQ '0' (
goto UACPrompt
) else ( goto gotAdmin )
:<><D0B4> vbs <20>ű<EFBFBD><C5B1>Թ<EFBFBD><D4B9><EFBFBD>Ա<EFBFBD><D4B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1>ű<EFBFBD><C5B1><EFBFBD>bat<61><74>
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
echo UAC.ShellExecute "%~s0", "", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
exit /B
::<3A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ vbs <20>ű<EFBFBD><C5B1><EFBFBD><EFBFBD>ڣ<EFBFBD><DAA3><EFBFBD>ɾ<EFBFBD><C9BE>
:gotAdmin
if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
pushd "%CD%"
CD /D "%~dp0"
::<3A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD><D0B6>Ƿ<EFBFBD><C7B7>Ի<EFBFBD><D4BB>ù<EFBFBD><C3B9><EFBFBD>ԱȨ<D4B1>ޣ<EFBFBD><DEA3><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD>о<EFBFBD>ȥ<EFBFBD><C8A5>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>DZ<EFBFBD><C7B1>ű<EFBFBD><C5B1><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD>
::<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>Ϊ 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.
set /p nowip="<EFBFBD><EFBFBD><EFBFBD>뵱ǰ 3Proxy <20><><EFBFBD><EFBFBD>ʹ<EFBFBD>õ<EFBFBD> Cloudflare CDN IP <20><><EFBFBD>س<EFBFBD><D8B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ò<EFBFBD><C3B2>裩:"
echo !nowip!>nowip_3proxy.txt
echo.
)
::<3A><> nowip_3proxy.txt <20>ļ<EFBFBD><C4BC><EFBFBD>ȡ<EFBFBD><C8A1>ǰʹ<C7B0>õ<EFBFBD> Cloudflare CDN IP
set /p nowip=<nowip_3proxy.txt
echo <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>...
:: <20><><EFBFBD><EFBFBD> RESET <20>Ǹ<EFBFBD><C7B8><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><DCB5><EFBFBD>׼<EFBFBD><D7BC><EFBFBD><EFBFBD>
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܾͰ<DCBE><CDB0><EFBFBD><EFBFBD><EFBFBD> 3 <20><> goto :STOP <20><>Ϊ goto :RESET <20><><EFBFBD><EFBFBD>
:RESET
:: <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 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 "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 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 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>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 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
::)
echo %bestip%>nowip_3proxy.txt
echo.
echo <20><> IP Ϊ %nowip%
echo <20><> IP Ϊ %bestip%
:: <20><EFBFBD><EBBDAB><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD> D:\Program Files\3Proxy <20><>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD> 3Proxy <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼
CD /d "D:\Program Files\3Proxy"
:: <20><>ȷ<EFBFBD><C8B7><EFBFBD><EFBFBD><EFBFBD>иýű<C3BD>ǰ<EFBFBD><C7B0><EFBFBD>Ѿ<EFBFBD><D1BE><EFBFBD><EFBFBD>Թ<EFBFBD> 3Proxy <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>в<EFBFBD>ʹ<EFBFBD>ã<EFBFBD>
echo.
echo <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD> 3proxy.cfg <20>ļ<EFBFBD><C4BC><EFBFBD>3proxy.cfg_backup<75><70>...
copy 3proxy.cfg 3proxy.cfg_backup
echo.
echo <20><>ʼ<EFBFBD>滻...
(
for /f "tokens=*" %%i in (3proxy.cfg_backup) do (
set s=%%i
set s=!s:%nowip%=%bestip%!
echo !s!
)
)>3proxy.cfg
net stop 3proxy
net start 3proxy
echo <20><><EFBFBD><EFBFBD>...
echo.
:STOP
pause

42
script/cfst_ddns.bat Normal file
View File

@@ -0,0 +1,42 @@
:: --------------------------------------------------------------
:: 项目: CloudflareSpeedTest 自动更新域名解析记录
:: 版本: 1.0.6
:: 作者: XIU2
:: 项目: https://github.com/XIU2/CloudflareSpeedTest
:: --------------------------------------------------------------
@echo off
Setlocal Enabledelayedexpansion
:: 这里可以自己添加、修改 CFST 的运行参数echo.| 的作用是自动回车退出程序(不再需要加上 -p 0 参数了)
echo.|cfst.exe -o "result_ddns.txt"
:: 判断结果文件是否存在,如果不存在说明结果为 0
if not exist result_ddns.txt (
echo.
echo CFST 测速结果 IP 数量为 0跳过下面步骤...
goto :END
)
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

65
script/cfst_ddns.sh Normal file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# --------------------------------------------------------------
# 项目: CloudflareSpeedTest 自动更新域名解析记录
# 版本: 1.0.5
# 作者: XIU2
# 项目: https://github.com/XIU2/CloudflareSpeedTest
# --------------------------------------------------------------
_READ() {
[[ ! -e "cfst_ddns.conf" ]] && echo -e "[错误] 配置文件不存在 [cfst_ddns.conf] !" && exit 1
CONFIG=$(cat "cfst_ddns.conf")
FOLDER=$(echo "${CONFIG}"|grep 'FOLDER='|awk -F '=' '{print $NF}')
[[ -z "${FOLDER}" ]] && echo -e "[错误] 缺少配置项 [FOLDER] !" && exit 1
ZONE_ID=$(echo "${CONFIG}"|grep 'ZONE_ID='|awk -F '=' '{print $NF}')
[[ -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
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}')
[[ -z "${NAME}" ]] && echo -e "[错误] 缺少配置项 [NAME] !" && exit 1
TTL=$(echo "${CONFIG}"|grep 'TTL='|awk -F '=' '{print $NF}')
[[ -z "${TTL}" ]] && echo -e "[错误] 缺少配置项 [TTL] !" && exit 1
PROXIED=$(echo "${CONFIG}"|grep 'PROXIED='|awk -F '=' '{print $NF}')
[[ -z "${PROXIED}" ]] && echo -e "[错误] 缺少配置项 [PROXIED] !" && exit 1
}
_UPDATE() {
# 这里可以自己添加、修改 CFST 的运行参数
./cfst -o "result_ddns.txt"
# 判断结果文件是否存在,如果不存在说明结果为 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 "CFST 测速结果 IP 数量为 0跳过下面步骤..."
exit 0
fi
# 如果 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
cd "${FOLDER}"
_UPDATE

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

124
script/cfst_hosts.bat Normal file
View File

@@ -0,0 +1,124 @@
:: --------------------------------------------------------------
:: <09><>Ŀ: CloudflareSpeedTest <20>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD> Hosts
:: <09>汾: 1.0.5
:: <09><><EFBFBD><EFBFBD>: XIU2
:: <09><>Ŀ: https://github.com/XIU2/CloudflareSpeedTest
:: --------------------------------------------------------------
@echo off
Setlocal Enabledelayedexpansion
::<3A>ж<EFBFBD><D0B6>Ƿ<EFBFBD><C7B7>ѻ<EFBFBD><D1BB>ù<EFBFBD><C3B9><EFBFBD>ԱȨ<D4B1><C8A8>
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
if '%errorlevel%' NEQ '0' (
goto UACPrompt
) else ( goto gotAdmin )
:<><D0B4> vbs <20>ű<EFBFBD><C5B1>Թ<EFBFBD><D4B9><EFBFBD>Ա<EFBFBD><D4B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1>ű<EFBFBD><C5B1><EFBFBD>bat<61><74>
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
echo UAC.ShellExecute "%~s0", "", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
exit /B
::<3A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ vbs <20>ű<EFBFBD><C5B1><EFBFBD><EFBFBD>ڣ<EFBFBD><DAA3><EFBFBD>ɾ<EFBFBD><C9BE>
:gotAdmin
if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
pushd "%CD%"
CD /D "%~dp0"
::<3A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD><D0B6>Ƿ<EFBFBD><C7B7>Ի<EFBFBD><D4BB>ù<EFBFBD><C3B9><EFBFBD>ԱȨ<D4B1>ޣ<EFBFBD><DEA3><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD>о<EFBFBD>ȥ<EFBFBD><C8A5>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>DZ<EFBFBD><C7B1>ű<EFBFBD><C5B1><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD>
::<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>Ϊ 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>
set /p nowip="<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Cloudflare CDN IP <20><><EFBFBD>س<EFBFBD><D8B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ò<EFBFBD><C3B2>裩:"
echo !nowip!>nowip_hosts.txt
echo.
)
::<3A><> nowip_hosts.txt <20>ļ<EFBFBD><C4BC><EFBFBD>ȡ<EFBFBD><C8A1>ǰ Hosts <20><>ʹ<EFBFBD>õ<EFBFBD> Cloudflare CDN IP
set /p nowip=<nowip_hosts.txt
echo <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>...
:: <20><><EFBFBD><EFBFBD> RESET <20>Ǹ<EFBFBD><C7B8><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><DCB5><EFBFBD>׼<EFBFBD><D7BC><EFBFBD><EFBFBD>
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܾͰ<DCBE><CDB0><EFBFBD><EFBFBD><EFBFBD> 3 <20><> goto :STOP <20><>Ϊ goto :RESET <20><><EFBFBD><EFBFBD>
:RESET
:: <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 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 "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 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 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>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 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
::)
echo %bestip%>nowip_hosts.txt
echo.
echo <20><> IP Ϊ %nowip%
echo <20><> IP Ϊ %bestip%
CD /d "C:\Windows\System32\drivers\etc"
echo.
echo <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD> Hosts <20>ļ<EFBFBD><C4BC><EFBFBD>hosts_backup<75><70>...
copy hosts hosts_backup
echo.
echo <20><>ʼ<EFBFBD>滻...
(
for /f "tokens=*" %%i in (hosts_backup) do (
set s=%%i
set s=!s:%nowip%=%bestip%!
echo !s!
)
)>hosts
echo <20><><EFBFBD><EFBFBD>...
echo.
:STOP
pause

63
script/cfst_hosts.sh Normal file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# --------------------------------------------------------------
# 项目: CloudflareSpeedTest 自动更新 Hosts
# 版本: 1.0.4
# 作者: XIU2
# 项目: https://github.com/XIU2/CloudflareSpeedTest
# --------------------------------------------------------------
_CHECK() {
while true
do
if [[ ! -e "nowip_hosts.txt" ]]; then
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
echo ${NOWIP} > nowip_hosts.txt
break
else
echo "该 IP 不能是空!"
fi
else
break
fi
done
}
_UPDATE() {
echo -e "开始测速..."
NOWIP=$(head -1 nowip_hosts.txt)
# 这里可以自己添加、修改 CFST 的运行参数
./cfst -o "result_hosts.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}')
if [[ -z "${BESTIP}" ]]; then
echo "CFST 测速结果 IP 数量为 0跳过下面步骤..."
exit 0
fi
echo ${BESTIP} > nowip_hosts.txt
echo -e "\n旧 IP 为 ${NOWIP}\n新 IP 为 ${BESTIP}\n"
echo "开始备份 Hosts 文件hosts_backup..."
\cp -f /etc/hosts /etc/hosts_backup
echo -e "开始替换..."
sed -i 's/'${NOWIP}'/'${BESTIP}'/g' /etc/hosts
echo -e "完成..."
}
_CHECK
_UPDATE

63
script/cfst_hosts_mac.sh Normal file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# --------------------------------------------------------------
# 项目: CloudflareSpeedTest 自动更新 Hosts
# 版本: 1.0.4
# 作者: XIU2
# 项目: https://github.com/XIU2/CloudflareSpeedTest
# --------------------------------------------------------------
_CHECK() {
while true
do
if [[ ! -e "nowip_hosts.txt" ]]; then
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
echo ${NOWIP} > nowip_hosts.txt
break
else
echo "该 IP 不能是空!"
fi
else
break
fi
done
}
_UPDATE() {
echo -e "开始测速..."
NOWIP=$(head -1 nowip_hosts.txt)
# 这里可以自己添加、修改 CFST 的运行参数
./cfst -o "result_hosts.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}')
if [[ -z "${BESTIP}" ]]; then
echo "CFST 测速结果 IP 数量为 0跳过下面步骤..."
exit 0
fi
echo ${BESTIP} > nowip_hosts.txt
echo -e "\n旧 IP 为 ${NOWIP}\n新 IP 为 ${BESTIP}\n"
echo "开始备份 Hosts 文件hosts_backup..."
\cp -f /etc/hosts /etc/hosts_backup
echo -e "开始替换..."
sed -i '' 's/'${NOWIP}'/'${BESTIP}'/g' /etc/hosts
echo -e "完成..."
}
_CHECK
_UPDATE

230
task/download.go Normal file
View File

@@ -0,0 +1,230 @@
package task
import (
"context"
"fmt"
"io"
"net"
"net/http"
"sort"
"strconv"
"time"
"github.com/XIU2/CloudflareSpeedTest/utils"
"github.com/VividCortex/ewma"
)
const (
bufferSize = 1024
defaultURL = "https://cf.xiu2.xyz/url"
defaultTimeout = 10 * time.Second
defaultDisableDownload = false
defaultTestNum = 10
defaultMinSpeed float64 = 0.0
)
var (
URL = defaultURL
Timeout = defaultTimeout
Disable = defaultDisableDownload
TestCount = defaultTestNum
MinSpeed = defaultMinSpeed
)
func checkDownloadDefault() {
if URL == "" {
URL = defaultURL
}
if Timeout <= 0 {
Timeout = defaultTimeout
}
if TestCount <= 0 {
TestCount = defaultTestNum
}
if MinSpeed <= 0.0 {
MinSpeed = defaultMinSpeed
}
}
func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSet) {
checkDownloadDefault()
if Disable {
return utils.DownloadSpeedSet(ipSet)
}
if len(ipSet) <= 0 { // IP 数组长度(IP数量) 大于 0 时才会继续下载测速
utils.Yellow.Println("[信息] 延迟测速结果 IP 数量为 0跳过下载测速。")
return
}
testNum := TestCount // 等待下载测速的队列数量 先默认等于 下载测速数量(-dn
if len(ipSet) < TestCount || MinSpeed > 0 { // 如果延迟测速并过滤后的 IP 数组长度(IP数量) 小于 下载测速数量(-dn即 -dn 预期数量是不够的),或者指定了 下载测速下限 (-sl) 条件(这就可能要全部下载测速一遍,直到找齐预期数量或测完为止),则 等待下载测速的队列数量 修正为 IP 数量
testNum = len(ipSet)
}
if testNum < TestCount { // 如果 等待下载测速的队列数量 小于 下载测速数量(-dn显然 -dn 预期数量是不够的),所以 下载测速数量(-dn修正为 等待下载测速的队列数量
TestCount = testNum
}
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, 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, "")
speedSet = append(speedSet, ipSet[i]) // 高于下载速度下限时,添加到新数组中
if len(speedSet) == TestCount { // 凑够满足条件的 IP 时(下载测速数量 -dn就跳出循环
break
}
}
}
bar.Done()
if MinSpeed == 0.00 { // 如果没有指定下载速度下限,则直接返回所有测速数据
speedSet = utils.DownloadSpeedSet(ipSet)
} else if utils.Debug && len(speedSet) == 0 { // 如果指定了下载速度下限,且是调试模式下,且没有找到任何一个满足条件的 IP 时,返回所有测速数据,供用户查看当前的测速结果,以便适当调低预期测速条件
utils.Yellow.Println("[调试] 没有满足 下载速度下限 条件的 IP忽略条件返回所有测速数据方便下次测速时调整条件。")
speedSet = utils.DownloadSpeedSet(ipSet)
}
// 按速度排序
sort.Sort(speedSet)
return
}
func getDialContext(ip *net.IPAddr) func(ctx context.Context, network, address string) (net.Conn, error) {
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, string) {
var lastRedirectURL string // 用于记录最后一次重定向目标,以便在访问错误时输出
client := &http.Client{
Transport: &http.Transport{DialContext: getDialContext(ip)},
Timeout: Timeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
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
req.Header.Del("Referer")
}
return nil
},
}
req, err := http.NewRequest("GET", URL, nil)
if err != nil {
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 {
if utils.Debug { // 调试模式下,输出更多信息
printDownloadDebugInfo(ip, err, 0, URL, lastRedirectURL, response)
}
return 0.0, ""
}
defer response.Body.Close()
if response.StatusCode != 200 {
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) // 加上下载测速时间得到的结束时间
contentLength := response.ContentLength // 文件大小
buffer := make([]byte, bufferSize)
var (
contentRead int64 = 0
timeSlice = Timeout / 100
timeCounter = 1
lastContentRead int64 = 0
)
var nextTime = timeStart.Add(timeSlice * time.Duration(timeCounter))
e := ewma.NewMovingAverage()
// 循环计算,如果文件下载完了(两者相等),则退出循环(终止测速)
for contentLength != contentRead {
currentTime := time.Now()
if currentTime.After(nextTime) {
timeCounter++
nextTime = timeStart.Add(timeSlice * time.Duration(timeCounter))
e.Add(float64(contentRead - lastContentRead))
lastContentRead = contentRead
}
// 如果超出下载测速时间,则退出循环(终止测速)
if currentTime.After(timeEnd) {
break
}
bufferRead, err := response.Body.Read(buffer)
if err != nil {
if err != io.EOF { // 如果文件下载过程中遇到报错(如 Timeout且并不是因为文件下载完了则退出循环终止测速
break
} else if contentLength == -1 { // 文件下载完成 且 文件大小未知则退出循环终止测速例如https://speed.cloudflare.com/__down?bytes=200000000 这样的,如果在 10 秒内就下载完成了,会导致测速结果明显偏低甚至显示为 0.00(下载速度太快时)
break
}
// 获取上个时间片
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), 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 ""
}

190
task/ip.go Normal file
View File

@@ -0,0 +1,190 @@
package task
import (
"bufio"
"log"
"math/rand"
"net"
"os"
"strconv"
"strings"
"time"
)
const defaultInputFile = "ip.txt"
var (
// 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)))
}
type IPRanges struct {
ips []*net.IPAddr
mask string
firstIP net.IP
ipNet *net.IPNet
}
func newIPRanges() *IPRanges {
return &IPRanges{
ips: make([]*net.IPAddr, 0),
}
}
// 如果是单独 IP 则加上子网掩码,反之则获取子网掩码(r.mask)
func (r *IPRanges) fixIP(ip string) string {
// 如果不含有 '/' 则代表不是 IP 段,而是一个单独的 IP因此需要加上 /32 /128 子网掩码
if i := strings.IndexByte(ip, '/'); i < 0 {
if isIPv4(ip) {
r.mask = "/32"
} else {
r.mask = "/128"
}
ip += r.mask
} else {
r.mask = ip[i:]
}
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 {
log.Fatalln("ParseCIDR err", err)
}
}
func (r *IPRanges) appendIPv4(d byte) {
r.appendIP(net.IPv4(r.firstIP[12], r.firstIP[13], r.firstIP[14], d))
}
func (r *IPRanges) appendIP(ip net.IP) {
r.ips = append(r.ips, &net.IPAddr{IP: ip})
}
// 返回第四段 ip 的最小值及可用数目
func (r *IPRanges) getIPRange() (minIP, hosts byte) {
minIP = r.firstIP[15] & r.ipNet.Mask[3] // IP 第四段最小值
// 根据子网掩码获取主机数量
m := net.IPv4Mask(255, 255, 255, 255)
for i, v := range r.ipNet.Mask {
m[i] ^= v
}
total, _ := strconv.ParseInt(m.String(), 16, 32) // 总可用 IP 数
if total > 255 { // 矫正 第四段 可用 IP 数
hosts = 255
return
}
hosts = byte(total)
return
}
func (r *IPRanges) chooseIPv4() {
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))
}
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() {
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) // 加入 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 {
ranges := newIPRanges()
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()
}
}
}
return ranges.ips
}

149
task/tcping.go Normal file
View File

@@ -0,0 +1,149 @@
package task
import (
"fmt"
"net"
"sort"
"strconv"
"sync"
"time"
"github.com/XIU2/CloudflareSpeedTest/utils"
)
const (
tcpConnectTimeout = time.Second * 1
maxRoutine = 1000
defaultRoutines = 200
defaultPort = 443
defaultPingTimes = 4
)
var (
Routines = defaultRoutines
TCPPort int = defaultPort
PingTimes int = defaultPingTimes
)
type Ping struct {
wg *sync.WaitGroup
m *sync.Mutex
ips []*net.IPAddr
csv utils.PingDelaySet
control chan bool
bar *utils.Bar
}
func checkPingDefault() {
if Routines <= 0 {
Routines = defaultRoutines
}
if TCPPort <= 0 || TCPPort >= 65535 {
TCPPort = defaultPort
}
if PingTimes <= 0 {
PingTimes = defaultPingTimes
}
}
func NewPing() *Ping {
checkPingDefault()
ips := loadIPRanges()
return &Ping{
wg: &sync.WaitGroup{},
m: &sync.Mutex{},
ips: ips,
csv: make(utils.PingDelaySet, 0),
control: make(chan bool, Routines),
bar: utils.NewBar(len(ips), "可用:", ""),
}
}
func (p *Ping) Run() utils.PingDelaySet {
if len(p.ips) == 0 {
return p.csv
}
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)
}
for _, ip := range p.ips {
p.wg.Add(1)
p.control <- false
go p.start(ip)
}
p.wg.Wait()
p.bar.Done()
sort.Sort(p.csv)
return p.csv
}
func (p *Ping) start(ip *net.IPAddr) {
defer p.wg.Done()
p.tcpingHandler(ip)
<-p.control
}
// bool connectionSucceed float32 time
func (p *Ping) tcping(ip *net.IPAddr) (bool, time.Duration) {
startTime := time.Now()
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)
if err != nil {
return false, 0
}
defer conn.Close()
duration := time.Since(startTime)
return true, 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++
totalDelay += delay
}
}
return
}
func (p *Ping) appendIPData(data *utils.PingData) {
p.m.Lock()
defer p.m.Unlock()
p.csv = append(p.csv, utils.CloudflareIPData{
PingData: data,
})
}
// handle tcping
func (p *Ping) tcpingHandler(ip *net.IPAddr) {
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
}
data := &utils.PingData{
IP: ip,
Sended: PingTimes,
Received: recv,
Delay: totalDlay / time.Duration(recv),
Colo: colo,
}
p.appendIPData(data)
}

160
tcping.go
View File

@@ -1,160 +0,0 @@
package main
import (
"context"
"io"
"net"
"net/http"
"strconv"
"sync"
"time"
"github.com/VividCortex/ewma"
)
//bool connectionSucceed float32 time
func tcping(ip net.IPAddr, tcpPort int) (bool, float32) {
startTime := time.Now()
conn, err := net.DialTimeout("tcp", ip.String()+":"+strconv.Itoa(tcpPort), tcpConnectTimeout)
if err != nil {
return false, 0
} else {
var endTime = time.Since(startTime)
var duration = float32(endTime.Microseconds()) / 1000.0
_ = conn.Close()
return true, duration
}
}
//pingReceived pingTotalTime
func checkConnection(ip net.IPAddr, tcpPort int) (int, float32) {
pingRecv := 0
var pingTime float32 = 0.0
for i := 1; i <= failTime; i++ {
pingSucceed, pingTimeCurrent := tcping(ip, tcpPort)
if pingSucceed {
pingRecv++
pingTime += pingTimeCurrent
}
}
return pingRecv, pingTime
}
//return Success packetRecv averagePingTime specificIPAddr
func tcpingHandler(ip net.IPAddr, tcpPort int, pingCount int, progressHandler func(e progressEvent)) (bool, int, float32, net.IPAddr) {
ipCanConnect := false
pingRecv := 0
var pingTime float32 = 0.0
for !ipCanConnect {
pingRecvCurrent, pingTimeCurrent := checkConnection(ip, tcpPort)
if pingRecvCurrent != 0 {
ipCanConnect = true
pingRecv = pingRecvCurrent
pingTime = pingTimeCurrent
} else {
ip.IP[15]++
if ip.IP[15] == 0 {
break
}
break
}
}
if ipCanConnect {
progressHandler(AvailableIPFound)
for i := failTime; i < pingCount; i++ {
pingSuccess, pingTimeCurrent := tcping(ip, tcpPort)
progressHandler(NormalPing)
if pingSuccess {
pingRecv++
pingTime += pingTimeCurrent
}
}
return true, pingRecv, pingTime / float32(pingRecv), ip
} else {
progressHandler(NoAvailableIPFound)
return false, 0, 0, net.IPAddr{}
}
}
func tcpingGoroutine(wg *sync.WaitGroup, mutex *sync.Mutex, ip net.IPAddr, tcpPort int, pingCount int, csv *[]CloudflareIPData, control chan bool, progressHandler func(e progressEvent)) {
defer wg.Done()
success, pingRecv, pingTimeAvg, currentIP := tcpingHandler(ip, tcpPort, pingCount, progressHandler)
if success {
mutex.Lock()
var cfdata CloudflareIPData
cfdata.ip = currentIP
cfdata.pingReceived = pingRecv
cfdata.pingTime = pingTimeAvg
cfdata.pingCount = pingCount
*csv = append(*csv, cfdata)
mutex.Unlock()
}
<-control
}
func GetDialContextByAddr(fakeSourceAddr string) func(ctx context.Context, network, address string) (net.Conn, error) {
return func(ctx context.Context, network, address string) (net.Conn, error) {
c, e := (&net.Dialer{}).DialContext(ctx, network, fakeSourceAddr)
return c, e
}
}
//bool : can download,float32 downloadSpeed
func DownloadSpeedHandler(ip net.IPAddr) (bool, float32) {
var client = http.Client{
Transport: nil,
CheckRedirect: nil,
Jar: nil,
Timeout: 0,
}
client.Transport = &http.Transport{
DialContext: GetDialContextByAddr(ip.String() + ":443"),
}
response, err := client.Get(url)
if err != nil {
return false, 0
} else {
defer func() { _ = response.Body.Close() }()
if response.StatusCode == 200 {
timeStart := time.Now()
timeEnd := timeStart.Add(downloadTestTime)
contentLength := response.ContentLength
buffer := make([]byte, downloadBufferSize)
var contentRead int64 = 0
var timeSlice = downloadTestTime / 100
var timeCounter = 1
var lastContentRead int64 = 0
var nextTime = timeStart.Add(timeSlice * time.Duration(timeCounter))
e := ewma.NewMovingAverage()
for contentLength != contentRead {
var currentTime = time.Now()
if currentTime.After(nextTime) {
timeCounter += 1
nextTime = timeStart.Add(timeSlice * time.Duration(timeCounter))
e.Add(float64(contentRead - lastContentRead))
lastContentRead = contentRead
}
if currentTime.After(timeEnd) {
break
}
bufferRead, err := response.Body.Read(buffer)
contentRead += int64(bufferRead)
if err != nil {
if err != io.EOF {
break
} else {
e.Add(float64(contentRead-lastContentRead) / (float64(nextTime.Sub(currentTime)) / float64(timeSlice)))
}
}
}
return true, float32(e.Value()) / (float32(downloadTestTime.Seconds()) / 100)
} else {
return false, 0
}
}
}

123
util.go
View File

@@ -1,123 +0,0 @@
package main
import (
"encoding/csv"
"log"
"math/rand"
"net"
"os"
"strconv"
"time"
"github.com/cheggaaa/pb/v3"
)
type CloudflareIPData struct {
ip net.IPAddr
pingCount int
pingReceived int
recvRate float32
downloadSpeed float32
pingTime float32
}
func (cf *CloudflareIPData) getRecvRate() float32 {
if cf.recvRate == 0 {
pingLost := cf.pingCount - cf.pingReceived
cf.recvRate = float32(pingLost) / float32(cf.pingCount)
}
return cf.recvRate
}
func ExportCsv(filePath string, data []CloudflareIPData) {
fp, err := os.Create(filePath)
if err != nil {
log.Fatalf("创建文件["+filePath+"]句柄失败,%v", err)
return
}
defer fp.Close()
w := csv.NewWriter(fp) //创建一个新的写入文件流
w.Write([]string{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)"})
w.WriteAll(convertToString(data))
w.Flush()
}
func (cf *CloudflareIPData) toString() []string {
result := make([]string, 6)
result[0] = cf.ip.String()
result[1] = strconv.Itoa(cf.pingCount)
result[2] = strconv.Itoa(cf.pingReceived)
result[3] = strconv.FormatFloat(float64(cf.getRecvRate()), 'f', 2, 32)
result[4] = strconv.FormatFloat(float64(cf.pingTime), 'f', 2, 32)
result[5] = strconv.FormatFloat(float64(cf.downloadSpeed)/1024/1024, 'f', 2, 32)
return result
}
func convertToString(data []CloudflareIPData) [][]string {
result := make([][]string, 0)
for _, v := range data {
result = append(result, v.toString())
}
return result
}
var pingTime int
var pingRoutine int
var ipEndWith uint8 = 0
type progressEvent int
const (
NoAvailableIPFound progressEvent = iota
AvailableIPFound
NormalPing
)
const url string = "https://apple.freecdn.workers.dev/105/media/us/iphone-11-pro/2019/3bd902e4-0752-4ac1-95f8-6225c32aec6d/films/product/iphone-11-pro-product-tpl-cc-us-2019_1280x720h.mp4"
var downloadTestTime time.Duration
const downloadBufferSize = 1024
var downloadTestCount int
//const defaultTcpPort = 443
const tcpConnectTimeout = time.Second * 1
var failTime int
type CloudflareIPDataSet []CloudflareIPData
func initipEndWith() {
rand.Seed(time.Now().UnixNano())
ipEndWith = uint8(rand.Intn(254) + 1)
}
func handleProgressGenerator(pb *pb.ProgressBar) func(e progressEvent) {
return func(e progressEvent) {
switch e {
case NoAvailableIPFound:
pb.Add(pingTime)
case AvailableIPFound:
pb.Add(failTime)
case NormalPing:
pb.Increment()
}
}
}
func (cfs CloudflareIPDataSet) Len() int {
return len(cfs)
}
func (cfs CloudflareIPDataSet) Less(i, j int) bool {
if (cfs)[i].getRecvRate() != cfs[j].getRecvRate() {
return cfs[i].getRecvRate() < cfs[j].getRecvRate()
}
return cfs[i].pingTime < cfs[j].pingTime
}
func (cfs CloudflareIPDataSet) Swap(i, j int) {
cfs[i], cfs[j] = cfs[j], cfs[i]
}

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
)

195
utils/csv.go Normal file
View File

@@ -0,0 +1,195 @@
package utils
import (
"encoding/csv"
"fmt"
"log"
"net"
"os"
"strconv"
"time"
)
const (
defaultOutput = "result.csv"
maxDelay = 9999 * time.Millisecond
minDelay = 0 * time.Millisecond
maxLossRate float32 = 1.0
)
var (
InputMaxDelay = maxDelay
InputMinDelay = minDelay
InputMaxLossRate = maxLossRate
Output = defaultOutput
PrintNum = 10
Debug = false // 是否开启调试模式
)
// 是否打印测试结果
func NoPrintResult() bool {
return PrintNum == 0
}
// 是否输出到文件
func noOutput() bool {
return Output == "" || Output == " "
}
type PingData struct {
IP *net.IPAddr
Sended int
Received int
Delay time.Duration
Colo string
}
type CloudflareIPData struct {
*PingData
lossRate float32
DownloadSpeed float64
}
// 计算丢包率
func (cf *CloudflareIPData) getLossRate() float32 {
if cf.lossRate == 0 {
pingLost := cf.Sended - cf.Received
cf.lossRate = float32(pingLost) / float32(cf.Sended)
}
return cf.lossRate
}
func (cf *CloudflareIPData) toString() []string {
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.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
}
func ExportCsv(data []CloudflareIPData) {
if noOutput() || len(data) == 0 {
return
}
fp, err := os.Create(Output)
if err != nil {
log.Fatalf("创建文件[%s]失败:%v", Output, err)
return
}
defer fp.Close()
w := csv.NewWriter(fp) //创建一个新的写入文件流
_ = w.Write([]string{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度(MB/s)", "地区码"})
_ = w.WriteAll(convertToString(data))
w.Flush()
}
func convertToString(data []CloudflareIPData) [][]string {
result := make([][]string, 0)
for _, v := range data {
result = append(result, v.toString())
}
return result
}
// 延迟丢包排序
type PingDelaySet []CloudflareIPData
// 延迟条件过滤
func (s PingDelaySet) FilterDelay() (data PingDelaySet) {
if InputMaxDelay > maxDelay || InputMinDelay < minDelay { // 当输入的延迟条件不在默认范围内时,不进行过滤
return s
}
if InputMaxDelay == maxDelay && InputMinDelay == minDelay { // 当输入的延迟条件为默认值时,不进行过滤
return s
}
for _, v := range s {
if v.Delay > InputMaxDelay { // 平均延迟上限,延迟大于条件最大值时,后面的数据都不满足条件,直接跳出循环
break
}
if v.Delay < InputMinDelay { // 平均延迟下限,延迟小于条件最小值时,不满足条件,跳过
continue
}
data = append(data, v) // 延迟满足条件时,添加到新数组中
}
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].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]
}
// 下载速度排序
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() {
if NoPrintResult() {
return
}
if len(s) <= 0 { // IP数组长度(IP数量) 大于 0 时继续
fmt.Println("\n[信息] 完整测速结果 IP 数量为 0跳过输出结果。")
return
}
dateString := convertToString(s) // 转为多维数组 [][]String
if len(dateString) < PrintNum { // 如果IP数组长度(IP数量) 小于 打印次数则次数改为IP数量
PrintNum = len(dateString)
}
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
}
}
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], dateString[i][6])
}
if !noOutput() {
fmt.Printf("\n完整测速结果已写入 %v 文件,可使用记事本/表格软件查看。\n", Output)
}
}

25
utils/progress.go Normal file
View File

@@ -0,0 +1,25 @@
package utils
import (
"fmt"
"github.com/cheggaaa/pb/v3"
)
type Bar struct {
pb *pb.ProgressBar
}
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, MyStrVal string) {
b.pb.Set("MyStr", MyStrVal).Add(num)
}
func (b *Bar) Done() {
b.pb.Finish()
}