浏览代码

新增下载测速功能

Spedoske 5 年之前
父节点
当前提交
5c67f34e71
共有 6 个文件被更改,包括 296 次插入71 次删除
  1. 41 0
      .goreleaser.yml
  2. 1 1
      IPRangeLoader.go
  3. 4 1
      go.mod
  4. 43 55
      main.go
  5. 86 14
      tcping.go
  6. 121 0
      util.go

+ 41 - 0
.goreleaser.yml

@@ -0,0 +1,41 @@
+# This is an example goreleaser.yaml file with some sane defaults.
+# Make sure to check the documentation at http://goreleaser.com
+before:
+  hooks:
+    # you may remove this if you don't use vgo
+    - go mod tidy
+    # you may remove this if you don't need go generate
+    - go generate ./...
+builds:
+- env:
+  - CGO_ENABLED=0
+  id: "CloudflareScanner"
+  binary: "CloudflareScanner"
+  goos:
+    - darwin
+    - freebsd
+    - linux
+    - windows
+  goarch:
+    - 386
+    - amd64
+    - arm
+  #hooks:
+    #post: ./compile.bat "{{ dir .Path }}"
+archives:
+- replacements:
+    darwin: MacOS
+    linux: Linux
+    windows: Windows
+    386: x86
+    amd64: x64
+checksum:
+  name_template: 'checksums.txt'
+snapshot:
+  name_template: "v1.1.0"
+changelog:
+  sort: asc
+  filters:
+    exclude:
+    - '^docs:'
+    - '^test:'

+ 1 - 1
IPRangeLoader.go

