mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 11:07:40 +08:00
feat: connection quality test.
This commit is contained in:
parent
6e4bbb9e42
commit
6570d10dc4
@ -242,7 +242,7 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
|
||||
for i := range cli.servers {
|
||||
go func(index int) {
|
||||
defer wg.Done()
|
||||
p, err := qualityTest(cli.servers[index])
|
||||
p, err := qualityTest(cli.servers[index].String())
|
||||
if err != nil {
|
||||
pings[index] = 9999
|
||||
return
|
||||
|
@ -488,10 +488,10 @@ func getSSOAddress() ([]*net.TCPAddr, error) {
|
||||
return adds, nil
|
||||
}
|
||||
|
||||
func qualityTest(addr *net.TCPAddr) (int64, error) {
|
||||
func qualityTest(addr string) (int64, error) {
|
||||
// see QualityTestManager
|
||||
start := time.Now()
|
||||
conn, err := net.DialTimeout("tcp", addr.String(), time.Second*5)
|
||||
conn, err := net.DialTimeout("tcp", addr, time.Second*5)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "failed to connect to server during quality test")
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@ -12,6 +15,74 @@ import (
|
||||
"github.com/Mrs4s/MiraiGo/utils"
|
||||
)
|
||||
|
||||
// ConnectionQualityInfo 客户端连接质量测试结果
|
||||
// 延迟单位为 ms 如为 9999 则测试失败 测试方法为 TCP 连接测试
|
||||
// 丢包测试方法为 ICMP. 总共发送 10 个包, 记录丢包数
|
||||
type ConnectionQualityInfo struct {
|
||||
// ChatServerLatency 聊天服务器延迟
|
||||
ChatServerLatency int64
|
||||
// ChatServerPacketLoss 聊天服务器ICMP丢包数
|
||||
ChatServerPacketLoss int
|
||||
// LongMessageServerLatency 长消息服务器延迟. 涉及长消息以及合并转发消息下载
|
||||
LongMessageServerLatency int64
|
||||
// LongMessageServerResponseLatency 长消息服务器返回延迟
|
||||
LongMessageServerResponseLatency int64
|
||||
// SrvServerLatency Highway服务器延迟. 涉及媒体以及群文件上传
|
||||
SrvServerLatency int64
|
||||
// SrvServerPacketLoss Highway服务器ICMP丢包数.
|
||||
SrvServerPacketLoss int
|
||||
}
|
||||
|
||||
func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo {
|
||||
if !c.Online {
|
||||
return nil
|
||||
}
|
||||
r := &ConnectionQualityInfo{}
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
go func(w *sync.WaitGroup) {
|
||||
var err error
|
||||
|
||||
if r.ChatServerLatency, err = qualityTest(c.servers[c.currServerIndex].String()); err != nil {
|
||||
c.Error("test chat server latency error: %v", err)
|
||||
r.ChatServerLatency = 9999
|
||||
}
|
||||
|
||||
if addr, err := net.ResolveIPAddr("ip", "ssl.htdata.qq.com"); err == nil {
|
||||
if r.LongMessageServerLatency, err = qualityTest((&net.TCPAddr{IP: addr.IP, Port: 443}).String()); err != nil {
|
||||
c.Error("test long message server latency error: %v", err)
|
||||
r.LongMessageServerLatency = 9999
|
||||
}
|
||||
} else {
|
||||
c.Error("resolve long message server error: %v", err)
|
||||
r.LongMessageServerLatency = 9999
|
||||
}
|
||||
|
||||
if r.SrvServerLatency, err = qualityTest(c.srvSsoAddrs[0]); err != nil {
|
||||
c.Error("test srv server latency error: %v", err)
|
||||
r.SrvServerLatency = 9999
|
||||
}
|
||||
|
||||
w.Done()
|
||||
}(&wg)
|
||||
go func(w *sync.WaitGroup) {
|
||||
res := utils.RunICMPPingLoop(&net.IPAddr{IP: c.servers[c.currServerIndex].IP}, 10)
|
||||
r.ChatServerPacketLoss = res.PacketsLoss
|
||||
res = utils.RunICMPPingLoop(&net.IPAddr{IP: net.ParseIP(strings.Split(c.srvSsoAddrs[0], ":")[0])}, 10)
|
||||
r.SrvServerPacketLoss = res.PacketsLoss
|
||||
w.Done()
|
||||
}(&wg)
|
||||
start := time.Now()
|
||||
if _, err := utils.HttpGetBytes("https://ssl.htdata.qq.com", ""); err == nil {
|
||||
r.LongMessageServerResponseLatency = time.Now().Sub(start).Milliseconds()
|
||||
} else {
|
||||
c.Error("test long message server response latency error: %v", err)
|
||||
r.LongMessageServerResponseLatency = 9999
|
||||
}
|
||||
wg.Wait()
|
||||
return r
|
||||
}
|
||||
|
||||
// connect 连接到 QQClient.servers 中的服务器
|
||||
func (c *QQClient) connect() error {
|
||||
c.Info("connect to server: %v", c.servers[c.currServerIndex].String())
|
||||
|
3
go.mod
3
go.mod
@ -3,11 +3,10 @@ module github.com/Mrs4s/MiraiGo
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/tidwall/gjson v1.8.1
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
||||
google.golang.org/protobuf v1.27.1
|
||||
)
|
||||
|
8
go.sum
8
go.sum
@ -49,6 +49,8 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -57,7 +59,13 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cO
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
|
72
utils/icmp.go
Normal file
72
utils/icmp.go
Normal file
@ -0,0 +1,72 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ICMPPingResult struct {
|
||||
PacketsSent int
|
||||
PacketsRecv int
|
||||
PacketsLoss int
|
||||
Rtts []int64
|
||||
}
|
||||
|
||||
func RunICMPPingLoop(addr *net.IPAddr, count int) *ICMPPingResult {
|
||||
if count <= 0 {
|
||||
return nil
|
||||
}
|
||||
r := &ICMPPingResult{
|
||||
PacketsSent: count,
|
||||
Rtts: make([]int64, count),
|
||||
}
|
||||
for i := 1; i <= count; i++ {
|
||||
rtt, err := SendICMPRequest(addr, i)
|
||||
if err != nil {
|
||||
r.PacketsLoss++
|
||||
r.Rtts[i-1] = 9999
|
||||
continue
|
||||
}
|
||||
r.PacketsRecv++
|
||||
r.Rtts[i-1] = rtt
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func SendICMPRequest(addr *net.IPAddr, seq int) (int64, error) {
|
||||
data := make([]byte, 32)
|
||||
rand.Read(data)
|
||||
body := &icmp.Echo{
|
||||
ID: 0,
|
||||
Seq: seq,
|
||||
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 {
|
||||
return 0, errors.Wrap(err, "dial icmp conn error")
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
if _, err = conn.Write(msgBytes); err != nil {
|
||||
return 0, errors.Wrap(err, "write icmp packet error")
|
||||
}
|
||||
start := time.Now()
|
||||
_ = conn.SetReadDeadline(time.Now().Add(time.Second * 2))
|
||||
buff := make([]byte, 1024)
|
||||
_, err = conn.Read(buff)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "read icmp conn error")
|
||||
}
|
||||
duration := time.Now().Sub(start).Milliseconds()
|
||||
return duration, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user