rebuild download
This commit is contained in:
parent
4d64abb94d
commit
f1a9b5c966
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"CloudflareSpeedTest/task"
|
||||
"bufio"
|
||||
"log"
|
||||
"net"
|
||||
|
@ -65,7 +66,7 @@ func loadFirstIPOfRangeFromFile(ipFile string) []net.IPAddr {
|
|||
for scanner.Scan() {
|
||||
IPString := scanner.Text()
|
||||
if !strings.Contains(IPString, "/") { // 如果不含有 / 则代表不是 IP 段,而是一个单独的 IP,因此需要加上 /32 /128 子网掩码
|
||||
if ipv6Mode {
|
||||
if task.IPv6 {
|
||||
IPString += "/128"
|
||||
} else {
|
||||
IPString += "/32"
|
||||
|
@ -77,12 +78,12 @@ func loadFirstIPOfRangeFromFile(ipFile string) []net.IPAddr {
|
|||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !ipv6Mode { // IPv4
|
||||
if !task.IPv6 { // IPv4
|
||||
minIP, maxIP := getCidrIPRange(IPString) // 获取 IP 最后一段最小值和最大值
|
||||
Mask, _ := strconv.Atoi(strings.Split(IPString, "/")[1]) // 获取子网掩码
|
||||
MaxIPNum := getCidrHostNum(Mask) // 根据子网掩码获取主机数量
|
||||
for IPRange.Contains(firstIP) {
|
||||
if allip { // 如果是测速全部 IP
|
||||
if task.TestAll { // 如果是测速全部 IP
|
||||
for i := minIP; i <= maxIP; i++ { // 遍历 IP 最后一段最小值到最大值
|
||||
firstIP[15] = i
|
||||
firstIPCopy := make([]byte, len(firstIP))
|
||||
|
|
252
main.go
252
main.go
|
@ -7,20 +7,14 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"CloudflareSpeedTest/task"
|
||||
|
||||
"github.com/cheggaaa/pb/v3"
|
||||
"CloudflareSpeedTest/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
version, ipFile, outputFile, versionNew string
|
||||
disableDownload, ipv6Mode, allip bool
|
||||
tcpPort, printResultNum, downloadSecond int
|
||||
timeLimit, timeLimitLow, speedLimit float64
|
||||
version, versionNew string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -67,27 +61,23 @@ https://github.com/XIU2/CloudflareSpeedTest
|
|||
打印帮助说明
|
||||
`
|
||||
|
||||
flag.IntVar(&pingRoutine, "n", 200, "测速线程数量")
|
||||
flag.IntVar(&pingTime, "t", 4, "延迟测速次数")
|
||||
flag.IntVar(&tcpPort, "tp", 443, "延迟测速端口")
|
||||
flag.IntVar(&downloadTestCount, "dn", 20, "下载测速数量")
|
||||
flag.IntVar(&downloadSecond, "dt", 10, "下载测速时间")
|
||||
flag.StringVar(&url, "url", "https://cf.xiu2.xyz/Github/CloudflareSpeedTest.png", "下载测速地址")
|
||||
flag.Float64Var(&timeLimit, "tl", 9999, "平均延迟上限")
|
||||
flag.Float64Var(&timeLimitLow, "tll", 0, "平均延迟下限")
|
||||
flag.Float64Var(&speedLimit, "sl", 0, "下载速度下限")
|
||||
flag.IntVar(&printResultNum, "p", 20, "显示结果数量")
|
||||
flag.BoolVar(&disableDownload, "dd", false, "禁用下载测速")
|
||||
flag.BoolVar(&ipv6Mode, "ipv6", false, "启用IPv6")
|
||||
flag.BoolVar(&allip, "allip", false, "测速全部 IP")
|
||||
flag.StringVar(&ipFile, "f", "ip.txt", "IP 数据文件")
|
||||
flag.StringVar(&outputFile, "o", "result.csv", "输出结果文件")
|
||||
flag.IntVar(&task.Routines, "n", 200, "测速线程数量")
|
||||
flag.IntVar(&task.PingTimes, "t", 4, "延迟测速次数")
|
||||
flag.IntVar(&task.TCPPort, "tp", 443, "延迟测速端口")
|
||||
flag.DurationVar(&utils.InputMaxDelay, "tl", 9999*time.Millisecond, "平均延迟上限")
|
||||
flag.DurationVar(&utils.InputMinDelay, "tll", time.Duration(0), "平均延迟下限")
|
||||
flag.DurationVar(&task.Timeout, "dt", 10*time.Second, "下载测速时间")
|
||||
flag.IntVar(&task.TestCount, "dn", 20, "下载测速数量")
|
||||
flag.StringVar(&task.URL, "url", "https://cf.xiu2.xyz/Github/CloudflareSpeedTest.png", "下载测速地址")
|
||||
flag.BoolVar(&task.Disable, "dd", false, "禁用下载测速")
|
||||
flag.BoolVar(&task.IPv6, "ipv6", false, "启用IPv6")
|
||||
flag.BoolVar(&task.TestAll, "allip", false, "测速全部 IP")
|
||||
flag.StringVar(&task.IPFile, "f", "ip.txt", "IP 数据文件")
|
||||
flag.Float64Var(&task.MinSpeed, "sl", 0, "下载速度下限")
|
||||
flag.IntVar(&utils.PrintNum, "p", 20, "显示结果数量")
|
||||
flag.StringVar(&utils.Output, "o", "result.csv", "输出结果文件")
|
||||
flag.BoolVar(&printVersion, "v", false, "打印程序版本")
|
||||
|
||||
task.TCPPort = tcpPort
|
||||
task.IPv6 = ipv6Mode
|
||||
task.DefaultRoutine = pingRoutine
|
||||
|
||||
flag.Usage = func() { fmt.Print(help) }
|
||||
flag.Parse()
|
||||
if printVersion {
|
||||
|
@ -101,184 +91,80 @@ https://github.com/XIU2/CloudflareSpeedTest
|
|||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
if pingRoutine <= 0 {
|
||||
pingRoutine = 200
|
||||
}
|
||||
if pingTime <= 0 {
|
||||
pingTime = 4
|
||||
}
|
||||
if tcpPort < 1 || tcpPort > 65535 {
|
||||
tcpPort = 443
|
||||
}
|
||||
if downloadTestCount <= 0 {
|
||||
downloadTestCount = 20
|
||||
}
|
||||
if downloadSecond <= 0 {
|
||||
downloadSecond = 10
|
||||
}
|
||||
if url == "" {
|
||||
url = "https://cf.xiu2.xyz/Github/CloudflareSpeedTest.png"
|
||||
}
|
||||
if timeLimit <= 0 || timeLimit > 9999 {
|
||||
timeLimit = 9999
|
||||
}
|
||||
if timeLimitLow < 0 || timeLimitLow > 9999 {
|
||||
timeLimitLow = 0
|
||||
}
|
||||
if speedLimit < 0 {
|
||||
speedLimit = 0
|
||||
}
|
||||
if printResultNum < 0 {
|
||||
printResultNum = 20
|
||||
}
|
||||
if ipFile == "" {
|
||||
ipFile = "ip.txt"
|
||||
}
|
||||
if outputFile == " " {
|
||||
outputFile = ""
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
go checkUpdate() // 检查版本更新
|
||||
initRandSeed() // 置随机数种子
|
||||
failTime = pingTime // 设置接收次数
|
||||
ips := loadFirstIPOfRangeFromFile(ipFile) // 读入IP
|
||||
pingCount := len(ips) * pingTime // 计算进度条总数(IP*测试次数)
|
||||
bar := pb.Simple.Start(pingCount) // 进度条总数
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
data = make([]CloudflareIPData, 0)
|
||||
data2 = make([]CloudflareIPData, 0)
|
||||
)
|
||||
downloadTestTime = time.Duration(downloadSecond) * time.Second
|
||||
ips := loadFirstIPOfRangeFromFile(task.IPFile) // 读入IP
|
||||
|
||||
// 开始延迟测速
|
||||
fmt.Printf("# XIU2/CloudflareSpeedTest %s \n", version)
|
||||
ipVersion := "IPv4"
|
||||
if ipv6Mode { // IPv6 模式判断
|
||||
if task.IPv6 { // IPv6 模式判断
|
||||
ipVersion = "IPv6"
|
||||
}
|
||||
fmt.Printf("开始延迟测速(模式:TCP %s,端口:%d ,平均延迟上限:%.2f ms,平均延迟下限:%.2f ms):\n", ipVersion, tcpPort, timeLimit, timeLimitLow)
|
||||
fmt.Printf("开始延迟测速(模式:TCP %s,端口:%d ,平均延迟上限:%v,平均延迟下限:%v)\n", ipVersion, task.TCPPort, utils.InputMaxDelay, utils.InputMinDelay)
|
||||
|
||||
// ping := task.NewPing(ips)
|
||||
// ping.Run()
|
||||
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)
|
||||
}
|
||||
wg.Wait()
|
||||
bar.Finish()
|
||||
// data := ping.Data()
|
||||
// sort.Sort(utils.CloudflareIPDataSet(data))
|
||||
sort.Sort(CloudflareIPDataSet(data)) // 排序(按延迟,从低到高,不同丢包率会分开单独按延迟和丢包率排序)
|
||||
|
||||
// 延迟测速完毕后,以 [平均延迟上限] + [平均延迟下限] 条件过滤结果
|
||||
if timeLimit != 9999 || timeLimitLow != 0 {
|
||||
for i := 0; i < len(data); i++ {
|
||||
if float64(data[i].pingTime) > timeLimit { // 平均延迟上限
|
||||
break
|
||||
}
|
||||
if float64(data[i].pingTime) <= timeLimitLow { // 平均延迟下限
|
||||
continue
|
||||
}
|
||||
data2 = append(data2, data[i]) // 延迟满足条件时,添加到新数组中
|
||||
}
|
||||
data = data2
|
||||
data2 = []CloudflareIPData{}
|
||||
}
|
||||
|
||||
// 开始下载测速
|
||||
if !disableDownload { // 如果禁用下载测速就跳过
|
||||
testDownloadSpeed(data, data2, bar)
|
||||
}
|
||||
|
||||
if len(data2) > 0 { // 如果该数组有内容,说明指定了 [下载测速下限] 条件,且最少有 1 个满足条件的 IP
|
||||
data = data2
|
||||
}
|
||||
sort.Sort(CloudflareIPDataSetD(data)) // 排序(按下载速度,从高到低)
|
||||
if outputFile != "" {
|
||||
ExportCsv(outputFile, data) // 输出结果到文件
|
||||
}
|
||||
printResult(data) // 显示最快结果
|
||||
}
|
||||
|
||||
func testDownloadSpeed(data, data2 []CloudflareIPData, bar *pb.ProgressBar) {
|
||||
if len(data) <= 0 { // IP数组长度(IP数量) 大于 0 时才会继续下载测速
|
||||
fmt.Println("\n[信息] 延迟测速结果 IP 数量为 0,跳过下载测速。")
|
||||
return
|
||||
}
|
||||
if len(data) < downloadTestCount { // 如果IP数组长度(IP数量) 小于下载测速数量(-dn),则次数修正为IP数
|
||||
downloadTestCount = len(data)
|
||||
}
|
||||
// 临时的下载测速次数,即实际的下载测速数量
|
||||
downloadTestCount2 := downloadTestCount
|
||||
// 如果没有指定 [下载速度下限] 条件,则临时变量为下载测速数量(-dn)
|
||||
if speedLimit > 0 {
|
||||
// 如果指定了 [下载速度下限] 条件,则临时变量改为总数量(即一直测速下去,直到凑够下载测速数量 -dn)
|
||||
downloadTestCount2 = len(data)
|
||||
}
|
||||
fmt.Printf("开始下载测速(下载速度下限:%.2f MB/s,下载测速数量:%d,下载测速队列:%d):\n", speedLimit, downloadTestCount, downloadTestCount2)
|
||||
bar = pb.Simple.Start(downloadTestCount)
|
||||
for i := 0; i < downloadTestCount2; i++ {
|
||||
_, speed := DownloadSpeedHandler(data[i].ip)
|
||||
data[i].downloadSpeed = speed
|
||||
// 在每个 IP 下载测速后,以 [下载速度下限] 条件过滤结果
|
||||
if float64(speed) >= speedLimit*1024*1024 {
|
||||
data2 = append(data2, data[i]) // 高于下载速度下限时,添加到新数组中
|
||||
bar.Add(1)
|
||||
if len(data2) == downloadTestCount { // 凑够满足条件的 IP 时(下载测速数量 -dn),就跳出循环
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
bar.Finish()
|
||||
}
|
||||
|
||||
// 显示最快结果
|
||||
func printResult(data []CloudflareIPData) {
|
||||
sysType := runtime.GOOS
|
||||
if printResultNum <= 0 { // 如果禁止直接输出结果就跳过
|
||||
fmt.Printf("完整测速结果已写入 %v 文件,请使用记事本/表格软件查看。\n", outputFile)
|
||||
return
|
||||
}
|
||||
dateString := convertToString(data) // 转为多维数组 [][]String
|
||||
if len(dateString) <= 0 { // IP数组长度(IP数量) 大于 0 时继续
|
||||
fmt.Println("\n[信息] 完整测速结果 IP 数量为 0,跳过输出结果。")
|
||||
return
|
||||
}
|
||||
if len(dateString) < printResultNum { // 如果IP数组长度(IP数量) 小于 打印次数,则次数改为IP数量
|
||||
printResultNum = len(dateString)
|
||||
}
|
||||
resHeader := []interface{}{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)"}
|
||||
if ipv6Mode { // IPv6 太长了,所以需要调整一下间隔
|
||||
fmt.Printf("%-40s%-5s%-5s%-5s%-6s%-11s\n", resHeader...)
|
||||
for i := 0; i < printResultNum; i++ {
|
||||
fmt.Printf("%-42s%-8s%-8s%-8s%-10s%-15s\n", ipPadding(dateString[i][0]), dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5])
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("%-16s%-5s%-5s%-5s%-6s%-11s\n", resHeader...)
|
||||
for i := 0; i < printResultNum; i++ {
|
||||
fmt.Printf("%-18s%-8s%-8s%-8s%-15s%-15s\n", ipPadding(dateString[i][0]), dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5])
|
||||
}
|
||||
}
|
||||
pingData := task.NewPing(ips).Run().FilterDelay()
|
||||
speedData := task.TestDownloadSpeed(pingData)
|
||||
utils.ExportCsv(speedData)
|
||||
speedData.Print(task.IPv6)
|
||||
|
||||
if versionNew != "" {
|
||||
fmt.Printf("\n*** 发现新版本 [%s]!请前往 [https://github.com/XIU2/CloudflareSpeedTest] 更新! ***\n", versionNew)
|
||||
}
|
||||
|
||||
if outputFile != "" {
|
||||
fmt.Printf("完整测速结果已写入 %v 文件,请使用记事本/表格软件查看。\n", outputFile)
|
||||
if utils.Output != "" {
|
||||
fmt.Printf("完整测速结果已写入 %v 文件,请使用记事本/表格软件查看。\n", utils.Output)
|
||||
}
|
||||
if sysType == "windows" { // 如果是 Windows 系统,则需要按下 回车键 或 Ctrl+C 退出(避免通过双击运行时,测速完毕后直接关闭)
|
||||
if runtime.GOOS == "windows" { // 如果是 Windows 系统,则需要按下 回车键 或 Ctrl+C 退出(避免通过双击运行时,测速完毕后直接关闭)
|
||||
fmt.Printf("按下 回车键 或 Ctrl+C 退出。\n")
|
||||
var pause int
|
||||
fmt.Scanln(&pause)
|
||||
}
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// wg.Wait()
|
||||
// bar.Finish()
|
||||
// data := ping.Data()
|
||||
// sort.Sort(utils.CloudflareIPDataSet(data))
|
||||
// sort.Sort(CloudflareIPDataSet(data)) // 排序(按延迟,从低到高,不同丢包率会分开单独按延迟和丢包率排序)
|
||||
|
||||
// 延迟测速完毕后,以 [平均延迟上限] + [平均延迟下限] 条件过滤结果
|
||||
// if timeLimit != 9999 || timeLimitLow != 0 {
|
||||
// for i := 0; i < len(data); i++ {
|
||||
// if float64(data[i].pingTime) > timeLimit { // 平均延迟上限
|
||||
// break
|
||||
// }
|
||||
// if float64(data[i].pingTime) <= timeLimitLow { // 平均延迟下限
|
||||
// continue
|
||||
// }
|
||||
// data2 = append(data2, data[i]) // 延迟满足条件时,添加到新数组中
|
||||
// }
|
||||
// data = data2
|
||||
// data2 = []CloudflareIPData{}
|
||||
// }
|
||||
|
||||
// 开始下载测速
|
||||
// if !disableDownload { // 如果禁用下载测速就跳过
|
||||
// testDownloadSpeed(data, data2, bar)
|
||||
// }
|
||||
|
||||
// if len(data2) > 0 { // 如果该数组有内容,说明指定了 [下载测速下限] 条件,且最少有 1 个满足条件的 IP
|
||||
// data = data2
|
||||
// }
|
||||
// sort.Sort(CloudflareIPDataSetD(data)) // 排序(按下载速度,从高到低)
|
||||
// if outputFile != "" {
|
||||
// ExportCsv(outputFile, data) // 输出结果到文件
|
||||
// }
|
||||
// printResult(data) // 显示最快结果
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"CloudflareSpeedTest/utils"
|
||||
|
||||
"github.com/VividCortex/ewma"
|
||||
)
|
||||
|
||||
const (
|
||||
bufferSize = 1024
|
||||
defaultURL = "https://cf.xiu2.xyz/Github/CloudflareSpeedTest.png"
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultDisableDownlaod = false
|
||||
defaultTestNum = 20
|
||||
defaultMinSpeed float64 = 0.0
|
||||
)
|
||||
|
||||
var (
|
||||
// download test url
|
||||
URL = defaultURL
|
||||
// download timeout
|
||||
Timeout = defaultTimeout
|
||||
// disable download
|
||||
Disable = defaultDisableDownlaod
|
||||
|
||||
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) (sppedSet utils.DownloadSpeedSet) {
|
||||
checkDownloadDefault()
|
||||
if Disable {
|
||||
return utils.DownloadSpeedSet(ipSet)
|
||||
}
|
||||
if len(ipSet) <= 0 { // IP数组长度(IP数量) 大于 0 时才会继续下载测速
|
||||
fmt.Println("\n[信息] 延迟测速结果 IP 数量为 0,跳过下载测速。")
|
||||
return
|
||||
}
|
||||
testNum := TestCount
|
||||
if len(ipSet) < TestCount || MinSpeed > 0 { // 如果IP数组长度(IP数量) 小于下载测速数量(-dn),则次数修正为IP数
|
||||
testNum = len(ipSet)
|
||||
}
|
||||
|
||||
fmt.Printf("开始下载测速(下载速度下限:%.2f MB/s,下载测速数量:%d,下载测速队列:%d):\n", MinSpeed, TestCount, testNum)
|
||||
bar := utils.NewBar(TestCount)
|
||||
for i := 0; i < testNum; i++ {
|
||||
speed := downloadSpeedHandler(&ipSet[i].IP)
|
||||
ipSet[i].DownloadSpeed = speed
|
||||
// 在每个 IP 下载测速后,以 [下载速度下限] 条件过滤结果
|
||||
if speed >= MinSpeed*1024*1024 {
|
||||
sppedSet = append(sppedSet, ipSet[i]) // 高于下载速度下限时,添加到新数组中
|
||||
bar.Grow(1)
|
||||
if len(sppedSet) == TestCount { // 凑够满足条件的 IP 时(下载测速数量 -dn),就跳出循环
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
bar.Done()
|
||||
// 按速度排序
|
||||
sort.Sort(sppedSet)
|
||||
return
|
||||
}
|
||||
|
||||
func getDialContext(ip *net.IPAddr) func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
fakeSourceAddr := ip.String() + ":443"
|
||||
if IPv6 { // IPv6 需要加上 []
|
||||
fakeSourceAddr = "[" + ip.String() + "]:443"
|
||||
}
|
||||
return func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return (&net.Dialer{}).DialContext(ctx, network, fakeSourceAddr)
|
||||
}
|
||||
}
|
||||
|
||||
//bool : can download,float32 downloadSpeed
|
||||
func downloadSpeedHandler(ip *net.IPAddr) float64 {
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{DialContext: getDialContext(ip)},
|
||||
Timeout: Timeout,
|
||||
}
|
||||
response, err := client.Get(URL)
|
||||
if err != nil {
|
||||
return 0.0
|
||||
}
|
||||
defer response.Body.Close()
|
||||
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)
|
||||
contentRead += int64(bufferRead)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
break
|
||||
}
|
||||
e.Add(float64(contentRead-lastContentRead) / (float64(nextTime.Sub(currentTime)) / float64(timeSlice)))
|
||||
}
|
||||
}
|
||||
return e.Value() / (Timeout.Seconds() / 120)
|
||||
|
||||
}
|
|
@ -3,4 +3,8 @@ package task
|
|||
var (
|
||||
// IPv6 IP version is 6
|
||||
IPv6 = false
|
||||
// TestAll test all ip
|
||||
TestAll = false
|
||||
// IPFile is the filename of IP Rangs
|
||||
IPFile = "ip.txt"
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ package task
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -12,12 +13,15 @@ import (
|
|||
const (
|
||||
tcpConnectTimeout = time.Second * 1
|
||||
maxRoutine = 1000
|
||||
defaultRoutines = 200
|
||||
defaultPort = 443
|
||||
defaultPingTimes = 4
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultRoutine = 200
|
||||
TCPPort int = 443
|
||||
PingTimes int = 4
|
||||
Routines = defaultRoutines
|
||||
TCPPort int = defaultPort
|
||||
PingTimes int = defaultPingTimes
|
||||
)
|
||||
|
||||
type Ping struct {
|
||||
|
@ -29,14 +33,27 @@ type Ping struct {
|
|||
bar *utils.Bar
|
||||
}
|
||||
|
||||
func checkPingDefault() {
|
||||
if Routines <= 0 {
|
||||
Routines = defaultRoutines
|
||||
}
|
||||
if TCPPort <= 0 || TCPPort >= 65535 {
|
||||
TCPPort = defaultPort
|
||||
}
|
||||
if PingTimes <= 0 {
|
||||
PingTimes = defaultPingTimes
|
||||
}
|
||||
}
|
||||
|
||||
func NewPing(ips []net.IPAddr) *Ping {
|
||||
checkPingDefault()
|
||||
return &Ping{
|
||||
wg: &sync.WaitGroup{},
|
||||
m: &sync.Mutex{},
|
||||
ips: ips,
|
||||
csv: make(utils.PingDelaySet, 0),
|
||||
control: make(chan bool, DefaultRoutine),
|
||||
bar: utils.NewBar(len(ips) * PingTimes),
|
||||
control: make(chan bool, Routines),
|
||||
bar: utils.NewBar(len(ips)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +65,7 @@ func (p *Ping) Run() utils.PingDelaySet {
|
|||
}
|
||||
p.wg.Wait()
|
||||
p.bar.Done()
|
||||
sort.Sort(p.csv)
|
||||
return p.csv
|
||||
}
|
||||
|
||||
|
@ -112,7 +130,7 @@ func (p *Ping) tcpingHandler(ip net.IPAddr) {
|
|||
break
|
||||
}
|
||||
}
|
||||
p.bar.Grow(PingTimes)
|
||||
p.bar.Grow(1)
|
||||
if !ipCanConnect {
|
||||
return
|
||||
}
|
||||
|
@ -126,7 +144,7 @@ func (p *Ping) tcpingHandler(ip net.IPAddr) {
|
|||
// }
|
||||
data := &utils.PingData{
|
||||
IP: ip,
|
||||
Sended: PingTimes,
|
||||
Sended: PingTimes,
|
||||
Received: pingRecv,
|
||||
Delay: delay / time.Duration(pingRecv),
|
||||
}
|
||||
|
|
167
tcping.go
167
tcping.go
|
@ -1,167 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/VividCortex/ewma"
|
||||
)
|
||||
|
||||
//bool connectionSucceed float32 time
|
||||
func tcping(ip net.IPAddr, tcpPort int) (bool, time.Duration) {
|
||||
startTime := time.Now()
|
||||
fullAddress := fmt.Sprintf("%s:%d", ip.String(), tcpPort)
|
||||
//fmt.Println(ip.String())
|
||||
if ipv6Mode { // IPv6 需要加上 []
|
||||
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 checkConnection(ip net.IPAddr, tcpPort int) (pingRecv int, pingTime time.Duration) {
|
||||
for i := 1; i <= failTime; i++ {
|
||||
pingSucceed, pingTimeCurrent := tcping(ip, tcpPort)
|
||||
if pingSucceed {
|
||||
pingRecv++
|
||||
pingTime += pingTimeCurrent
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//return Success packetRecv averagePingTime specificIPAddr
|
||||
func tcpingHandler(ip net.IPAddr, tcpPort, pingCount int, progressHandler func(e progressEvent)) (bool, int, time.Duration, net.IPAddr) {
|
||||
ipCanConnect := false
|
||||
pingRecv := 0
|
||||
var pingTime time.Duration
|
||||
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(NoAvailableIPFound)
|
||||
return false, 0, 0, net.IPAddr{}
|
||||
}
|
||||
progressHandler(AvailableIPFound)
|
||||
for i := failTime; i < pingCount; i++ {
|
||||
fmt.Println("failTime", failTime)
|
||||
pingSuccess, pingTimeCurrent := tcping(ip, tcpPort)
|
||||
progressHandler(NormalPing)
|
||||
if pingSuccess {
|
||||
pingRecv++
|
||||
pingTime += pingTimeCurrent
|
||||
}
|
||||
}
|
||||
return true, pingRecv, pingTime / time.Duration(pingRecv), ip
|
||||
}
|
||||
|
||||
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()
|
||||
// fmt.Println(ip.String())
|
||||
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: downloadTestTime,
|
||||
}
|
||||
var fullAddress string
|
||||
if ipv6Mode { // IPv6 需要加上 []
|
||||
fullAddress = "[" + ip.String() + "]:443"
|
||||
} else {
|
||||
fullAddress = ip.String() + ":443"
|
||||
}
|
||||
client.Transport = &http.Transport{
|
||||
DialContext: GetDialContextByAddr(fullAddress),
|
||||
}
|
||||
response, err := client.Get(url)
|
||||
if err != nil {
|
||||
return false, 0
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return false, 0
|
||||
}
|
||||
timeStart := time.Now()
|
||||
timeEnd := timeStart.Add(downloadTestTime)
|
||||
|
||||
contentLength := response.ContentLength
|
||||
buffer := make([]byte, downloadBufferSize)
|
||||
|
||||
var (
|
||||
contentRead int64 = 0
|
||||
timeSlice = downloadTestTime / 100
|
||||
timeCounter = 1
|
||||
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++
|
||||
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
|
||||
}
|
||||
e.Add(float64(contentRead-lastContentRead) / (float64(nextTime.Sub(currentTime)) / float64(timeSlice)))
|
||||
}
|
||||
}
|
||||
return true, float32(e.Value()) / (float32(downloadTestTime.Seconds()) / 120)
|
||||
|
||||
}
|
43
utils/csv.go
43
utils/csv.go
|
@ -2,20 +2,24 @@ package utils
|
|||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultOutput = "result.csv"
|
||||
|
||||
var (
|
||||
MaxDelay = 9999 * time.Millisecond
|
||||
MinDelay = time.Duration(0)
|
||||
|
||||
InputMaxDelay = MaxDelay
|
||||
InputMinDelay = MinDelay
|
||||
Output = defaultOutput
|
||||
PrintNum = 20
|
||||
)
|
||||
|
||||
type PingData struct {
|
||||
|
@ -28,7 +32,7 @@ type PingData struct {
|
|||
type CloudflareIPData struct {
|
||||
*PingData
|
||||
recvRate float32
|
||||
downloadSpeed float32
|
||||
DownloadSpeed float64
|
||||
}
|
||||
|
||||
func (cf *CloudflareIPData) getRecvRate() float32 {
|
||||
|
@ -46,14 +50,17 @@ func (cf *CloudflareIPData) toString() []string {
|
|||
result[2] = strconv.Itoa(cf.Received)
|
||||
result[3] = strconv.FormatFloat(float64(cf.getRecvRate()), 'f', 2, 32)
|
||||
result[4] = cf.Delay.String()
|
||||
result[5] = strconv.FormatFloat(float64(cf.downloadSpeed)/1024/1024, 'f', 2, 32)
|
||||
result[5] = strconv.FormatFloat(cf.DownloadSpeed/1024/1024, 'f', 2, 32)
|
||||
return result
|
||||
}
|
||||
|
||||
func ExportCsv(filePath string, data []CloudflareIPData) {
|
||||
fp, err := os.Create(filePath)
|
||||
func ExportCsv(data []CloudflareIPData) {
|
||||
if Output == "" {
|
||||
Output = defaultOutput
|
||||
}
|
||||
fp, err := os.Create(Output)
|
||||
if err != nil {
|
||||
log.Fatalf("创建文件[%s]失败:%v", filePath, err)
|
||||
log.Fatalf("创建文件[%s]失败:%v", Output, err)
|
||||
return
|
||||
}
|
||||
defer fp.Close()
|
||||
|
@ -74,7 +81,6 @@ func convertToString(data []CloudflareIPData) [][]string {
|
|||
type PingDelaySet []CloudflareIPData
|
||||
|
||||
func (s PingDelaySet) FilterDelay() (data PingDelaySet) {
|
||||
sort.Sort(s)
|
||||
if InputMaxDelay >= MaxDelay || InputMinDelay <= MinDelay {
|
||||
return s
|
||||
}
|
||||
|
@ -114,9 +120,30 @@ func (s DownloadSpeedSet) Len() int {
|
|||
}
|
||||
|
||||
func (s DownloadSpeedSet) Less(i, j int) bool {
|
||||
return s[i].downloadSpeed > s[j].downloadSpeed
|
||||
return s[i].DownloadSpeed > s[j].DownloadSpeed
|
||||
}
|
||||
|
||||
func (s DownloadSpeedSet) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func (s DownloadSpeedSet) Print(ipv6 bool) {
|
||||
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%-11s\n"
|
||||
dataFormat := "%-18s%-8s%-8s%-8s%-15s%-15s\n"
|
||||
if ipv6 { // IPv6 太长了,所以需要调整一下间隔
|
||||
headFormat = "%-40s%-5s%-5s%-5s%-6s%-11s\n"
|
||||
dataFormat = "%-42s%-8s%-8s%-8s%-10s%-15s\n"
|
||||
}
|
||||
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])
|
||||
}
|
||||
}
|
|
@ -2,14 +2,6 @@ package utils
|
|||
|
||||
import "github.com/cheggaaa/pb/v3"
|
||||
|
||||
type ProgressEvent int
|
||||
|
||||
const (
|
||||
NoAvailableIPFound ProgressEvent = iota
|
||||
AvailableIPFound
|
||||
NormalPing
|
||||
)
|
||||
|
||||
type Bar struct {
|
||||
pb *pb.ProgressBar
|
||||
}
|
||||
|
@ -24,17 +16,4 @@ func (b *Bar) Grow(num int) {
|
|||
|
||||
func (b *Bar) Done() {
|
||||
b.pb.Finish()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue