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  }