github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/client/network.go (about) 1 package client 2 3 import ( 4 "net" 5 "net/netip" 6 "runtime/debug" 7 "sort" 8 "sync" 9 "time" 10 11 "github.com/pkg/errors" 12 13 "github.com/Mrs4s/MiraiGo/client/internal/network" 14 "github.com/Mrs4s/MiraiGo/client/internal/oicq" 15 "github.com/Mrs4s/MiraiGo/message" 16 "github.com/Mrs4s/MiraiGo/utils" 17 ) 18 19 // ConnectionQualityInfo 客户端连接质量测试结果 20 // 延迟单位为 ms 如为 9999 则测试失败 测试方法为 TCP 连接测试 21 // 丢包测试方法为 ICMP. 总共发送 10 个包, 记录丢包数 22 type ConnectionQualityInfo struct { 23 // ChatServerLatency 聊天服务器延迟 24 ChatServerLatency int64 25 // ChatServerPacketLoss 聊天服务器ICMP丢包数 26 ChatServerPacketLoss int 27 // LongMessageServerLatency 长消息服务器延迟. 涉及长消息以及合并转发消息下载 28 LongMessageServerLatency int64 29 // LongMessageServerResponseLatency 长消息服务器返回延迟 30 LongMessageServerResponseLatency int64 31 // SrvServerLatency Highway服务器延迟. 涉及媒体以及群文件上传 32 SrvServerLatency int64 33 // SrvServerPacketLoss Highway服务器ICMP丢包数. 34 SrvServerPacketLoss int 35 } 36 37 func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { 38 if !c.Online.Load() { 39 return nil 40 } 41 r := &ConnectionQualityInfo{} 42 wg := sync.WaitGroup{} 43 wg.Add(2) 44 45 currentServerAddr := c.servers[c.currServerIndex].String() 46 go func() { 47 defer wg.Done() 48 var err error 49 50 if r.ChatServerLatency, err = qualityTest(currentServerAddr); err != nil { 51 c.error("test chat server latency error: %v", err) 52 r.ChatServerLatency = 9999 53 } 54 55 if addr, err := net.ResolveIPAddr("ip", "ssl.htdata.qq.com"); err == nil { 56 if r.LongMessageServerLatency, err = qualityTest((&net.TCPAddr{IP: addr.IP, Port: 443}).String()); err != nil { 57 c.error("test long message server latency error: %v", err) 58 r.LongMessageServerLatency = 9999 59 } 60 } else { 61 c.error("resolve long message server error: %v", err) 62 r.LongMessageServerLatency = 9999 63 } 64 if c.highwaySession.AddrLength() > 0 { 65 if r.SrvServerLatency, err = qualityTest(c.highwaySession.SsoAddr[0].String()); err != nil { 66 c.error("test srv server latency error: %v", err) 67 r.SrvServerLatency = 9999 68 } 69 } 70 }() 71 go func() { 72 defer wg.Done() 73 res := utils.RunTCPPingLoop(currentServerAddr, 10) 74 r.ChatServerPacketLoss = res.PacketsLoss 75 if c.highwaySession.AddrLength() > 0 { 76 res = utils.RunTCPPingLoop(c.highwaySession.SsoAddr[0].String(), 10) 77 r.SrvServerPacketLoss = res.PacketsLoss 78 } 79 }() 80 start := time.Now() 81 if _, err := utils.HttpGetBytes("https://ssl.htdata.qq.com", ""); err == nil { 82 r.LongMessageServerResponseLatency = time.Since(start).Milliseconds() 83 } else { 84 c.error("test long message server response latency error: %v", err) 85 r.LongMessageServerResponseLatency = 9999 86 } 87 wg.Wait() 88 return r 89 } 90 91 func (c *QQClient) initServers() { 92 if c.Device() == nil { 93 // must have device. Use c.UseDevice to set it! 94 panic("client device is nil") 95 } 96 97 sso, err := getSSOAddress(c.Device()) 98 if err == nil && len(sso) > 0 { 99 c.servers = append(sso, c.servers...) 100 } 101 adds, err := net.LookupIP("msfwifi.3g.qq.com") // host servers 102 if err == nil && len(adds) > 0 { 103 var hostAddrs []netip.AddrPort 104 for _, addr := range adds { 105 ip, ok := netip.AddrFromSlice(addr.To4()) 106 if ok { 107 hostAddrs = append(hostAddrs, netip.AddrPortFrom(ip, 8080)) 108 } 109 } 110 c.servers = append(hostAddrs, c.servers...) 111 } 112 if len(c.servers) == 0 { 113 c.servers = []netip.AddrPort{ // default servers 114 netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 81}), 80), 115 netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 148, 59}), 14000), 116 netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 147}), 443), 117 netip.AddrPortFrom(netip.AddrFrom4([4]byte{125, 94, 60, 146}), 80), 118 netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 144, 215}), 80), 119 netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 22}), 80), 120 } 121 } 122 pings := make([]int64, len(c.servers)) 123 wg := sync.WaitGroup{} 124 wg.Add(len(c.servers)) 125 for i := range c.servers { 126 go func(index int) { 127 defer wg.Done() 128 p, err := qualityTest(c.servers[index].String()) 129 if err != nil { 130 pings[index] = 9999 131 return 132 } 133 pings[index] = p 134 }(i) 135 } 136 wg.Wait() 137 sort.Slice(c.servers, func(i, j int) bool { 138 return pings[i] < pings[j] 139 }) 140 if len(c.servers) > 3 { 141 c.servers = c.servers[0 : len(c.servers)/2] // 保留ping值中位数以上的server 142 } 143 } 144 145 // connect 连接到 QQClient.servers 中的服务器 146 func (c *QQClient) connect() error { 147 // init qq servers 148 c.initServerOnce.Do(c.initServers) 149 150 addr := c.servers[c.currServerIndex].String() 151 c.info("connect to server: %v", addr) 152 err := c.TCP.Connect(addr) 153 c.currServerIndex++ 154 if c.currServerIndex == len(c.servers) { 155 c.currServerIndex = 0 156 } 157 if err != nil { 158 c.retryTimes++ 159 if c.retryTimes > len(c.servers) { 160 return errors.New("All servers are unreachable") 161 } 162 c.error("connect server error: %v", err) 163 return err 164 } 165 c.once.Do(func() { 166 c.GroupMessageEvent.Subscribe(func(_ *QQClient, _ *message.GroupMessage) { 167 c.stat.MessageReceived.Add(1) 168 c.stat.LastMessageTime.Store(time.Now().Unix()) 169 }) 170 c.PrivateMessageEvent.Subscribe(func(_ *QQClient, _ *message.PrivateMessage) { 171 c.stat.MessageReceived.Add(1) 172 c.stat.LastMessageTime.Store(time.Now().Unix()) 173 }) 174 c.TempMessageEvent.Subscribe(func(_ *QQClient, _ *TempMessageEvent) { 175 c.stat.MessageReceived.Add(1) 176 c.stat.LastMessageTime.Store(time.Now().Unix()) 177 }) 178 c.onGroupMessageReceipt("internal", func(_ *QQClient, _ *groupMessageReceiptEvent) { 179 c.stat.MessageSent.Add(1) 180 }) 181 go c.netLoop() 182 }) 183 c.retryTimes = 0 184 c.ConnectTime = time.Now() 185 return nil 186 } 187 188 // quickReconnect 快速重连 189 func (c *QQClient) quickReconnect() { 190 c.Disconnect() 191 time.Sleep(time.Millisecond * 200) 192 if err := c.connect(); err != nil { 193 c.error("connect server error: %v", err) 194 c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "quick reconnect failed"}) 195 return 196 } 197 if err := c.registerClient(); err != nil { 198 c.error("register client failed: %v", err) 199 c.Disconnect() 200 c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"}) 201 return 202 } 203 } 204 205 // Disconnect 中断连接, 不释放资源 206 func (c *QQClient) Disconnect() { 207 c.Online.Store(false) 208 c.TCP.Close() 209 } 210 211 // sendAndWait 向服务器发送一个数据包, 并等待返回 212 func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.RequestParams) (any, error) { 213 type T struct { 214 Response any 215 Error error 216 } 217 ch := make(chan T, 1) 218 var p network.RequestParams 219 220 if len(params) != 0 { 221 p = params[0] 222 } 223 224 c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) { 225 ch <- T{ 226 Response: i, 227 Error: err, 228 } 229 }, params: p, dynamic: false}) 230 231 err := c.sendPacket(pkt) 232 if err != nil { 233 c.handlers.Delete(seq) 234 return nil, err 235 } 236 237 retry := 0 238 for { 239 select { 240 case rsp := <-ch: 241 return rsp.Response, rsp.Error 242 case <-time.After(time.Second * 15): 243 retry++ 244 if retry < 2 { 245 _ = c.sendPacket(pkt) 246 continue 247 } 248 c.handlers.Delete(seq) 249 return nil, errors.New("Packet timed out") 250 } 251 } 252 } 253 254 // sendPacket 向服务器发送一个数据包 255 func (c *QQClient) sendPacket(pkt []byte) error { 256 err := c.TCP.Write(pkt) 257 if err != nil { 258 c.stat.PacketLost.Add(1) 259 } else { 260 c.stat.PacketSent.Add(1) 261 } 262 return errors.Wrap(err, "Packet failed to sendPacket") 263 } 264 265 // waitPacket 266 // 等待一个或多个数据包解析, 优先级低于 sendAndWait 267 // 返回终止解析函数 268 func (c *QQClient) waitPacket(cmd string, f func(any, error)) func() { 269 c.waiters.Store(cmd, f) 270 return func() { 271 c.waiters.Delete(cmd) 272 } 273 } 274 275 // waitPacketTimeoutSyncF 276 // 等待一个数据包解析, 优先级低于 sendAndWait 277 func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, filter func(any) bool) (r any, e error) { 278 notifyChan := make(chan bool, 4) 279 defer c.waitPacket(cmd, func(i any, err error) { 280 if filter(i) { 281 r = i 282 e = err 283 notifyChan <- true 284 } 285 })() 286 select { 287 case <-notifyChan: 288 return 289 case <-time.After(timeout): 290 return nil, errors.New("timeout") 291 } 292 } 293 294 // sendAndWaitDynamic 295 // 发送数据包并返回需要解析的 response 296 func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) { 297 ch := make(chan []byte, 1) 298 c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) { ch <- i.([]byte) }, dynamic: true}) 299 err := c.sendPacket(pkt) 300 if err != nil { 301 c.handlers.Delete(seq) 302 return nil, err 303 } 304 select { 305 case rsp := <-ch: 306 return rsp, nil 307 case <-time.After(time.Second * 15): 308 c.handlers.Delete(seq) 309 return nil, errors.New("Packet timed out") 310 } 311 } 312 313 // SendSsoPacket 314 // 发送签名回调包给服务器并获取返回结果供提交 315 func (c *QQClient) SendSsoPacket(cmd string, body []byte) ([]byte, error) { 316 seq, data := c.uniPacket(cmd, body) 317 return c.sendAndWaitDynamic(seq, data) 318 } 319 320 // plannedDisconnect 计划中断线事件 321 func (c *QQClient) plannedDisconnect(_ *network.TCPClient) { 322 c.debug("planned disconnect.") 323 c.stat.DisconnectTimes.Add(1) 324 c.Online.Store(false) 325 } 326 327 // unexpectedDisconnect 非预期断线事件 328 func (c *QQClient) unexpectedDisconnect(_ *network.TCPClient, e error) { 329 c.error("unexpected disconnect: %v", e) 330 c.stat.DisconnectTimes.Add(1) 331 c.Online.Store(false) 332 if err := c.connect(); err != nil { 333 c.error("connect server error: %v", err) 334 c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "connection dropped by server."}) 335 return 336 } 337 if err := c.registerClient(); err != nil { 338 c.error("register client failed: %v", err) 339 c.Disconnect() 340 c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"}) 341 return 342 } 343 } 344 345 // netLoop 通过循环来不停接收数据包 346 func (c *QQClient) netLoop() { 347 errCount := 0 348 for c.alive { 349 l, err := c.TCP.ReadInt32() 350 if err != nil { 351 time.Sleep(time.Millisecond * 500) 352 continue 353 } 354 if l < 4 || l > 1024*1024*10 { // max 10MB 355 c.error("parse incoming packet error: invalid packet length %v", l) 356 errCount++ 357 if errCount > 2 { 358 go c.quickReconnect() 359 } 360 continue 361 } 362 data, _ := c.TCP.ReadBytes(int(l) - 4) 363 resp, err := c.transport.ReadResponse(data) 364 // pkt, err := packets.ParseIncomingPacket(data, c.sig.D2Key) 365 if err != nil { 366 c.error("parse incoming packet error: %v", err) 367 if errors.Is(err, network.ErrSessionExpired) || errors.Is(err, network.ErrPacketDropped) { 368 c.Disconnect() 369 go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "session expired"}) 370 continue 371 } 372 errCount++ 373 if errCount > 2 { 374 go c.quickReconnect() 375 } 376 continue 377 } 378 if resp.EncryptType == network.EncryptTypeEmptyKey { 379 m, err := c.oicq.Unmarshal(resp.Body) 380 if err != nil { 381 c.error("decrypt payload error: %v", err) 382 if errors.Is(err, oicq.ErrUnknownFlag) { 383 go c.quickReconnect() 384 } 385 continue 386 } 387 resp.Body = m.Body 388 } 389 errCount = 0 390 c.debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID) 391 c.stat.PacketReceived.Add(1) 392 pkt := &network.Packet{ 393 SequenceId: uint16(resp.SequenceID), 394 CommandName: resp.CommandName, 395 Payload: resp.Body, 396 } 397 go func(pkt *network.Packet) { 398 defer func() { 399 if pan := recover(); pan != nil { 400 c.error("panic on decoder %v : %v\n%s", pkt.CommandName, pan, debug.Stack()) 401 c.dump("packet decode error: %v - %v", pkt.Payload, pkt.CommandName, pan) 402 } 403 }() 404 405 if decoder, ok := decoders[pkt.CommandName]; ok { 406 // found predefined decoder 407 info, ok := c.handlers.LoadAndDelete(pkt.SequenceId) 408 var decoded any 409 decoded = pkt.Payload 410 if info == nil || !info.dynamic { 411 pkt.Params = info.getParams() 412 decoded, err = decoder(c, pkt) 413 if err != nil { 414 c.debug("decode pkt %v error: %+v", pkt.CommandName, err) 415 } 416 } 417 if ok { 418 info.fun(decoded, err) 419 } else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait 420 f(decoded, err) 421 } 422 } else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok { 423 // does not need decoder 424 f.fun(pkt.Payload, nil) 425 } else { 426 c.debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId) 427 } 428 }(pkt) 429 } 430 }