CloudflareSpeedTest/tcping/ping.go

130 lines
2.8 KiB
Go

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