diff --git a/.gitignore b/.gitignore index 7970fb16..f964d988 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ dist Releases *.exe *.csv +.vscode/ +*.bat +*.sh \ No newline at end of file diff --git a/main.go b/main.go index 9ae5b26a..225082b9 100644 --- a/main.go +++ b/main.go @@ -34,7 +34,8 @@ https://github.com/XIU2/CloudflareSpeedTest -dt 10 下载测速时间;单个 IP 下载测速最长时间,不能太短;(默认 10 秒) -tp 443 - 指定测速端口;延迟测速/下载测速时使用的端口;(默认 443 端口) + 指定所有测速端口;即延迟测速/下载测速时使用的端口;(默认 443 端口) + (会被-ip参数或IP段文件中指定的 ip/mask:port 覆盖) -url https://cf.xiu2.xyz/url 指定测速地址;延迟测速(HTTPing)/下载测速时使用的地址,默认地址不保证可用性,建议自建; @@ -67,6 +68,8 @@ https://github.com/XIU2/CloudflareSpeedTest 禁用下载测速;禁用后测速结果会按延迟排序 (默认按下载速度排序);(默认 启用) -allip 测速全部的IP;对 IP 段中的每个 IP (仅支持 IPv4) 进行测速;(默认 每个 /24 段随机测速一个 IP) + -onlyip + 命令行输出和结果文件仅输出 IP,不附带端口信息 -v 打印程序版本 + 检查版本更新 @@ -98,6 +101,7 @@ https://github.com/XIU2/CloudflareSpeedTest flag.BoolVar(&task.Disable, "dd", false, "禁用下载测速") flag.BoolVar(&task.TestAll, "allip", false, "测速全部 IP") + flag.BoolVar(&utils.OnlyIP, "onlyip", false, "结果仅输出 IP") flag.BoolVar(&printVersion, "v", false, "打印程序版本") flag.Usage = func() { fmt.Print(help) } diff --git a/task/download.go b/task/download.go index 26eac418..0adafad6 100644 --- a/task/download.go +++ b/task/download.go @@ -74,7 +74,7 @@ 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 := downloadHandler(ipSet[i].IP, ipSet[i].Port) ipSet[i].DownloadSpeed = speed // 在每个 IP 下载测速后,以 [下载速度下限] 条件过滤结果 if speed >= MinSpeed*1024*1024 { @@ -94,12 +94,12 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe return } -func getDialContext(ip *net.IPAddr) func(ctx context.Context, network, address string) (net.Conn, error) { +func getDialContext(ip *net.IPAddr, port int) 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) + fakeSourceAddr = fmt.Sprintf("%s:%d", ip.String(), port) } else { - fakeSourceAddr = fmt.Sprintf("[%s]:%d", ip.String(), TCPPort) + fakeSourceAddr = fmt.Sprintf("[%s]:%d", ip.String(), port) } return func(ctx context.Context, network, address string) (net.Conn, error) { return (&net.Dialer{}).DialContext(ctx, network, fakeSourceAddr) @@ -107,9 +107,9 @@ 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, port int) float64 { client := &http.Client{ - Transport: &http.Transport{DialContext: getDialContext(ip)}, + Transport: &http.Transport{DialContext: getDialContext(ip, port)}, Timeout: Timeout, CheckRedirect: func(req *http.Request, via []*http.Request) error { if len(via) > 10 { // 限制最多重定向 10 次 diff --git a/task/httping.go b/task/httping.go index 783a8830..e1ce789a 100644 --- a/task/httping.go +++ b/task/httping.go @@ -5,7 +5,6 @@ import ( //"fmt" "io" "log" - "net" "net/http" "regexp" "strings" @@ -22,11 +21,11 @@ var ( ) // pingReceived pingTotalTime -func (p *Ping) httping(ip *net.IPAddr) (int, time.Duration) { +func (p *Ping) httping(ip *dest) (int, time.Duration) { hc := http.Client{ Timeout: time.Second * 2, Transport: &http.Transport{ - DialContext: getDialContext(ip), + DialContext: getDialContext(ip.IPAddr, ip.Port), //TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过证书验证 }, CheckRedirect: func(req *http.Request, via []*http.Request) error { diff --git a/task/ip.go b/task/ip.go index 48f27158..768a451c 100644 --- a/task/ip.go +++ b/task/ip.go @@ -18,11 +18,12 @@ var ( TestAll = false // IPFile is the filename of IP Rangs IPFile = defaultInputFile + seed *rand.Rand IPText string ) func InitRandSeed() { - rand.Seed(time.Now().UnixNano()) + seed = rand.New(rand.NewSource(time.Now().UnixNano())) } func isIPv4(ip string) bool { @@ -33,19 +34,25 @@ func randIPEndWith(num byte) byte { if num == 0 { // 对于 /32 这种单独的 IP return byte(0) } - return byte(rand.Intn(int(num))) + return byte(seed.Intn(int(num))) } type IPRanges struct { - ips []*net.IPAddr + ips []*dest + port int mask string firstIP net.IP ipNet *net.IPNet } +type dest struct { + *net.IPAddr + Port int +} + func newIPRanges() *IPRanges { return &IPRanges{ - ips: make([]*net.IPAddr, 0), + ips: make([]*dest, 0), } } @@ -73,12 +80,54 @@ func (r *IPRanges) parseCIDR(ip string) { } } +// 解析自定义的 IP 段,获得 IP、端口、IP 范围、子网掩码 +func (r *IPRanges) parseCIDRwithport(ip string) { + var ( + err error + port int64 + isIPv4bool bool + sep string + ) + + if isIPv4(ip) { + isIPv4bool = true + sep = ":" + } else { + isIPv4bool = false + sep = "]:" + } + + arr := strings.Split(ip, sep) + if len(arr) > 1 { + if isIPv4bool { + ip = arr[0] + } else { + ip = arr[0][1:] + } + port, err = strconv.ParseInt(arr[1], 10, 0) + if (err == nil) && (port > 0) && (port < 65535) { + r.port = int(port) + } + } + + if err != nil || port == 0 { + r.port = TCPPort + } + + if r.firstIP, r.ipNet, err = net.ParseCIDR(r.fixIP(ip)); err != nil { + log.Fatalln("ParseCIDRwithport 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}) + r.ips = append(r.ips, &dest{ + &net.IPAddr{IP: ip}, + r.port, + }) } // 返回第四段 ip 的最小值及可用数目 @@ -147,7 +196,7 @@ func (r *IPRanges) chooseIPv6() { } } -func loadIPRanges() []*net.IPAddr { +func loadIPRanges() []*dest { ranges := newIPRanges() if IPText != "" { // 从参数中获取 IP 段数据 IPs := strings.Split(IPText, ",") // 以逗号分隔为数组并循环遍历 @@ -156,8 +205,9 @@ func loadIPRanges() []*net.IPAddr { if IP == "" { // 跳过空的(即开头、结尾或连续多个 ,, 的情况) continue } - ranges.parseCIDR(IP) // 解析 IP 段,获得 IP、IP 范围、子网掩码 - if isIPv4(IP) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部) + //ranges.parseCIDR(IP) // 解析 IP 段,获得 IP、IP 范围、子网掩码 + ranges.parseCIDRwithport(IP) // 解析 IP 段,获得 IP、端口、IP 范围、子网掩码 + if isIPv4(IP) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部) ranges.chooseIPv4() } else { ranges.chooseIPv6() @@ -178,8 +228,9 @@ func loadIPRanges() []*net.IPAddr { if line == "" { // 跳过空行 continue } - ranges.parseCIDR(line) // 解析 IP 段,获得 IP、IP 范围、子网掩码 - if isIPv4(line) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部) + //ranges.parseCIDR(IP) // 解析 IP 段,获得 IP、IP 范围、子网掩码 + ranges.parseCIDRwithport(line) // 解析 IP 段,获得 IP、端口、IP 范围、子网掩码 + if isIPv4(line) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部) ranges.chooseIPv4() } else { ranges.chooseIPv6() diff --git a/task/tcping.go b/task/tcping.go index 511e688c..0b336399 100644 --- a/task/tcping.go +++ b/task/tcping.go @@ -28,7 +28,7 @@ var ( type Ping struct { wg *sync.WaitGroup m *sync.Mutex - ips []*net.IPAddr + ips []*dest csv utils.PingDelaySet control chan bool bar *utils.Bar @@ -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("开始延迟测速(模式:HTTP, 默认端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\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("开始延迟测速(模式: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) @@ -79,20 +79,20 @@ func (p *Ping) Run() utils.PingDelaySet { return p.csv } -func (p *Ping) start(ip *net.IPAddr) { +func (p *Ping) start(ip *dest) { defer p.wg.Done() p.tcpingHandler(ip) <-p.control } // bool connectionSucceed float32 time -func (p *Ping) tcping(ip *net.IPAddr) (bool, time.Duration) { +func (p *Ping) tcping(ip *dest) (bool, time.Duration) { startTime := time.Now() var fullAddress string if isIPv4(ip.String()) { - fullAddress = fmt.Sprintf("%s:%d", ip.String(), TCPPort) + fullAddress = fmt.Sprintf("%s:%d", ip.IPAddr.String(), ip.Port) } else { - fullAddress = fmt.Sprintf("[%s]:%d", ip.String(), TCPPort) + fullAddress = fmt.Sprintf("[%s]:%d", ip.IPAddr.String(), ip.Port) } conn, err := net.DialTimeout("tcp", fullAddress, tcpConnectTimeout) if err != nil { @@ -104,7 +104,7 @@ 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 *dest) (recv int, totalDelay time.Duration) { if Httping { recv, totalDelay = p.httping(ip) return @@ -127,7 +127,7 @@ func (p *Ping) appendIPData(data *utils.PingData) { } // handle tcping -func (p *Ping) tcpingHandler(ip *net.IPAddr) { +func (p *Ping) tcpingHandler(ip *dest) { recv, totalDlay := p.checkConnection(ip) nowAble := len(p.csv) if recv != 0 { @@ -138,7 +138,8 @@ func (p *Ping) tcpingHandler(ip *net.IPAddr) { return } data := &utils.PingData{ - IP: ip, + IP: ip.IPAddr, + Port: ip.Port, Sended: PingTimes, Received: recv, Delay: totalDlay / time.Duration(recv), diff --git a/utils/csv.go b/utils/csv.go index ac48e27e..b2d4b6be 100644 --- a/utils/csv.go +++ b/utils/csv.go @@ -23,6 +23,7 @@ var ( InputMaxLossRate = maxLossRate Output = defaultOutput PrintNum = 10 + OnlyIP = false ) // 是否打印测试结果 @@ -37,6 +38,7 @@ func noOutput() bool { type PingData struct { IP *net.IPAddr + Port int Sended int Received int Delay time.Duration @@ -59,7 +61,11 @@ func (cf *CloudflareIPData) getLossRate() float32 { func (cf *CloudflareIPData) toString() []string { result := make([]string, 6) - result[0] = cf.IP.String() + if OnlyIP { + result[0] = cf.IP.String() + } else { + result[0] = fmt.Sprintf("%s:%d", cf.IP.String(), cf.Port) + } result[1] = strconv.Itoa(cf.Sended) result[2] = strconv.Itoa(cf.Received) result[3] = strconv.FormatFloat(float64(cf.getLossRate()), 'f', 2, 32)