新增下载测速功能
This commit is contained in:
parent
839f48eb01
commit
5c67f34e71
|
@ -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:'
|
|
@ -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
5
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
|
||||
)
|
||||
|
|
98
main.go
98
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)
|
||||
}
|
||||
|
|
100
tcping.go
100
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
Loading…
Reference in New Issue