1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 19:17:38 +08:00

feat: use udp to simulate icmp echo

This commit is contained in:
fumiama 2021-12-26 13:08:38 +08:00
parent 9946b404ae
commit d72696a0c8
3 changed files with 69 additions and 51 deletions

View File

@ -67,10 +67,10 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo {
}() }()
go func() { go func() {
defer wg.Done() defer wg.Done()
res := utils.RunICMPPingLoop(&net.IPAddr{IP: c.servers[c.currServerIndex].IP}, 10) res := utils.RunICMPPingLoop(c.servers[c.currServerIndex].IP.String(), 10)
r.ChatServerPacketLoss = res.PacketsLoss r.ChatServerPacketLoss = res.PacketsLoss
if c.highwaySession.AddrLength() > 0 { if c.highwaySession.AddrLength() > 0 {
res = utils.RunICMPPingLoop(&net.IPAddr{IP: c.highwaySession.SsoAddr[0].AsNetIP()}, 10) res = utils.RunICMPPingLoop(c.highwaySession.SsoAddr[0].AsNetIP().String(), 10)
r.SrvServerPacketLoss = res.PacketsLoss r.SrvServerPacketLoss = res.PacketsLoss
} }
}() }()

View File

@ -1,73 +1,79 @@
package utils package utils
import ( import (
"errors"
"math/rand" "math/rand"
"net" "net"
"strconv"
"time" "time"
"github.com/pkg/errors"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
) )
type ICMPPingResult struct { type ICMPPingResult struct {
PacketsSent int PacketsSent int
PacketsRecv int
PacketsLoss int PacketsLoss int
Rtts []int64 AvgTimeMill int64
} }
func RunICMPPingLoop(addr *net.IPAddr, count int) *ICMPPingResult { // RunICMPPingLoop unix 下的 ping
if count <= 0 { func RunICMPPingLoop(ip string, count int) (r ICMPPingResult) {
return nil r = ICMPPingResult{
}
r := &ICMPPingResult{
PacketsSent: count, PacketsSent: count,
Rtts: make([]int64, count), PacketsLoss: count,
AvgTimeMill: 9999,
} }
for i := 1; i <= count; i++ { if count <= 0 {
rtt, err := SendICMPRequest(addr, i) return
if err != nil { }
r.PacketsLoss++ durs := make([]int64, 0, count)
r.Rtts[i-1] = 9999 for i := 0; i < count; i++ {
continue d, err := pingudp(ip)
if err == nil {
r.PacketsLoss--
durs = append(durs, d)
} }
r.PacketsRecv++
r.Rtts[i-1] = rtt
time.Sleep(time.Millisecond * 100)
} }
return r
if len(durs) > 0 {
r.AvgTimeMill = 0
for _, d := range durs {
r.AvgTimeMill += d
}
if len(durs) > 1 {
r.AvgTimeMill /= int64(len(durs))
}
}
return
} }
func SendICMPRequest(addr *net.IPAddr, seq int) (int64, error) { func pingudp(ip string) (int64, error) {
data := make([]byte, 32) var buf [256]byte
rand.Read(data) ch := make(chan error, 1)
body := &icmp.Echo{
ID: 0, port := rand.Intn(10000) + 50000
Seq: seq, conn, err := net.Dial("udp", ip+":"+strconv.Itoa(port))
Data: data,
}
msg := &icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: body,
}
msgBytes, _ := msg.Marshal(nil)
conn, err := net.DialIP("ip4:icmp", nil, addr)
if err != nil { if err != nil {
return 0, errors.Wrap(err, "dial icmp conn error") return 9999, err
} }
defer func() { _ = conn.Close() }()
if _, err = conn.Write(msgBytes); err != nil { t := time.Now().UnixMilli()
return 0, errors.Wrap(err, "write icmp packet error")
} _, err = conn.Write([]byte("fill"))
start := time.Now()
_ = conn.SetReadDeadline(time.Now().Add(time.Second * 2))
buff := make([]byte, 1024)
_, err = conn.Read(buff)
if err != nil { if err != nil {
return 0, errors.Wrap(err, "read icmp conn error") return 0, err
} }
duration := time.Since(start).Milliseconds() go func() {
return duration, nil _, err := conn.Read(buf[:])
ch <- err
}()
select {
case <-time.NewTimer(time.Second * 4).C:
err = errors.New("timeout")
case err = <-ch:
}
if err != nil && err.Error() == "timeout" {
return 9999, err
}
return time.Now().UnixMilli() - t, nil
} }

12
utils/icmp_test.go Normal file
View File

@ -0,0 +1,12 @@
package utils
import (
"testing"
)
func TestPing(t *testing.T) {
r := RunICMPPingLoop("127.0.0.1", 4)
if r.PacketsLoss == r.PacketsSent {
t.Fatal(r)
}
}