新增下载测速功能

This commit is contained in:
Spedoske 2020-07-09 10:52:26 +08:00
parent 839f48eb01
commit 5c67f34e71
6 changed files with 296 additions and 71 deletions

41
.goreleaser.yml Normal file
View File

@ -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:'

View File

@ -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)

5
go.mod
View File

@ -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
)

98
main.go
View File

@ -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)
}

100
tcping.go
View File

@ -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
util.go Normal file
View File

@ -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]
}