1
0
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:
Mrs4s 2021-10-16 16:12:33 +08:00
parent 6e4bbb9e42
commit 6570d10dc4
No known key found for this signature in database
GPG Key ID: 3186E98FA19CE3A7
6 changed files with 155 additions and 5 deletions

View File

@ -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

View File

@ -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")
}

View File

@ -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
View File

@ -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
View File

@ -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
View 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
}