最后一板自己写的
This commit is contained in:
parent
859251ab3f
commit
b2e862d651
5
go.mod
5
go.mod
@ -2,7 +2,10 @@ module DockerST
|
||||
|
||||
go 1.22
|
||||
|
||||
require github.com/schollz/progressbar/v3 v3.14.4
|
||||
require (
|
||||
github.com/VividCortex/ewma v1.2.0
|
||||
github.com/schollz/progressbar/v3 v3.14.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
|
2
go.sum
2
go.sum
@ -1,3 +1,5 @@
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
24
main.go
24
main.go
@ -9,7 +9,6 @@ import (
|
||||
var (
|
||||
VersionPrint bool
|
||||
Version string
|
||||
DDownload bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -21,9 +20,10 @@ func init() {
|
||||
flag.IntVar(&task.MinMS, "mis", 0, "只输出高于指定平均延迟的 IP")
|
||||
flag.IntVar(&task.MaxMS, "mxs", 1000, "只输出低于指定平均延迟的 IP")
|
||||
flag.IntVar(&task.DownloadNum, "dn", 10, "下载数量")
|
||||
flag.StringVar(&task.DownloadUrl, "url", "https://github.com", "默认文件下载地址")
|
||||
flag.StringVar(&task.URL, "url", "https://cf.xiu2.xyz/url", "默认文件下载地址")
|
||||
flag.Float64Var(&task.MinSpeed, "md", 0, "最低下载速度")
|
||||
flag.BoolVar(&task.TestAll, "ta", false, "测试所有 IP")
|
||||
flag.BoolVar(&DDownload, "du", false, "禁止下载")
|
||||
flag.BoolVar(&task.Disable, "dd", true, "禁止下载")
|
||||
flag.BoolVar(&VersionPrint, "v", false, "输出版本")
|
||||
flag.BoolVar(&task.IsOff, "om", false, "不下载子网列表")
|
||||
flag.Parse()
|
||||
@ -40,18 +40,12 @@ func main() {
|
||||
task.InitRandSeed()
|
||||
// 输出版本
|
||||
fmt.Printf("# DockerST %s \n", Version)
|
||||
pingData := task.CreateData().Run().ExcludeInvalid()
|
||||
// 按照延迟排序
|
||||
sortedPingData := task.SortNodesDesc(pingData)
|
||||
// 仅输出前10个结果
|
||||
for i, ipDelay := range sortedPingData {
|
||||
if i >= 10 {
|
||||
break
|
||||
pingData := task.CreateData().Run().ExcludeInvalid().SortNodesDesc()
|
||||
DownloadData := task.TestDownloadSpeed(pingData)
|
||||
for a, v := range DownloadData {
|
||||
if a == 10 {
|
||||
return
|
||||
}
|
||||
fmt.Printf("IP: %s, 延迟: %s\n", ipDelay.IP.String(), ipDelay.Delay)
|
||||
}
|
||||
|
||||
if DDownload {
|
||||
|
||||
fmt.Printf("IP: %s, 延迟: %v, 下载速度: %.2f MB/s\n", v.IP.String(), v.Delay, v.DownloadSpeed)
|
||||
}
|
||||
}
|
||||
|
178
task/download.go
178
task/download.go
@ -1,6 +1,178 @@
|
||||
package task
|
||||
|
||||
var (
|
||||
DownloadUrl = ""
|
||||
DownloadNum int
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/VividCortex/ewma"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
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
|
||||
DownloadNum = defaultTestNum
|
||||
MinSpeed = defaultMinSpeed
|
||||
)
|
||||
|
||||
type BySpeed []IPDelay
|
||||
|
||||
func TestDownloadSpeed(p []IPDelay) []IPDelay {
|
||||
if Disable {
|
||||
return p
|
||||
}
|
||||
if len(p) <= 0 { // IP数组长度(IP数量) 大于 0 时才会继续下载测速
|
||||
fmt.Println("\n[信息] 延迟测速结果 IP 数量为 0,跳过下载测速。")
|
||||
return p
|
||||
}
|
||||
speedSet := make([]IPDelay, 0)
|
||||
testNum := DownloadNum
|
||||
if len(p) < DownloadNum || MinSpeed > 0 { // 如果IP数组长度(IP数量) 小于下载测速数量(-dn),则次数修正为IP数
|
||||
testNum = len(p)
|
||||
}
|
||||
if testNum < DownloadNum {
|
||||
DownloadNum = testNum
|
||||
}
|
||||
fmt.Printf("开始下载测速(下限:%.2f MB/s, 数量:%d, 队列:%d)\n", MinSpeed, DownloadNum, testNum)
|
||||
// 创建进度条
|
||||
bar := progressbar.NewOptions(len(p),
|
||||
progressbar.OptionSetWidth(50),
|
||||
progressbar.OptionSetDescription("下载测速"),
|
||||
progressbar.OptionShowCount(),
|
||||
progressbar.OptionShowIts(),
|
||||
)
|
||||
for i := 0; i < testNum; i++ {
|
||||
speed := downloadHandler(p[i].IP)
|
||||
p[i].DownloadSpeed = speed
|
||||
// 在每个 IP 下载测速后,以 [下载速度下限] 条件过滤结果
|
||||
if speed >= MinSpeed*1024*1024 {
|
||||
p[i].DownloadSpeed = speed
|
||||
err := bar.Add(1)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
p = p[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
err := bar.Finish()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(speedSet) == 0 { // 没有符合速度限制的数据,返回所有测试数据
|
||||
speedSet = p
|
||||
}
|
||||
// 按速度排序
|
||||
sort.Sort(BySpeed(speedSet))
|
||||
return speedSet
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// return download Speed
|
||||
func downloadHandler(ip *net.IPAddr) float64 {
|
||||
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 次
|
||||
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 {
|
||||
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
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
err = Body.Close()
|
||||
if err != nil {
|
||||
}
|
||||
}(response.Body)
|
||||
if response.StatusCode != 200 {
|
||||
return 0.0
|
||||
}
|
||||
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
|
||||
}
|
||||
// 获取上个时间片
|
||||
lastTimeSlice := timeStart.Add(timeSlice * time.Duration(timeCounter-1))
|
||||
// 下载数据量 / (用当前时间 - 上个时间片/ 时间片)
|
||||
e.Add(float64(contentRead-lastContentRead) / (float64(currentTime.Sub(lastTimeSlice)) / float64(timeSlice)))
|
||||
}
|
||||
contentRead += int64(bufferRead)
|
||||
}
|
||||
return e.Value() / (Timeout.Seconds() / 120)
|
||||
}
|
||||
func (a BySpeed) Len() int { return len(a) }
|
||||
func (a BySpeed) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a BySpeed) Less(i, j int) bool { return a[i].DownloadSpeed > a[j].DownloadSpeed } // 这里使用 > 使其降序排序
|
||||
|
11
task/ip.go
11
task/ip.go
@ -117,7 +117,7 @@ func GetIPv4List() []string {
|
||||
}
|
||||
|
||||
// ExcludeInvalid 排除不合格节点
|
||||
func (p *IPRangeList) ExcludeInvalid() []IPDelay {
|
||||
func (p *IPRangeList) ExcludeInvalid() *IPRangeList {
|
||||
// 初始化一个空IPDelay切片
|
||||
var delays []IPDelay
|
||||
// 遍历IPRangeList的Delays切片
|
||||
@ -133,13 +133,14 @@ func (p *IPRangeList) ExcludeInvalid() []IPDelay {
|
||||
// 将IPDelay的IP和Delay添加到delays切片中
|
||||
delays = append(delays, IPDelay{IP: p.Ips[ip], Delay: delay.Delay, DownloadSpeed: 0})
|
||||
}
|
||||
return delays
|
||||
p.Delays = delays
|
||||
return p
|
||||
}
|
||||
|
||||
// SortNodesDesc 按延迟降序排列
|
||||
func SortNodesDesc(p []IPDelay) []IPDelay {
|
||||
sorted := make([]IPDelay, len(p))
|
||||
_ = copy(sorted, p)
|
||||
func (p *IPRangeList) SortNodesDesc() []IPDelay {
|
||||
sorted := make([]IPDelay, len(p.Delays))
|
||||
_ = copy(sorted, p.Delays)
|
||||
// 使用sort.Slice 对切片进行排序
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return sorted[i].Delay < sorted[j].Delay
|
||||
|
@ -59,36 +59,23 @@ func (p *IPRangeList) Run() *IPRangeList {
|
||||
|
||||
// TCPing 通过TCP连接测试IP是否可用
|
||||
func TCPing(ip *net.IPAddr) (bool, time.Duration) {
|
||||
var totalDuration time.Duration
|
||||
var successCount int
|
||||
for i := 0; i < 4; i++ { // 重试4次
|
||||
func() {
|
||||
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
|
||||
}
|
||||
defer func() {
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
}
|
||||
}()
|
||||
duration := time.Since(startTime)
|
||||
totalDuration += duration
|
||||
successCount++
|
||||
}()
|
||||
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)
|
||||
}
|
||||
|
||||
if successCount == 0 { // 如果所有尝试都失败,返回 false 和 0
|
||||
conn, err := net.DialTimeout("tcp", fullAddress, TcpConnectTimeOut)
|
||||
if err != nil {
|
||||
return false, 0
|
||||
}
|
||||
defer func(conn net.Conn) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
|
||||
averageDuration := totalDuration / time.Duration(successCount) // 计算平均持续时间
|
||||
return true, averageDuration
|
||||
}
|
||||
}(conn)
|
||||
duration := time.Since(startTime)
|
||||
return true, duration
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user