From 83c63e975e2c8df0698430d4e3417d8903f30ea9 Mon Sep 17 00:00:00 2001 From: mazhuang Date: Tue, 9 Nov 2021 18:44:17 +0800 Subject: [PATCH] rebuild tcping --- tcping.go | 123 ++++++++++++++++++++++------------------------ tcping/tcping.go | 19 +++++++ util.go | 6 +-- utils/csv.go | 59 ++++++++++++++++++++++ utils/progress.go | 9 ++++ 5 files changed, 148 insertions(+), 68 deletions(-) create mode 100644 tcping/tcping.go create mode 100644 utils/csv.go create mode 100644 utils/progress.go diff --git a/tcping.go b/tcping.go index 9c38df8..6ac296e 100644 --- a/tcping.go +++ b/tcping.go @@ -2,10 +2,10 @@ package main import ( "context" + "fmt" "io" "net" "net/http" - "strconv" "sync" "time" @@ -13,30 +13,24 @@ import ( ) //bool connectionSucceed float32 time -func tcping(ip net.IPAddr, tcpPort int) (bool, float32) { +func tcping(ip net.IPAddr, tcpPort int) (bool, time.Duration) { startTime := time.Now() - var fullAddress string + fullAddress := fmt.Sprintf("%s:%d", ip.String(), tcpPort) //fmt.Println(ip.String()) if ipv6Mode { // IPv6 需要加上 [] - fullAddress = "[" + ip.String() + "]:" + strconv.Itoa(tcpPort) - } else { - fullAddress = ip.String() + ":" + strconv.Itoa(tcpPort) + fullAddress = fmt.Sprintf("[%s]:%d", ip.String(), tcpPort) } conn, err := net.DialTimeout("tcp", fullAddress, tcpConnectTimeout) if err != nil { return false, 0 - } else { - var endTime = time.Since(startTime) - var duration = float32(endTime.Microseconds()) / 1000.0 - _ = conn.Close() - return true, duration } + defer conn.Close() + duration := time.Since(startTime) + return true, duration } //pingReceived pingTotalTime -func checkConnection(ip net.IPAddr, tcpPort int) (int, float32) { - pingRecv := 0 - var pingTime float32 = 0.0 +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 { @@ -44,14 +38,14 @@ func checkConnection(ip net.IPAddr, tcpPort int) (int, float32) { pingTime += pingTimeCurrent } } - return pingRecv, pingTime + return } //return Success packetRecv averagePingTime specificIPAddr -func tcpingHandler(ip net.IPAddr, tcpPort int, pingCount int, progressHandler func(e progressEvent)) (bool, int, float32, net.IPAddr) { +func tcpingHandler(ip net.IPAddr, tcpPort, pingCount int, progressHandler func(e progressEvent)) (bool, int, time.Duration, net.IPAddr) { ipCanConnect := false pingRecv := 0 - var pingTime float32 = 0.0 + var pingTime time.Duration for !ipCanConnect { pingRecvCurrent, pingTimeCurrent := checkConnection(ip, tcpPort) if pingRecvCurrent != 0 { @@ -66,21 +60,20 @@ func tcpingHandler(ip net.IPAddr, tcpPort int, pingCount int, progressHandler fu break } } - if ipCanConnect { - progressHandler(AvailableIPFound) - for i := failTime; i < pingCount; i++ { - pingSuccess, pingTimeCurrent := tcping(ip, tcpPort) - progressHandler(NormalPing) - if pingSuccess { - pingRecv++ - pingTime += pingTimeCurrent - } - } - return true, pingRecv, pingTime / float32(pingRecv), ip - } else { + if !ipCanConnect { progressHandler(NoAvailableIPFound) return false, 0, 0, net.IPAddr{} } + progressHandler(AvailableIPFound) + for i := failTime; i < pingCount; i++ { + 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)) { @@ -126,47 +119,47 @@ func DownloadSpeedHandler(ip net.IPAddr) (bool, float32) { 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) + } + 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) + contentLength := response.ContentLength + buffer := make([]byte, downloadBufferSize) - var contentRead int64 = 0 - var timeSlice = downloadTestTime / 100 - var timeCounter = 1 - var lastContentRead int64 = 0 + var ( + contentRead int64 = 0 + timeSlice = downloadTestTime / 100 + timeCounter = 1 + lastContentRead int64 = 0 + ) - var nextTime = timeStart.Add(timeSlice * time.Duration(timeCounter)) - e := ewma.NewMovingAverage() + 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))) - } - } + 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 } - return true, float32(e.Value()) / (float32(downloadTestTime.Seconds()) / 120) - } else { - return false, 0 + e.Add(float64(contentRead-lastContentRead) / (float64(nextTime.Sub(currentTime)) / float64(timeSlice))) } } + return true, float32(e.Value()) / (float32(downloadTestTime.Seconds()) / 120) + } diff --git a/tcping/tcping.go b/tcping/tcping.go new file mode 100644 index 0000000..eb0e13b --- /dev/null +++ b/tcping/tcping.go @@ -0,0 +1,19 @@ +package tcping + +import ( + "net" + "sync" + + "CloudflareSpeedTest/utils" +) + +type Tcp struct { + wg *sync.WaitGroup + mutex *sync.Mutex + ip net.IPAddr + tcpPort int + pingCount int + csv *[]utils.CloudflareIPData + control chan bool + progressHandler func(e utils.ProgressEvent) +} diff --git a/util.go b/util.go index fb20cd5..e1117ab 100644 --- a/util.go +++ b/util.go @@ -18,7 +18,7 @@ type CloudflareIPData struct { pingReceived int recvRate float32 downloadSpeed float32 - pingTime float32 + pingTime time.Duration } func (cf *CloudflareIPData) getRecvRate() float32 { @@ -32,7 +32,7 @@ func (cf *CloudflareIPData) getRecvRate() float32 { func ExportCsv(filePath string, data []CloudflareIPData) { fp, err := os.Create(filePath) if err != nil { - log.Fatalf("创建文件["+filePath+"]句柄失败,%v", err) + log.Fatalf("创建文件[%s]失败:%v", filePath, err) return } defer fp.Close() @@ -48,7 +48,7 @@ func (cf *CloudflareIPData) toString() []string { result[1] = strconv.Itoa(cf.pingCount) result[2] = strconv.Itoa(cf.pingReceived) result[3] = strconv.FormatFloat(float64(cf.getRecvRate()), 'f', 2, 32) - result[4] = strconv.FormatFloat(float64(cf.pingTime), 'f', 2, 32) + result[4] = cf.pingTime.String() result[5] = strconv.FormatFloat(float64(cf.downloadSpeed)/1024/1024, 'f', 2, 32) return result } diff --git a/utils/csv.go b/utils/csv.go new file mode 100644 index 0000000..8e586fd --- /dev/null +++ b/utils/csv.go @@ -0,0 +1,59 @@ +package utils + +import ( + "encoding/csv" + "log" + "net" + "os" + "strconv" + "time" +) + +type CloudflareIPData struct { + ip net.IPAddr + pingCount int + pingReceived int + recvRate float32 + downloadSpeed float32 + pingTime time.Duration +} + +func (cf *CloudflareIPData) getRecvRate() float32 { + if cf.recvRate == 0 { + pingLost := cf.pingCount - cf.pingReceived + cf.recvRate = float32(pingLost) / float32(cf.pingCount) + } + return cf.recvRate +} + +func ExportCsv(filePath string, data []CloudflareIPData) { + fp, err := os.Create(filePath) + if err != nil { + log.Fatalf("创建文件[%s]失败:%v", filePath, err) + return + } + defer fp.Close() + w := csv.NewWriter(fp) //创建一个新的写入文件流 + w.Write([]string{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)"}) + w.WriteAll(convertToString(data)) + w.Flush() +} + +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', 2, 32) + result[4] = cf.pingTime.String() + result[5] = strconv.FormatFloat(float64(cf.downloadSpeed)/1024/1024, 'f', 2, 32) + return result +} + +func convertToString(data []CloudflareIPData) [][]string { + result := make([][]string, 0) + for _, v := range data { + result = append(result, v.toString()) + } + return result +} diff --git a/utils/progress.go b/utils/progress.go new file mode 100644 index 0000000..cdb6589 --- /dev/null +++ b/utils/progress.go @@ -0,0 +1,9 @@ +package utils + +type ProgressEvent int + +const ( + NoAvailableIPFound ProgressEvent = iota + AvailableIPFound + NormalPing +)