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