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 }