tcping add progressbar

This commit is contained in:
mazhuang 2021-11-10 12:25:10 +08:00
parent 71671ebe66
commit 4d64abb94d
7 changed files with 237 additions and 148 deletions

16
main.go
View File

@ -11,6 +11,8 @@ import (
"sync"
"time"
"CloudflareSpeedTest/task"
"github.com/cheggaaa/pb/v3"
)
@ -76,12 +78,16 @@ https://github.com/XIU2/CloudflareSpeedTest
flag.Float64Var(&speedLimit, "sl", 0, "下载速度下限")
flag.IntVar(&printResultNum, "p", 20, "显示结果数量")
flag.BoolVar(&disableDownload, "dd", false, "禁用下载测速")
flag.BoolVar(&ipv6Mode, "ipv6", false, "禁用下载测速")
flag.BoolVar(&ipv6Mode, "ipv6", false, "启用IPv6")
flag.BoolVar(&allip, "allip", false, "测速全部 IP")
flag.StringVar(&ipFile, "f", "ip.txt", "IP 数据文件")
flag.StringVar(&outputFile, "o", "result.csv", "输出结果文件")
flag.BoolVar(&printVersion, "v", false, "打印程序版本")
task.TCPPort = tcpPort
task.IPv6 = ipv6Mode
task.DefaultRoutine = pingRoutine
flag.Usage = func() { fmt.Print(help) }
flag.Parse()
if printVersion {
@ -155,16 +161,20 @@ func main() {
ipVersion = "IPv6"
}
fmt.Printf("开始延迟测速模式TCP %s端口%d ,平均延迟上限:%.2f ms平均延迟下限%.2f ms\n", ipVersion, tcpPort, timeLimit, timeLimitLow)
// ping := task.NewPing(ips)
// ping.Run()
control := make(chan bool, pingRoutine)
for _, ip := range ips {
wg.Add(1)
// control <- false
control <- false
handleProgress := handleProgressGenerator(bar) // 多线程进度条
go tcpingGoroutine(&wg, &mu, ip, tcpPort, pingTime, &data, control, handleProgress)
}
wg.Wait()
bar.Finish()
// data := ping.Data()
// sort.Sort(utils.CloudflareIPDataSet(data))
sort.Sort(CloudflareIPDataSet(data)) // 排序(按延迟,从低到高,不同丢包率会分开单独按延迟和丢包率排序)
// 延迟测速完毕后,以 [平均延迟上限] + [平均延迟下限] 条件过滤结果

6
task/ip.go Normal file
View File

@ -0,0 +1,6 @@
package task
var (
// IPv6 IP version is 6
IPv6 = false
)

135
task/tcping.go Normal file
View File

@ -0,0 +1,135 @@
package task
import (
"fmt"
"net"
"sync"
"time"
"CloudflareSpeedTest/utils"
)
const (
tcpConnectTimeout = time.Second * 1
maxRoutine = 1000
)
var (
DefaultRoutine = 200
TCPPort int = 443
PingTimes int = 4
)
type Ping struct {
wg *sync.WaitGroup
m *sync.Mutex
ips []net.IPAddr
csv utils.PingDelaySet
control chan bool
bar *utils.Bar
}
func NewPing(ips []net.IPAddr) *Ping {
return &Ping{
wg: &sync.WaitGroup{},
m: &sync.Mutex{},
ips: ips,
csv: make(utils.PingDelaySet, 0),
control: make(chan bool, DefaultRoutine),
bar: utils.NewBar(len(ips) * PingTimes),
}
}
func (p *Ping) Run() utils.PingDelaySet {
for _, ip := range p.ips {
p.wg.Add(1)
p.control <- false
go p.start(ip)
}
p.wg.Wait()
p.bar.Done()
return p.csv
}
func (p *Ping) start(ip net.IPAddr) {
defer p.wg.Done()
p.tcpingHandler(ip)
<-p.control
}
//bool connectionSucceed float32 time
func (p *Ping) tcping(ip net.IPAddr) (bool, time.Duration) {
startTime := time.Now()
fullAddress := fmt.Sprintf("%s:%d", ip.String(), TCPPort)
//fmt.Println(ip.String())
if IPv6 { // IPv6 需要加上 []
fullAddress = fmt.Sprintf("[%s]:%d", ip.String(), TCPPort)
}
conn, err := net.DialTimeout("tcp", fullAddress, tcpConnectTimeout)
if err != nil {
return false, 0
}
defer conn.Close()
duration := time.Since(startTime)
return true, duration
}
//pingReceived pingTotalTime
func (p *Ping) checkConnection(ip net.IPAddr) (recv int, totalDelay time.Duration) {
for i := 0; i < PingTimes; i++ {
if ok, delay := p.tcping(ip); ok {
recv++
totalDelay += delay
}
}
return
}
func (p *Ping) appendIPData(data *utils.PingData) {
p.m.Lock()
defer p.m.Unlock()
p.csv = append(p.csv, utils.CloudflareIPData{
PingData: data,
})
}
//return Success packetRecv averagePingTime specificIPAddr
func (p *Ping) tcpingHandler(ip net.IPAddr) {
ipCanConnect := false
pingRecv := 0
var delay time.Duration
for !ipCanConnect {
recv, totalDlay := p.checkConnection(ip)
if recv > 0 {
ipCanConnect = true
pingRecv = recv
delay = totalDlay
} else {
ip.IP[15]++
if ip.IP[15] == 0 {
break
}
break
}
}
p.bar.Grow(PingTimes)
if !ipCanConnect {
return
}
// for i := 0; i < PingTimes; i++ {
// pingSuccess, pingTimeCurrent := p.tcping(ip)
// progressHandler(utils.NormalPing)
// if pingSuccess {
// pingRecv++
// pingTime += pingTimeCurrent
// }
// }
data := &utils.PingData{
IP: ip,
Sended: PingTimes,
Received: pingRecv,
Delay: delay / time.Duration(pingRecv),
}
p.appendIPData(data)
return
}

View File

@ -91,7 +91,7 @@ func tcpingGoroutine(wg *sync.WaitGroup, mutex *sync.Mutex, ip net.IPAddr, tcpPo
*csv = append(*csv, cfdata)
mutex.Unlock()
}
// <-control
<-control
}
func GetDialContextByAddr(fakeSourceAddr string) func(ctx context.Context, network, address string) (net.Conn, error) {

View File

@ -1,129 +0,0 @@
package tcp
import (
"fmt"
"net"
"sync"
"time"
"CloudflareSpeedTest/utils"
)
const tcpConnectTimeout = time.Second * 1
type Ping struct {
wg *sync.WaitGroup
m *sync.Mutex
ips []net.IPAddr
isIPv6 bool
tcpPort int
pingCount int
csv []utils.CloudflareIPData
control chan bool
progressHandler func(e utils.ProgressEvent)
}
func NewPing(ips []net.IPAddr, port, pingTime int, ipv6 bool) *Ping {
return &Ping{
wg: &sync.WaitGroup{},
m: &sync.Mutex{},
ips: ips,
isIPv6: ipv6,
tcpPort: port,
pingCount: pingTime,
csv: make([]utils.CloudflareIPData, 0),
control: make(chan bool),
}
}
func (p *Ping) Run() {
for _, ip := range p.ips {
p.wg.Add(1)
p.control <- false
go p.start(ip)
}
}
func (p *Ping) start(ip net.IPAddr) {
defer p.wg.Done()
if ok, data := p.tcpingHandler(ip, nil); ok {
p.appendIPData(data)
}
<-p.control
}
func (p *Ping) appendIPData(data *utils.PingData) {
p.m.Lock()
defer p.m.Unlock()
p.csv = append(p.csv, utils.CloudflareIPData{
PingData: data,
})
}
//bool connectionSucceed float32 time
func (p *Ping) tcping(ip net.IPAddr) (bool, time.Duration) {
startTime := time.Now()
fullAddress := fmt.Sprintf("%s:%d", ip.String(), p.tcpPort)
//fmt.Println(ip.String())
if p.isIPv6 { // IPv6 需要加上 []
fullAddress = fmt.Sprintf("[%s]:%d", ip.String(), p.tcpPort)
}
conn, err := net.DialTimeout("tcp", fullAddress, tcpConnectTimeout)
if err != nil {
return false, 0
}
defer conn.Close()
duration := time.Since(startTime)
return true, duration
}
//pingReceived pingTotalTime
func (p *Ping) checkConnection(ip net.IPAddr) (pingRecv int, pingTime time.Duration) {
for i := 0; i < p.pingCount; i++ {
if pingSucceed, pingTimeCurrent := p.tcping(ip); pingSucceed {
pingRecv++
pingTime += pingTimeCurrent
}
}
return
}
//return Success packetRecv averagePingTime specificIPAddr
func (p *Ping) tcpingHandler(ip net.IPAddr, progressHandler func(e utils.ProgressEvent)) (bool, *utils.PingData) {
ipCanConnect := false
pingRecv := 0
var pingTime time.Duration
for !ipCanConnect {
pingRecvCurrent, pingTimeCurrent := p.checkConnection(ip)
if pingRecvCurrent != 0 {
ipCanConnect = true
pingRecv = pingRecvCurrent
pingTime = pingTimeCurrent
} else {
ip.IP[15]++
if ip.IP[15] == 0 {
break
}
break
}
}
if !ipCanConnect {
progressHandler(utils.NoAvailableIPFound)
return false, nil
}
progressHandler(utils.AvailableIPFound)
for i := 0; i < p.pingCount; i++ {
pingSuccess, pingTimeCurrent := p.tcping(ip)
progressHandler(utils.NormalPing)
if pingSuccess {
pingRecv++
pingTime += pingTimeCurrent
}
}
return true, &utils.PingData{
IP: ip,
Count: p.pingCount,
Received: pingRecv,
Delay: pingTime / time.Duration(pingRecv),
}
}

View File

@ -5,13 +5,22 @@ import (
"log"
"net"
"os"
"sort"
"strconv"
"time"
)
var (
MaxDelay = 9999 * time.Millisecond
MinDelay = time.Duration(0)
InputMaxDelay = MaxDelay
InputMinDelay = MinDelay
)
type PingData struct {
IP net.IPAddr
Count int
Sended int
Received int
Delay time.Duration
}
@ -24,12 +33,23 @@ type CloudflareIPData struct {
func (cf *CloudflareIPData) getRecvRate() float32 {
if cf.recvRate == 0 {
pingLost := cf.Count - cf.Received
cf.recvRate = float32(pingLost) / float32(cf.Count)
pingLost := cf.Sended - cf.Received
cf.recvRate = float32(pingLost) / float32(cf.Sended)
}
return cf.recvRate
}
func (cf *CloudflareIPData) toString() []string {
result := make([]string, 6)
result[0] = cf.IP.String()
result[1] = strconv.Itoa(cf.Sended)
result[2] = strconv.Itoa(cf.Received)
result[3] = strconv.FormatFloat(float64(cf.getRecvRate()), 'f', 2, 32)
result[4] = cf.Delay.String()
result[5] = strconv.FormatFloat(float64(cf.downloadSpeed)/1024/1024, 'f', 2, 32)
return result
}
func ExportCsv(filePath string, data []CloudflareIPData) {
fp, err := os.Create(filePath)
if err != nil {
@ -43,17 +63,6 @@ func ExportCsv(filePath string, data []CloudflareIPData) {
w.Flush()
}
func (cf *CloudflareIPData) toString() []string {
result := make([]string, 6)
result[0] = cf.IP.String()
result[1] = strconv.Itoa(cf.Count)
result[2] = strconv.Itoa(cf.Received)
result[3] = strconv.FormatFloat(float64(cf.getRecvRate()), 'f', 2, 32)
result[4] = cf.Delay.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 {
@ -61,3 +70,53 @@ func convertToString(data []CloudflareIPData) [][]string {
}
return result
}
type PingDelaySet []CloudflareIPData
func (s PingDelaySet) FilterDelay() (data PingDelaySet) {
sort.Sort(s)
if InputMaxDelay >= MaxDelay || InputMinDelay <= MinDelay {
return s
}
for _, v := range s {
if v.Delay > MaxDelay { // 平均延迟上限
break
}
if v.Delay <= MinDelay { // 平均延迟下限
continue
}
data = append(data, v) // 延迟满足条件时,添加到新数组中
}
return
}
func (s PingDelaySet) Len() int {
return len(s)
}
func (s PingDelaySet) Less(i, j int) bool {
iRate, jRate := s[i].getRecvRate(), s[j].getRecvRate()
if iRate != jRate {
return iRate < jRate
}
return s[i].Delay < s[j].Delay
}
func (s PingDelaySet) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// 下载速度排序
type DownloadSpeedSet []CloudflareIPData
func (s DownloadSpeedSet) Len() int {
return len(s)
}
func (s DownloadSpeedSet) Less(i, j int) bool {
return s[i].downloadSpeed > s[j].downloadSpeed
}
func (s DownloadSpeedSet) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

View File

@ -11,13 +11,21 @@ const (
)
type Bar struct {
*pb.ProgressBar
pb *pb.ProgressBar
}
func NewBar(count int) *Bar {
return &Bar{pb.Simple.Start(count)}
}
func (b *Bar) Grow(num int) {
b.pb.Add(num)
}
func (b *Bar) Done() {
b.pb.Finish()
}
func handleProgressGenerator(pb *pb.ProgressBar) func(e ProgressEvent) {
return func(e ProgressEvent) {
switch e {