mirror of
https://github.com/XIU2/CloudflareSpeedTest.git
synced 2026-03-06 22:59:33 +08:00
新增 支持显示地区码(机场三字码,仅限 Cloudflare、AWS CloudFront,HTTPing 和 下载测速(无论是哪个测速模式)过程中都会自动获取);
新增 调试模式运行参数(-debug 方便排查下载测速过程中遇到的问题); 新增 彩色输出内容; 调整 当没找到符合速度条件的 IP 时,默认不再直接忽略条件输出所有 IP 测速结果了,而是只有在调试模式下才会输出;
This commit is contained in:
133
README.md
133
README.md
@@ -58,12 +58,12 @@ mkdir CloudflareST
|
||||
cd CloudflareST
|
||||
|
||||
# 下载 CloudflareST 压缩包(自行根据需求替换 URL 中 [版本号] 和 [文件名])
|
||||
wget -N https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.2.5/CloudflareST_linux_amd64.tar.gz
|
||||
wget -N https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.3.0/CloudflareST_linux_amd64.tar.gz
|
||||
# 如果你是在国内网络环境中下载,那么请使用下面这几个镜像加速之一:
|
||||
# wget -N https://ghp.ci/https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.2.5/CloudflareST_linux_amd64.tar.gz
|
||||
# wget -N https://ghproxy.cc/https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.2.5/CloudflareST_linux_amd64.tar.gz
|
||||
# wget -N https://ghproxy.net/https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.2.5/CloudflareST_linux_amd64.tar.gz
|
||||
# wget -N https://gh-proxy.com/https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.2.5/CloudflareST_linux_amd64.tar.gz
|
||||
# wget -N https://ghp.ci/https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.3.0/CloudflareST_linux_amd64.tar.gz
|
||||
# wget -N https://ghproxy.cc/https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.3.0/CloudflareST_linux_amd64.tar.gz
|
||||
# wget -N https://ghproxy.net/https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.3.0/CloudflareST_linux_amd64.tar.gz
|
||||
# wget -N https://gh-proxy.com/https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.3.0/CloudflareST_linux_amd64.tar.gz
|
||||
# 如果下载失败的话,尝试删除 -N 参数(如果是为了更新,则记得提前删除旧压缩包 rm CloudflareST_linux_amd64.tar.gz )
|
||||
|
||||
# 解压(不需要删除旧文件,会直接覆盖,自行根据需求替换 文件名)
|
||||
@@ -93,20 +93,20 @@ chmod +x CloudflareST
|
||||
|
||||
### 结果示例
|
||||
|
||||
测速完毕后,默认会显示**最快的 10 个 IP**,示例:
|
||||
测速完毕后,默认会显示**最快的 10 个 IP**,示例(仅为输出内容示例):
|
||||
|
||||
``` bash
|
||||
IP 地址 已发送 已接收 丢包率 平均延迟 下载速度 (MB/s)
|
||||
104.27.200.69 4 4 0.00 146.23 28.64
|
||||
172.67.60.78 4 4 0.00 139.82 15.02
|
||||
104.25.140.153 4 4 0.00 146.49 14.90
|
||||
104.27.192.65 4 4 0.00 140.28 14.07
|
||||
172.67.62.214 4 4 0.00 139.29 12.71
|
||||
104.27.207.5 4 4 0.00 145.92 11.95
|
||||
172.67.54.193 4 4 0.00 146.71 11.55
|
||||
104.22.66.8 4 4 0.00 147.42 11.11
|
||||
104.27.197.63 4 4 0.00 131.29 10.26
|
||||
172.67.58.91 4 4 0.00 140.19 9.14
|
||||
IP 地址 已发送 已接收 丢包率 平均延迟 下载速度(MB/s) 地区码
|
||||
104.27.200.69 4 4 0.00 146.23 28.64 LAX
|
||||
172.67.60.78 4 4 0.00 139.82 15.02 SEA
|
||||
104.25.140.153 4 4 0.00 146.49 14.90 SJC
|
||||
104.27.192.65 4 4 0.00 140.28 14.07 LAX
|
||||
172.67.62.214 4 4 0.00 139.29 12.71 LAX
|
||||
104.27.207.5 4 4 0.00 145.92 11.95 LAX
|
||||
172.67.54.193 4 4 0.00 146.71 11.55 LAX
|
||||
104.22.66.8 4 4 0.00 147.42 11.11 SEA
|
||||
104.27.197.63 4 4 0.00 131.29 10.26 FRA
|
||||
172.67.58.91 4 4 0.00 140.19 9.14 SJC
|
||||
...
|
||||
|
||||
# 如果平均延迟非常低(如 0.xx),则说明 CloudflareST 测速时走了代理,请先关闭代理软件后再测速。
|
||||
@@ -132,8 +132,8 @@ IP 地址 已发送 已接收 丢包率 平均延迟 下载速度
|
||||
完整结果保存在当前目录下的 `result.csv` 文件中,用**记事本/表格软件**打开,格式如下:
|
||||
|
||||
```
|
||||
IP 地址,已发送,已接收,丢包率,平均延迟,下载速度 (MB/s)
|
||||
104.27.200.69,4,4,0.00,146.23,28.64
|
||||
IP 地址,已发送,已接收,丢包率,平均延迟,下载速度(MB/s),地区码
|
||||
104.27.200.69,4,4,0.00,146.23,28.64,LAX
|
||||
```
|
||||
|
||||
> _大家可以按自己需求,对完整结果**进一步筛选处理**,或者去看一看进阶使用**指定过滤条件**!_
|
||||
@@ -163,15 +163,17 @@ https://github.com/XIU2/CloudflareSpeedTest
|
||||
指定测速端口;延迟测速/下载测速时使用的端口;(默认 443 端口)
|
||||
-url https://cf.xiu2.xyz/url
|
||||
指定测速地址;延迟测速(HTTPing)/下载测速时使用的地址,默认地址不保证可用性,建议自建;
|
||||
当下载测速时,软件会从 HTTP 响应头中获取该 IP 当前的机场地区码(支持 Cloudflare、AWS CloudFront)并显示出来。
|
||||
|
||||
-httping
|
||||
切换测速模式;延迟测速模式改为 HTTP 协议,所用测试地址为 [-url] 参数;(默认 TCPing)
|
||||
当使用 HTTP 测速模式时,软件会从 HTTP 响应头中获取该 IP 当前的机场地区码(支持 Cloudflare、AWS CloudFront)并显示出来。
|
||||
注意:HTTPing 本质上也算一种 网络扫描 行为,因此如果你在服务器上面运行,需要降低并发(-n),否则可能会被一些严格的商家暂停服务。
|
||||
如果你遇到 HTTPing 首次测速可用 IP 数量正常,后续测速越来越少甚至直接为 0,但停一段时间后又恢复了的情况,那么也可能是被 运营商、Cloudflare CDN 认为你在网络扫描而 触发临时限制机制,因此才会过一会儿就恢复了,建议降低并发(-n)减少这种情况的发生。
|
||||
-httping-code 200
|
||||
有效状态代码;HTTPing 延迟测速时网页返回的有效 HTTP 状态码,仅限一个;(默认 200 301 302)
|
||||
-cfcolo HKG,KHH,NRT,LAX,SEA,SJC,FRA,MAD
|
||||
匹配指定地区;地区名为当地机场三字码,英文逗号分隔,支持小写,支持 Cloudflare、AWS CloudFront,仅 HTTPing 模式可用;(默认 所有地区)
|
||||
匹配指定地区;地区名为当地机场地区码,英文逗号分隔,支持小写,支持 Cloudflare、AWS CloudFront,仅 HTTPing 模式可用;(默认 所有地区)
|
||||
|
||||
-tl 200
|
||||
平均延迟上限;只输出低于指定平均延迟的 IP,各上下限条件可搭配使用;(默认 9999 ms)
|
||||
@@ -196,6 +198,9 @@ https://github.com/XIU2/CloudflareSpeedTest
|
||||
-allip
|
||||
测速全部的IP;对 IP 段中的每个 IP (仅支持 IPv4) 进行测速;(默认 每个 /24 段随机测速一个 IP)
|
||||
|
||||
-debug
|
||||
调试输出模式;会在一些非预期情况下输出更多日志以便判断原因;(默认 关闭)
|
||||
|
||||
-v
|
||||
打印程序版本 + 检查版本更新
|
||||
-h
|
||||
@@ -220,10 +225,10 @@ https://github.com/XIU2/CloudflareSpeedTest
|
||||
321 / 321 [-----------------------------------------------------------] 可用: 30
|
||||
开始下载测速(下限:1.00 MB/s, 数量:5, 队列:10)
|
||||
3 / 5 [-----------------------------------------↗--------------------]
|
||||
IP 地址 已发送 已接收 丢包率 平均延迟 下载速度 (MB/s)
|
||||
XXX.XXX.XXX.XXX 4 4 0.00 83.32 3.66
|
||||
XXX.XXX.XXX.XXX 4 4 0.00 107.81 2.49
|
||||
XXX.XXX.XXX.XXX 4 3 0.25 149.59 1.04
|
||||
IP 地址 已发送 已接收 丢包率 平均延迟 下载速度(MB/s) 地区码
|
||||
XXX.XXX.XXX.XXX 4 4 0.00 83.32 3.66 LAX
|
||||
XXX.XXX.XXX.XXX 4 4 0.00 107.81 2.49 LAX
|
||||
XXX.XXX.XXX.XXX 4 3 0.25 149.59 1.04 N/A
|
||||
|
||||
完整测速结果已写入 result.csv 文件,可使用记事本/表格软件查看。
|
||||
按下 回车键 或 Ctrl+C 退出。
|
||||
@@ -266,7 +271,7 @@ CloudflareST 会先延迟测速,在这过程中进度条右侧会实时显示
|
||||
|
||||
****
|
||||
|
||||
另外,如果全部队列 IP 都测速完了,但一个满足下载速度条件的 IP 都没有,那么就会**直接输出全部队列 IP 的下载测速结果**,这样你就能看到这些 IP 的下载速度都有多少,心里也就有数了,然后**适当调低 `-sl` 再试试**。
|
||||
另外,如果全部队列 IP 都测速完了,但一个满足下载速度条件的 IP 都没有,你可能需要调低预期的下载测速下限条件,但你需要知道当前的大概测速速度都在什么范围,那么你就可以加上 `-debug` 参数开启调试模式,这样再遇到这种情况时,就会**忽略条件返回所有测速数据**,你就能看到这些 IP 的下载速度都有多少,心里也就有数了,然后**适当调低 `-sl` 再试试**。
|
||||
|
||||
同样,延迟测速方面,`可用: 30`、`队列:10` 这两个数值也可以让你清楚,你设置的延迟条件对你来说是否过于苛刻。如果可用 IP 一大堆,但条件过滤后只剩下 2、3 个,那不用说就知道需要**调低预期的延迟/丢包条件**了。
|
||||
|
||||
@@ -383,6 +388,8 @@ HTTP 协议适用于快速测试某域名指向某 IP 时是否可以访问,
|
||||
|
||||
> 另外,本软件 HTTPing 仅获取**响应头(response headers)**,并不获取正文内容(即 URL 文件大小不影响 HTTPing 测试,但如果你还要下载测速的话,那么还是需要一个大文件的),类似于 curl -i 功能。
|
||||
|
||||
> 另外,HTTPing 过程中,软件会从 HTTP 响应头中获取该 IP 当前的机场地区码(支持 Cloudflare、AWS CloudFront)并显示出来,而 TCPing 过程中无法这样做(但 下载测速 时也会这样做来获取地区码,毕竟下载测速也是个 HTTP 链接)
|
||||
|
||||
``` bash
|
||||
# 只需加上 -httping 参数即可切换到 HTTP 协议延迟测速模式
|
||||
CloudflareST.exe -httping
|
||||
@@ -403,7 +410,7 @@ CloudflareST.exe -httping -tp 80 -url http://cdn.cloudflare.steamstatic.com/stea
|
||||
|
||||
****
|
||||
|
||||
#### \# 匹配指定地区(colo 机场三字码)
|
||||
#### \# 匹配指定地区(colo 机场地区码)
|
||||
|
||||
<details>
|
||||
<summary><code><strong>「 点击展开 查看内容 」</strong></code></summary>
|
||||
@@ -420,22 +427,24 @@ Cloudflare CDN 的节点 IP 是 Anycast IP,即每个 IP 对应的服务器节
|
||||
|
||||
因此,对于这种 Anycast IP 的实际服务器位置,就不能靠那些在线 IP 地址位置查询网站来判断了。
|
||||
|
||||
除了通过 **HTTP 响应头**获取机场三字码外(该功能的实现方式),还可以手动访问 `http://CloudflareIP/cdn-cgi/trace` 来获知 CDN 分配给你的实际节点地区机场三字码。
|
||||
除了通过 **HTTP 响应头**获取机场地区码外(该功能的实现方式),还可以手动访问 `http://CloudflareIP/cdn-cgi/trace` 来获知 CDN 分配给你的实际节点地区机场地区码。
|
||||
|
||||
> 该功能支持 Cloudflare CDN 和 AWS CloudFront CDN,且这两个 CDN 的机场三字码是通用的(算是惯例)。
|
||||
> 该功能支持 Cloudflare CDN 和 AWS CloudFront CDN,且这两个 CDN 的机场地区码是通用的(算是惯例)。
|
||||
> **注意**:如果你要用于筛选 AWS CloudFront CDN 地区,那么要通过 `-url` 参数指定一个使用 AWS CloudFront CDN 的下载测速地址(因为软件默认下载测速地址是 Cloudflare CDN 的)
|
||||
|
||||
``` bash
|
||||
# 指定地区名后,延迟测速后得到的结果就都是指定地区的 IP 了(如果没有指定 -dd 的话则会继续进行下载测速)
|
||||
# 如果延迟测速后结果为 0,则说明没有找到任何一个(未超时可用的)指定地区的 IP。
|
||||
# 节点地区名为当地 机场三字码,指定多个时用英文逗号分隔,v2.2.3 版本后支持小写
|
||||
# 节点地区名为当地 机场地区码,指定多个时用英文逗号分隔,v2.2.3 版本后支持小写
|
||||
|
||||
CloudflareST.exe -httping -cfcolo HKG,KHH,NRT,LAX,SEA,SJC,FRA,MAD
|
||||
|
||||
# 注意,该参数只有在 HTTPing 延迟测速模式下才可用(因为软件是通过 HTTP 链接中的响应头来获得该 IP 的实际地区机场三字码)
|
||||
# 注意,该参数只有在 HTTPing 延迟测速模式下才可用(因为软件是通过 HTTP 链接中的响应头来获得该 IP 的实际地区机场地区码)
|
||||
|
||||
# 另外,HTTPing 过程中,软件会从 HTTP 响应头中获取该 IP 当前的机场地区码(支持 Cloudflare、AWS CloudFront)并显示出来,而 TCPing 过程中无法这样做(但 下载测速 时也会这样做来获取地区码,毕竟下载测速也是个 HTTP 链接)
|
||||
```
|
||||
|
||||
> 两个 CDN 机场三字码通用,因此各地区名可见:https://www.cloudflarestatus.com/
|
||||
> 两个 CDN 机场地区码通用,因此各地区名可见:https://www.cloudflarestatus.com/
|
||||
|
||||
</details>
|
||||
|
||||
@@ -574,7 +583,7 @@ CloudflareST.exe -tlr 0.25
|
||||
CloudflareST.exe -sl 5 -dn 10
|
||||
```
|
||||
|
||||
> 如果**没有找到一个满足速度**条件的 IP,那么会**忽略条件输出所有 IP 测速结果**(方便你下次测速时调整条件)。
|
||||
> 如果**没有找到一个满足速度**条件的 IP,那么不会输出任何内容,你可能需要调低预期的下载测速下限条件,但你需要知道当前的大概测速速度都在什么范围,那么你就可以加上 `-debug` 参数开启调试模式,这样再遇到这种情况时,就会**忽略条件返回所有测速数据**,你就能看到这些 IP 的下载速度都有多少,心里也就有数了,然后**适当调低 `-sl` 再试试**。
|
||||
|
||||
> 没有指定平均延迟上限时,如果一直**凑不够**满足条件的 IP 数量,就会**一直测速**下去。
|
||||
> 所以建议**同时指定 [下载速度下限] + [平均延迟上限]**,这样测速到指定延迟上限还没凑够数量,就会终止测速。
|
||||
@@ -591,7 +600,7 @@ CloudflareST.exe -tl 200 -sl 5.6 -dn 10
|
||||
```
|
||||
|
||||
> 如果**没有找到一个满足延迟**条件的 IP,那么不会输出任何内容。
|
||||
> 如果**没有找到一个满足速度**条件的 IP,那么会忽略条件输出所有 IP 测速结果(方便你下次测速时调整条件)。
|
||||
> 如果**没有找到一个满足速度**条件的 IP,那么不会输出任何内容,但可以通过加上 `-debug` 参数开启调试模式,这时会忽略条件输出所有 IP 测速结果(方便你下次测速时调整条件)。
|
||||
> 所以建议先不指定条件测速一遍,看看平均延迟和下载速度大概在什么范围,避免指定条件**过低/过高**!
|
||||
|
||||
> 因为 Cloudflare 公开的 IP 段是**回源 IP+任播 IP**,而**回源 IP**是无法使用的,所以下载测速是 0.00。
|
||||
@@ -652,6 +661,64 @@ CloudflareST.exe -f 1.txt
|
||||
|
||||
****
|
||||
|
||||
#### \# 下载测速都是 0.00 ?
|
||||
|
||||
<details>
|
||||
<summary><code><strong>「 点击展开 查看内容 」</strong></code></summary>
|
||||
|
||||
****
|
||||
|
||||
首先要明白,本软件的下载测速过程,本质上和你将 `IP 下载测速地址的域名` 写入 hosts 文件,然后浏览器去访问下载测速地址是一样的,只不过软件将其自动化了(类似于 `curl -I --resolve 下载测速地址的域名:443:IP https://下载测速地址`)。
|
||||
|
||||
因此如果下载测速结果全都是 0.00 MB/s,那么以为着下载测速失败,就只有这几种可能性。
|
||||
|
||||
1. **下载测速地址有问题**
|
||||
2. **测速的 IP 地址有问题**
|
||||
3. **你的网络有问题**
|
||||
|
||||
但在排查具体是哪个问题前,可以先在你原先的 CloudflareST 运行命令后追加一个 `-debug` 参数来开启调试模式,重新跑一边测速,这样下载过程中报错了就能直接看到下载测速失败的具体原因。
|
||||
|
||||
常见的下载测速失败报错原因有(因为是原生报错信息,因此基本都是英文):
|
||||
|
||||
1. `... read: connection reset by peer ... `
|
||||
下载测速地址被阻断了,可能是蔷干的,也可能是运营商干的(比如移动或部分地区的白名单)
|
||||
2. `... HTTP 状态码: 403 ...`
|
||||
像这种直接提示 HTTP 状态码的,比较好判断,如 403 就是测速地址禁止你访问,404 就是测速地址找不到文件,具体可以搜索 HTTP 状态码含义
|
||||
3. `... context deadline exceeded (Client.Timeout exceeded while awaiting headers) ...`
|
||||
这种一般是超时引起的,可能是 IP 等网络问题,也可能是 -dt 下载测速时间设置的太短了,当然默认的 10 秒到不至于超时
|
||||
|
||||
> 如果你遇到了其他报错原因,且翻译后还是不懂,可以发 Issues 或 Discussions 询问。
|
||||
|
||||
根据上面的报错原因排查一遍后,如果还是无法解决,那么可以尝试下面这些:
|
||||
|
||||
**一、下载测速地址有问题**:
|
||||
|
||||
先去 [#490](https://github.com/XIU2/CloudflareSpeedTest/discussions/490) 找几个其他的下载测速地址都试试。
|
||||
|
||||
如果其中有能下载测速出结果的,则就代表你之前使用的下载测速地址有问题(注意,目前默认下载测速地址仅为一个带负载均衡轮询的重定向链接,会自动重定向到上面帖子里大家分享的公益下载测速地址,而这些地址在**不同地区的可用性可能有差异**,因此可能出现之前不行现在又正常的情况,如果**想要稳定,建议自建**,上面帖子写了几种自建方法)。
|
||||
|
||||
如果找了很多,都是一样 0.00,那么就要考虑其他可能性了。
|
||||
|
||||
****
|
||||
|
||||
**二、测速的 IP 地址有问题**:
|
||||
|
||||
你用来测速的 IP 地址,可能一些 TCP 测试是通的,但实际上因为各种原因导致不能建立 HTTP 链接(比如是回源 IP,比如是企业用户专用 IP 等等),因此你可以多尝试一些其他的 IP 看是否可行。
|
||||
|
||||
****
|
||||
|
||||
**三、你的网络有问题**:
|
||||
|
||||
这个就比较麻烦了,如果你现在是用电脑+宽带来使用 CloudflareST 测速的,那么可以尝试关闭手机 WIFI 并打开流量,然后数据线连接电脑,设置好 USB 网络共享(不同手机系统不太一样,具体自行搜索哈),并拔掉电脑的网线,这样你的电脑现在就是走的手机流量数据网络了(如果手机流量数据和宽带不是一个运营商会更好排查),然后再次运行 CloudflareST 测速看看结果是否改变(也可以同时尝试上面的排查方法来交叉验证)。
|
||||
|
||||
如果测速结果正常了,那么显然就是宽带网络的问题,如果还是一样的 0.00,那么就麻烦了。。。
|
||||
|
||||
****
|
||||
|
||||
</details>
|
||||
|
||||
****
|
||||
|
||||
#### \# 一劳永逸加速所有使用 Cloudflare CDN 的网站(不需要再一个个添加域名到 Hosts 了)
|
||||
|
||||
我以前说过,开发该软件项目的目的就是为了通过**改 Hosts 的方式来加速访问使用 Cloudflare CDN 的网站**。
|
||||
|
||||
9
main.go
9
main.go
@@ -43,7 +43,7 @@ https://github.com/XIU2/CloudflareSpeedTest
|
||||
-httping-code 200
|
||||
有效状态代码;HTTPing 延迟测速时网页返回的有效 HTTP 状态码,仅限一个;(默认 200 301 302)
|
||||
-cfcolo HKG,KHH,NRT,LAX,SEA,SJC,FRA,MAD
|
||||
匹配指定地区;地区名为当地机场三字码,英文逗号分隔,仅 HTTPing 模式可用;(默认 所有地区)
|
||||
匹配指定地区;地区名为当地机场地区码,英文逗号分隔,仅 HTTPing 模式可用;(默认 所有地区)
|
||||
|
||||
-tl 200
|
||||
平均延迟上限;只输出低于指定平均延迟的 IP,各上下限条件可搭配使用;(默认 9999 ms)
|
||||
@@ -68,6 +68,9 @@ https://github.com/XIU2/CloudflareSpeedTest
|
||||
-allip
|
||||
测速全部的IP;对 IP 段中的每个 IP (仅支持 IPv4) 进行测速;(默认 每个 /24 段随机测速一个 IP)
|
||||
|
||||
-debug
|
||||
调试输出模式;会在一些非预期情况下输出更多日志以便判断原因;(默认 关闭)
|
||||
|
||||
-v
|
||||
打印程序版本 + 检查版本更新
|
||||
-h
|
||||
@@ -99,12 +102,14 @@ https://github.com/XIU2/CloudflareSpeedTest
|
||||
flag.BoolVar(&task.Disable, "dd", false, "禁用下载测速")
|
||||
flag.BoolVar(&task.TestAll, "allip", false, "测速全部 IP")
|
||||
|
||||
flag.BoolVar(&utils.Debug, "debug", false, "调试输出模式")
|
||||
|
||||
flag.BoolVar(&printVersion, "v", false, "打印程序版本")
|
||||
flag.Usage = func() { fmt.Print(help) }
|
||||
flag.Parse()
|
||||
|
||||
if task.MinSpeed > 0 && time.Duration(maxDelay)*time.Millisecond == utils.InputMaxDelay {
|
||||
fmt.Println("[小提示] 在使用 [-sl] 参数时,建议搭配 [-tl] 参数,以避免因凑不够 [-dn] 数量而一直测速...")
|
||||
fmt.Println("\033[33m[提示] 在使用 [-sl] 参数时,建议搭配 [-tl] 参数,以避免因凑不够 [-dn] 数量而一直测速...\033[0m")
|
||||
}
|
||||
utils.InputMaxDelay = time.Duration(maxDelay) * time.Millisecond
|
||||
utils.InputMinDelay = time.Duration(minDelay) * time.Millisecond
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
|
||||
return utils.DownloadSpeedSet(ipSet)
|
||||
}
|
||||
if len(ipSet) <= 0 { // IP数组长度(IP数量) 大于 0 时才会继续下载测速
|
||||
fmt.Println("\n[信息] 延迟测速结果 IP 数量为 0,跳过下载测速。")
|
||||
fmt.Println("\n\033[33m[信息] 延迟测速结果 IP 数量为 0,跳过下载测速。\033[0m")
|
||||
return
|
||||
}
|
||||
testNum := TestCount
|
||||
@@ -65,7 +65,7 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
|
||||
TestCount = testNum
|
||||
}
|
||||
|
||||
fmt.Printf("开始下载测速(下限:%.2f MB/s, 数量:%d, 队列:%d)\n", MinSpeed, TestCount, testNum)
|
||||
fmt.Printf("\033[34m开始下载测速(下限:%.2f MB/s, 数量:%d, 队列:%d)\033[0m\n", MinSpeed, TestCount, testNum)
|
||||
// 控制 下载测速进度条 与 延迟测速进度条 长度一致(强迫症)
|
||||
bar_a := len(strconv.Itoa(len(ipSet)))
|
||||
bar_b := " "
|
||||
@@ -74,8 +74,11 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
|
||||
}
|
||||
bar := utils.NewBar(TestCount, bar_b, "")
|
||||
for i := 0; i < testNum; i++ {
|
||||
speed := downloadHandler(ipSet[i].IP)
|
||||
speed, colo := downloadHandler(ipSet[i].IP)
|
||||
ipSet[i].DownloadSpeed = speed
|
||||
if ipSet[i].Colo == "" { // 只有当 Colo 是空的时候,才写入,否则代表之前是 httping 测速并获取过了
|
||||
ipSet[i].Colo = colo
|
||||
}
|
||||
// 在每个 IP 下载测速后,以 [下载速度下限] 条件过滤结果
|
||||
if speed >= MinSpeed*1024*1024 {
|
||||
bar.Grow(1, "")
|
||||
@@ -86,7 +89,8 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
|
||||
}
|
||||
}
|
||||
bar.Done()
|
||||
if len(speedSet) == 0 { // 没有符合速度限制的数据,返回所有测试数据
|
||||
if utils.Debug && len(speedSet) == 0 { // 调试模式下,没有满足速度限制的数据,返回所有测速数据供用户查看当前的测速结果,以便适当调低预期测速条件
|
||||
fmt.Println("\033[33m[调试] 没有满足 下载速度下限 条件的 IP,忽略条件返回所有测速数据(方便下次测速时调整条件)。\033[0m")
|
||||
speedSet = utils.DownloadSpeedSet(ipSet)
|
||||
}
|
||||
// 按速度排序
|
||||
@@ -107,12 +111,15 @@ func getDialContext(ip *net.IPAddr) func(ctx context.Context, network, address s
|
||||
}
|
||||
|
||||
// return download Speed
|
||||
func downloadHandler(ip *net.IPAddr) float64 {
|
||||
func downloadHandler(ip *net.IPAddr) (float64, string) {
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{DialContext: getDialContext(ip)},
|
||||
Timeout: Timeout,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
if len(via) > 10 { // 限制最多重定向 10 次
|
||||
if utils.Debug { // 调试模式下,输出更多信息
|
||||
fmt.Printf("\033[31m[调试] IP: %s, 下载测速地址重定向次数过多,终止测速,URL: %s\033[0m\n", ip.String(), req.URL.String())
|
||||
}
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
if req.Header.Get("Referer") == defaultURL { // 当使用默认下载测速地址时,重定向不携带 Referer
|
||||
@@ -123,19 +130,31 @@ func downloadHandler(ip *net.IPAddr) float64 {
|
||||
}
|
||||
req, err := http.NewRequest("GET", URL, nil)
|
||||
if err != nil {
|
||||
return 0.0
|
||||
if utils.Debug { // 调试模式下,输出更多信息
|
||||
fmt.Printf("\033[31m[调试] IP: %s, 下载测速请求创建失败,错误信息: %v, URL: %s\033[0m\n", ip.String(), err, URL)
|
||||
}
|
||||
return 0.0, ""
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36")
|
||||
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return 0.0
|
||||
if utils.Debug { // 调试模式下,输出更多信息
|
||||
fmt.Printf("\033[31m[调试] IP: %s, 下载测速失败,错误信息: %v, URL: , 最终URL: %s%s\033[0m\n", ip.String(), err, URL, response.Request.URL.String())
|
||||
}
|
||||
return 0.0, ""
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return 0.0
|
||||
if utils.Debug { // 调试模式下,输出更多信息
|
||||
fmt.Printf("\033[31m[调试] IP: %s, 下载测速终止,HTTP 状态码: %d, URL: %s, 最终URL: %s\033[0m\n", ip.String(), response.StatusCode, URL, response.Request.URL.String())
|
||||
}
|
||||
return 0.0, ""
|
||||
}
|
||||
// 通过头部 Server 值判断是 Cloudflare 还是 AWS CloudFront 并设置 cfRay 为各自的机场地区码完整内容
|
||||
colo := getHeaderColo(response.Header)
|
||||
|
||||
timeStart := time.Now() // 开始时间(当前)
|
||||
timeEnd := timeStart.Add(Timeout) // 加上下载测速时间得到的结束时间
|
||||
|
||||
@@ -179,5 +198,5 @@ func downloadHandler(ip *net.IPAddr) float64 {
|
||||
}
|
||||
contentRead += int64(bufferRead)
|
||||
}
|
||||
return e.Value() / (Timeout.Seconds() / 120)
|
||||
return e.Value() / (Timeout.Seconds() / 120), colo
|
||||
}
|
||||
|
||||
102
task/httping.go
102
task/httping.go
@@ -18,11 +18,11 @@ var (
|
||||
HttpingStatusCode int
|
||||
HttpingCFColo string
|
||||
HttpingCFColomap *sync.Map
|
||||
OutRegexp = regexp.MustCompile(`[A-Z]{3}`)
|
||||
ColoRegexp = regexp.MustCompile(`[A-Z]{3}`)
|
||||
)
|
||||
|
||||
// pingReceived pingTotalTime
|
||||
func (p *Ping) httping(ip *net.IPAddr) (int, time.Duration) {
|
||||
func (p *Ping) httping(ip *net.IPAddr) (int, time.Duration, string) {
|
||||
hc := http.Client{
|
||||
Timeout: time.Second * 2,
|
||||
Transport: &http.Transport{
|
||||
@@ -35,84 +35,79 @@ func (p *Ping) httping(ip *net.IPAddr) (int, time.Duration) {
|
||||
}
|
||||
|
||||
// 先访问一次获得 HTTP 状态码 及 Cloudflare Colo
|
||||
var colo string
|
||||
{
|
||||
requ, err := http.NewRequest(http.MethodHead, URL, nil)
|
||||
request, err := http.NewRequest(http.MethodHead, URL, nil)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
return 0, 0, ""
|
||||
}
|
||||
requ.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")
|
||||
resp, err := hc.Do(requ)
|
||||
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 {
|
||||
return 0, 0
|
||||
return 0, 0, ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer response.Body.Close()
|
||||
|
||||
//fmt.Println("IP:", ip, "StatusCode:", resp.StatusCode, resp.Request.URL)
|
||||
//fmt.Println("IP:", ip, "StatusCode:", response.StatusCode, response.Request.URL)
|
||||
// 如果未指定的 HTTP 状态码,或指定的状态码不合规,则默认只认为 200、301、302 才算 HTTPing 通过
|
||||
if HttpingStatusCode == 0 || HttpingStatusCode < 100 && HttpingStatusCode > 599 {
|
||||
if resp.StatusCode != 200 && resp.StatusCode != 301 && resp.StatusCode != 302 {
|
||||
return 0, 0
|
||||
if response.StatusCode != 200 && response.StatusCode != 301 && response.StatusCode != 302 {
|
||||
return 0, 0, ""
|
||||
}
|
||||
} else {
|
||||
if resp.StatusCode != HttpingStatusCode {
|
||||
return 0, 0
|
||||
if response.StatusCode != HttpingStatusCode {
|
||||
return 0, 0, ""
|
||||
}
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
io.Copy(io.Discard, response.Body)
|
||||
|
||||
// 只有指定了地区才匹配机场三字码
|
||||
// 通过头部 Server 值判断是 Cloudflare 还是 AWS CloudFront 并设置 cfRay 为各自的机场地区码完整内容
|
||||
colo = getHeaderColo(response.Header)
|
||||
|
||||
// 只有指定了地区才匹配机场地区码
|
||||
if HttpingCFColo != "" {
|
||||
// 通过头部 Server 值判断是 Cloudflare 还是 AWS CloudFront 并设置 cfRay 为各自的机场三字码完整内容
|
||||
cfRay := func() string {
|
||||
if resp.Header.Get("Server") == "cloudflare" {
|
||||
return resp.Header.Get("CF-RAY") // 示例 cf-ray: 7bd32409eda7b020-SJC
|
||||
}
|
||||
return resp.Header.Get("x-amz-cf-pop") // 示例 X-Amz-Cf-Pop: SIN52-P1
|
||||
}()
|
||||
colo := p.getColo(cfRay)
|
||||
if colo == "" { // 没有匹配到三字码或不符合指定地区则直接结束该 IP 测试
|
||||
return 0, 0
|
||||
// 判断是否匹配指定的地区码
|
||||
colo = p.filterColo(colo)
|
||||
if colo == "" { // 没有匹配到地区码或不符合指定地区则直接结束该 IP 测试
|
||||
return 0, 0, ""
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 循环测速计算延迟
|
||||
success := 0
|
||||
var delay time.Duration
|
||||
for i := 0; i < PingTimes; i++ {
|
||||
requ, err := http.NewRequest(http.MethodHead, URL, nil)
|
||||
request, err := http.NewRequest(http.MethodHead, URL, nil)
|
||||
if err != nil {
|
||||
log.Fatal("意外的错误,情报告:", err)
|
||||
return 0, 0
|
||||
return 0, 0, ""
|
||||
}
|
||||
requ.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")
|
||||
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 {
|
||||
requ.Header.Set("Connection", "close")
|
||||
request.Header.Set("Connection", "close")
|
||||
}
|
||||
startTime := time.Now()
|
||||
resp, err := hc.Do(requ)
|
||||
response, err := hc.Do(request)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
success++
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
io.Copy(io.Discard, response.Body)
|
||||
_ = response.Body.Close()
|
||||
duration := time.Since(startTime)
|
||||
delay += duration
|
||||
|
||||
}
|
||||
|
||||
return success, delay
|
||||
|
||||
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 {
|
||||
@@ -121,21 +116,36 @@ func MapColoMap() *sync.Map {
|
||||
return colomap
|
||||
}
|
||||
|
||||
func (p *Ping) getColo(b string) string {
|
||||
if b == "" {
|
||||
// 从响应头中获取 地区码 值
|
||||
func getHeaderColo(header http.Header) (colo string) {
|
||||
// 如果是 Cloudflare 的服务器,则获取 CF-RAY 头部
|
||||
if header.Get("Server") == "cloudflare" {
|
||||
colo = header.Get("CF-RAY") // 示例 cf-ray: 7bd32409eda7b020-SJC
|
||||
} else { // 如果是 AWS CloudFront 的服务器,则获取 X-Amz-Cf-Pop 头部
|
||||
colo = header.Get("x-amz-cf-pop") // 示例 X-Amz-Cf-Pop: SIN52-P1
|
||||
}
|
||||
|
||||
// 如果没有获取到头部信息,说明不是 Cloudflare 和 AWS CloudFront,则直接返回空字符串
|
||||
if colo == "" {
|
||||
return ""
|
||||
}
|
||||
// 正则匹配并返回 机场三字码
|
||||
out := OutRegexp.FindString(b)
|
||||
// 正则匹配并返回 机场地区码
|
||||
return ColoRegexp.FindString(colo)
|
||||
}
|
||||
|
||||
// 处理地区码
|
||||
func (p *Ping) filterColo(colo string) string {
|
||||
if colo == "" {
|
||||
return ""
|
||||
}
|
||||
// 如果没有指定 -cfcolo 参数,则直接返回
|
||||
if HttpingCFColomap == nil {
|
||||
return out
|
||||
return colo
|
||||
}
|
||||
// 匹配 机场三字码 是否为指定的地区
|
||||
_, ok := HttpingCFColomap.Load(out)
|
||||
// 匹配 机场地区码 是否为指定的地区
|
||||
_, ok := HttpingCFColomap.Load(colo)
|
||||
if ok {
|
||||
return out
|
||||
return colo
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ func (p *Ping) Run() utils.PingDelaySet {
|
||||
return p.csv
|
||||
}
|
||||
if Httping {
|
||||
fmt.Printf("开始延迟测速(模式:HTTP, 端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
|
||||
fmt.Printf("\033[34m开始延迟测速(模式:HTTP, 端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\033[0m\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
|
||||
} else {
|
||||
fmt.Printf("开始延迟测速(模式:TCP, 端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
|
||||
fmt.Printf("\033[34m开始延迟测速(模式:TCP, 端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\033[0m\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
|
||||
}
|
||||
for _, ip := range p.ips {
|
||||
p.wg.Add(1)
|
||||
@@ -104,11 +104,12 @@ func (p *Ping) tcping(ip *net.IPAddr) (bool, time.Duration) {
|
||||
}
|
||||
|
||||
// pingReceived pingTotalTime
|
||||
func (p *Ping) checkConnection(ip *net.IPAddr) (recv int, totalDelay time.Duration) {
|
||||
func (p *Ping) checkConnection(ip *net.IPAddr) (recv int, totalDelay time.Duration, colo string) {
|
||||
if Httping {
|
||||
recv, totalDelay = p.httping(ip)
|
||||
recv, totalDelay, colo = p.httping(ip)
|
||||
return
|
||||
}
|
||||
colo = "" // TCPing 不获取 colo
|
||||
for i := 0; i < PingTimes; i++ {
|
||||
if ok, delay := p.tcping(ip); ok {
|
||||
recv++
|
||||
@@ -128,7 +129,7 @@ func (p *Ping) appendIPData(data *utils.PingData) {
|
||||
|
||||
// handle tcping
|
||||
func (p *Ping) tcpingHandler(ip *net.IPAddr) {
|
||||
recv, totalDlay := p.checkConnection(ip)
|
||||
recv, totalDlay, colo := p.checkConnection(ip)
|
||||
nowAble := len(p.csv)
|
||||
if recv != 0 {
|
||||
nowAble++
|
||||
@@ -142,6 +143,7 @@ func (p *Ping) tcpingHandler(ip *net.IPAddr) {
|
||||
Sended: PingTimes,
|
||||
Received: recv,
|
||||
Delay: totalDlay / time.Duration(recv),
|
||||
Colo: colo,
|
||||
}
|
||||
p.appendIPData(data)
|
||||
}
|
||||
|
||||
24
utils/csv.go
24
utils/csv.go
@@ -23,6 +23,7 @@ var (
|
||||
InputMaxLossRate = maxLossRate
|
||||
Output = defaultOutput
|
||||
PrintNum = 10
|
||||
Debug = false // 是否开启调试模式
|
||||
)
|
||||
|
||||
// 是否打印测试结果
|
||||
@@ -40,6 +41,7 @@ type PingData struct {
|
||||
Sended int
|
||||
Received int
|
||||
Delay time.Duration
|
||||
Colo string
|
||||
}
|
||||
|
||||
type CloudflareIPData struct {
|
||||
@@ -58,13 +60,19 @@ func (cf *CloudflareIPData) getLossRate() float32 {
|
||||
}
|
||||
|
||||
func (cf *CloudflareIPData) toString() []string {
|
||||
result := make([]string, 6)
|
||||
result := make([]string, 7)
|
||||
result[0] = cf.IP.String()
|
||||
result[1] = strconv.Itoa(cf.Sended)
|
||||
result[2] = strconv.Itoa(cf.Received)
|
||||
result[3] = strconv.FormatFloat(float64(cf.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
|
||||
}
|
||||
|
||||
@@ -79,7 +87,7 @@ func ExportCsv(data []CloudflareIPData) {
|
||||
}
|
||||
defer fp.Close()
|
||||
w := csv.NewWriter(fp) //创建一个新的写入文件流
|
||||
_ = w.Write([]string{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)"})
|
||||
_ = w.Write([]string{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度(MB/s)", "地区码"})
|
||||
_ = w.WriteAll(convertToString(data))
|
||||
w.Flush()
|
||||
}
|
||||
@@ -168,18 +176,18 @@ func (s DownloadSpeedSet) Print() {
|
||||
if len(dateString) < PrintNum { // 如果IP数组长度(IP数量) 小于 打印次数,则次数改为IP数量
|
||||
PrintNum = len(dateString)
|
||||
}
|
||||
headFormat := "%-16s%-5s%-5s%-5s%-6s%-11s\n"
|
||||
dataFormat := "%-18s%-8s%-8s%-8s%-10s%-15s\n"
|
||||
headFormat := "\033[34m%-16s%-5s%-5s%-5s%-6s%-12s%-5s\033[0m\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%-11s\n"
|
||||
dataFormat = "%-42s%-8s%-8s%-8s%-10s%-15s\n"
|
||||
headFormat = "\033[34m%-40s%-5s%-5s%-5s%-6s%-12s%-5s\033[0m\n"
|
||||
dataFormat = "%-42s%-8s%-8s%-8s%-10s%-16s%-8s\n"
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Printf(headFormat, "IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)")
|
||||
fmt.Printf(headFormat, "IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度(MB/s)", "地区码")
|
||||
for i := 0; i < PrintNum; i++ {
|
||||
fmt.Printf(dataFormat, dateString[i][0], dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5])
|
||||
fmt.Printf(dataFormat, dateString[i][0], dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5], dateString[i][6])
|
||||
}
|
||||
if !noOutput() {
|
||||
fmt.Printf("\n完整测速结果已写入 %v 文件,可使用记事本/表格软件查看。\n", Output)
|
||||
|
||||
Reference in New Issue
Block a user