@@ -21,7 +21,7 @@ func loadFirstIPOfRangeFromFile() []net.IPAddr {
 		if err != nil {
 			log.Fatal(err)
 		}
-		firstIP[15]=ipEndWith
+		firstIP[15] = ipEndWith
 		for IPRange.Contains(firstIP) {
 			firstIPCopy := make([]byte, len(firstIP))
 			copy(firstIPCopy, firstIP)

+ 4 - 1
go.mod

@@ -2,4 +2,7 @@ module CloudflareIPScanner
 
 go 1.14
 
-require github.com/cheggaaa/pb/v3 v3.0.4
+require (
+	github.com/VividCortex/ewma v1.1.1
+	github.com/cheggaaa/pb/v3 v3.0.4
+)

+ 43 - 55
main.go

@@ -1,79 +1,67 @@
 package main
 
 import (
-	"encoding/csv"
 	"fmt"
 	"github.com/cheggaaa/pb/v3"
-	"log"
-	"os"
+	"sort"
 	"sync"
+	"time"
 )
 
-func ExportCsv(filePath string, data [][]string) {
-	fp, err := os.Create(filePath)
-	if err != nil {
-		log.Fatalf("创建文件["+filePath+"]句柄失败,%v", err)
-		return
-	}
-	defer fp.Close()
-	w := csv.NewWriter(fp) //创建一个新的写入文件流
-	w.WriteAll(data)
-	w.Flush()
-}
-
-var pingTime int
-var pingRoutine int
-const ipEndWith uint8 = 1
-type progressEvent int
-const (
-	NoAvailableIPFound progressEvent = iota
-	AvailableIPFound
-	NormalPing
-)
-
-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 handleUserInput(){
-	fmt.Println("请输入扫描协程数(数字越大越快,默认100):")
+func handleUserInput() {
+	fmt.Println("请输入扫描协程数(数字越大越快,默认400):")
 	fmt.Scanln(&pingRoutine)
-	if pingRoutine<=0{
-		pingRoutine=100
+	if pingRoutine <= 0 {
+		pingRoutine = 400
 	}
 	fmt.Println("请输入tcping次数(默认10):")
 	fmt.Scanln(&pingTime)
-	if pingTime<=0{
-		pingTime=10
+	if pingTime <= 0 {
+		pingTime = 10
+	}
+	fmt.Println("请输入要测试的下载节点个数(默认10):")
+	fmt.Scanln(&downloadTestCount)
+	if downloadTestCount <= 0 {
+		downloadTestCount = 10
 	}
+	fmt.Println("请输入下载测试时间(默认10,单位为秒):")
+	var downloadSecond int64
+	fmt.Scanln(&downloadSecond)
+	if downloadSecond <= 0 {
+		downloadSecond = 10
+	}
+	downloadTestTime = time.Duration(downloadSecond) * time.Second
 }
 
-func main(){
+func main() {
+	initipEndWith()
 	handleUserInput()
-	ips:=loadFirstIPOfRangeFromFile()
-	pingCount:=len(ips)*pingTime
+	ips := loadFirstIPOfRangeFromFile()
+	pingCount := len(ips) * pingTime
 	bar := pb.StartNew(pingCount)
 	var wg sync.WaitGroup
 	var mu sync.Mutex
-	var data = make([][]string,0)
-	data = append(data,[]string{"IP Address","Ping received","Ping time"})
-	control := make(chan bool,pingRoutine)
-	for _,ip :=range ips{
+	var data = make([]CloudflareIPData, 0)
+
+	fmt.Println("开始tcping")
+
+	control := make(chan bool, pingRoutine)
+	for _, ip := range ips {
 		wg.Add(1)
-		control<-false
-		handleProgress:=handleProgressGenerator(bar)
-		go tcpingGoroutine(&wg,&mu,ip,pingTime, &data,control,handleProgress)
+		control <- false
+		handleProgress := handleProgressGenerator(bar)
+		go tcpingGoroutine(&wg, &mu, ip, pingTime, &data, control, handleProgress)
 	}
 	wg.Wait()
 	bar.Finish()
-	ExportCsv("./result.csv",data)
+	bar = pb.StartNew(downloadTestCount)
+	sort.Sort(CloudflareIPDataSet(data))
+	fmt.Println("开始下载测速")
+	for i := 0; i < downloadTestCount; i++ {
+		_, speed := DownloadSpeedHandler(data[i].ip)
+		data[i].downloadSpeed = speed
+		bar.Add(1)
+	}
+	bar.Finish()
+	ExportCsv("./result.csv", data)
 }

+ 86 - 14
tcping.go

@@ -1,34 +1,34 @@
 package main
 
 import (
+	"context"
+	"github.com/VividCortex/ewma"
+	"io"
 	"net"
+	"net/http"
 	"strconv"
 	"sync"
 	"time"
 )
 
-const defaultTcpPort = 443
-const tcpConnectTimeout = time.Second * 1
-const failTime = 4
-
-//bool connectionSucceed float64 time
-func tcping(ip net.IPAddr) (bool, float64) {
+//bool connectionSucceed float32 time
+func tcping(ip net.IPAddr) (bool, float32) {
 	startTime := time.Now()
 	conn, err := net.DialTimeout("tcp", ip.String()+":"+strconv.Itoa(defaultTcpPort), tcpConnectTimeout)
 	if err != nil {
 		return false, 0
 	} else {
 		var endTime = time.Since(startTime)
-		var duration = float64(endTime.Microseconds()) / 1000.0
+		var duration = float32(endTime.Microseconds()) / 1000.0
 		_ = conn.Close()
 		return true, duration
 	}
 }
 
 //pingReceived pingTotalTime
-func checkConnection(ip net.IPAddr) (int, float64) {
+func checkConnection(ip net.IPAddr) (int, float32) {
 	pingRecv := 0
-	pingTime := 0.0
+	var pingTime float32 = 0.0
 	for i := 1; i <= failTime; i++ {
 		pingSucceed, pingTimeCurrent := tcping(ip)
 		if pingSucceed {
@@ -40,10 +40,10 @@ func checkConnection(ip net.IPAddr) (int, float64) {
 }
 
 //return Success packetRecv averagePingTime specificIPAddr
-func tcpingHandler(ip net.IPAddr, pingCount int, progressHandler func(e progressEvent)) (bool, int, float64, net.IPAddr) {
+func tcpingHandler(ip net.IPAddr, pingCount int, progressHandler func(e progressEvent)) (bool, int, float32, net.IPAddr) {
 	ipCanConnect := false
 	pingRecv := 0
-	pingTime := 0.0
+	var pingTime float32 = 0.0
 	for !ipCanConnect {
 		pingRecvCurrent, pingTimeCurrent := checkConnection(ip)
 		if pingRecvCurrent != 0 {
@@ -68,20 +68,92 @@ func tcpingHandler(ip net.IPAddr, pingCount int, progressHandler func(e progress
 				pingTime += pingTimeCurrent
 			}
 		}
-		return true, pingRecv, pingTime / float64(pingRecv), ip
+		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, pingCount int, csv *[][]string, control chan bool, progressHandler func(e progressEvent)) {
+func tcpingGoroutine(wg *sync.WaitGroup, mutex *sync.Mutex, ip net.IPAddr, pingCount int, csv *[]CloudflareIPData, control chan bool, progressHandler func(e progressEvent)) {
 	defer wg.Done()
 	success, pingRecv, pingTimeAvg, currentIP := tcpingHandler(ip, pingCount, progressHandler)
 	if success {
 		mutex.Lock()
-		*csv = append(*csv, []string{currentIP.String(), strconv.Itoa(pingRecv), strconv.FormatFloat(pingTimeAvg, 'f', 4, 64)})
+		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
+		}
+	}
+}

+ 121 - 0
util.go

@@ -0,0 +1,121 @@
+package main
+
+import (
+	"encoding/csv"
+	"github.com/cheggaaa/pb/v3"
+	"log"
+	"math/rand"
+	"net"
+	"os"
+	"strconv"
+	"time"
+)
+
+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 {
+		cf.recvRate = float32(cf.pingReceived) / 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 Address", "Ping count", "Ping received", "Ping received rate", "Ping time", "Download Speed (MB/s)"})
+	w.WriteAll(convertToString(data))
+	w.Flush()
+}
+
+//"IP Address","Ping Count","Ping received","Ping received rate","Ping time","Download speed"
+
+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', 4, 32)
+	result[4] = strconv.FormatFloat(float64(cf.pingTime), 'f', 4, 32)
+	result[5] = strconv.FormatFloat(float64(cf.downloadSpeed)/1024/1024, 'f', 4, 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
+const failTime = 4
+
+type CloudflareIPDataSet []CloudflareIPData
+
+func initipEndWith() {
+	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]
+}