package mmtls import ( "bufio" "errors" "fmt" "io" "net" "strconv" "time" "xiawan/wx/clientsdk/baseutils" ) // MMLongConnect 链接MMtls服务器 func MMLongConnect(mmInfo *MMInfo) error { strPort := strconv.Itoa(int(mmInfo.LONGPort)) serverAddr := mmInfo.LongHost + ":" + strPort fmt.Println("MMLongConnect serverAddr: ", serverAddr) // mminfoDialer := "0" // if mmInfo.Dialer != nil { // mminfoDialer = "1" // } // 打印 mmInfo.Dialer // fmt.Println("MMLongConnect mmInfo.Dialer: ", mminfoDialer) // 从MMInfo获取重试参数,如果未设置则使用默认值 maxRetries := mmInfo.LongConnRetryTimes if maxRetries <= 0 { maxRetries = 30 } retryInterval := time.Duration(mmInfo.LongConnRetryInterval) * time.Millisecond if retryInterval <= 0 { retryInterval = 500 * time.Millisecond } connTimeout := time.Duration(mmInfo.LongConnTimeout) * time.Second if connTimeout <= 0 { connTimeout = 15 * time.Second } // 调试:打印 AllowDirectOnProxyFail 的值 fmt.Printf("MMLongConnect AllowDirectOnProxyFail: %v\n", mmInfo.AllowDirectOnProxyFail) // 定义 var conn net.Conn var err error proxyFailed := false // 标记代理是否已经失败过 for i := 0; i < maxRetries; i++ { // 如果有代理且代理没有被标记为失败,尝试代理连接 if mmInfo.Dialer != nil && !proxyFailed { // 使用代理连接 conn, err = mmInfo.Dialer.Dial("tcp4", serverAddr) if err != nil { baseutils.PrintLog(fmt.Sprintf("MMLongConnect with proxy attempt %d/%d failed: %s", i+1, maxRetries, err.Error())) // 检查是否允许降级到直连 if mmInfo.AllowDirectOnProxyFail { baseutils.PrintLog("MMLongConnect: Proxy failed, falling back to direct connection") proxyFailed = true // 标记代理失败,后续使用直连 // 不等待,立即尝试直连 } else { // 不允许直连,等待后重试代理 time.Sleep(retryInterval) } continue } // 设置连接超时 if err := conn.SetDeadline(time.Now().Add(connTimeout)); err != nil { baseutils.PrintLog(fmt.Sprintf("MMLongConnect SetDeadline failed: %s", err.Error())) conn.Close() time.Sleep(retryInterval) continue } mmInfo.Conn = conn mmInfo.reader = bufio.NewReader(conn) fmt.Println("MMLongConnect with proxy success!") return nil } // 没有使用代理或代理已失败 - 直连 conn, err = net.DialTimeout("tcp4", serverAddr, connTimeout) if err != nil { baseutils.PrintLog(fmt.Sprintf("MMLongConnect direct attempt %d/%d failed: %s", i+1, maxRetries, err.Error())) // 等待重试 time.Sleep(retryInterval) continue } // 设置连接超时 if err := conn.SetDeadline(time.Now().Add(connTimeout)); err != nil { baseutils.PrintLog(fmt.Sprintf("MMLongConnect SetDeadline failed: %s", err.Error())) conn.Close() time.Sleep(retryInterval) continue } mmInfo.Conn = conn mmInfo.reader = bufio.NewReader(conn) if proxyFailed { fmt.Println("MMLongConnect direct success (after proxy fallback)!") } else { fmt.Println("MMLongConnect direct success!") } return nil } if err != nil { return fmt.Errorf("MMLongConnect failed after %d attempts: %w", maxRetries, err) } return errors.New("MMLongConnect failed: unknown error") } // MMTCPSendData MMTCPSendData 长链接发送数据 func MMTCPSendData(mmInfo *MMInfo, data []byte) error { // 连接服务器 if mmInfo.Conn == nil { // 提前设置好长链接Host Port err := MMLongConnect(mmInfo) if err != nil { return err } } // 设置写超时 writeTimeout := time.Duration(mmInfo.LongConnTimeout) * time.Second if writeTimeout <= 0 { writeTimeout = 15 * time.Second } if err := mmInfo.Conn.SetWriteDeadline(time.Now().Add(writeTimeout)); err != nil { return fmt.Errorf("SetWriteDeadline failed: %w", err) } // 发送数据 length, err := mmInfo.Conn.Write(data) // 判断是否出错 if err != nil { return fmt.Errorf("write failed: %w", err) } // 判断数据是否发送完毕 if length != len(data) { return errors.New("MMTCPSendData err: length != len(data)") } return nil } // MMTCPRecvItems 循环接收长链接数据 // Deprecated func MMTCPRecvItems(mmInfo *MMInfo) ([]*PackItem, error) { // 设置读超时 readTimeout := time.Duration(mmInfo.LongConnReadTimeout) * time.Second if readTimeout <= 0 { readTimeout = time.Duration(mmInfo.LongConnTimeout) * time.Second if readTimeout <= 0 { readTimeout = 15 * time.Second } } if err := mmInfo.Conn.SetReadDeadline(time.Now().Add(readTimeout)); err != nil { return nil, fmt.Errorf("SetReadDeadline failed: %w", err) } // 接收返回数据 recvBuf := make([]byte, 10240) recvLen, err := mmInfo.Conn.Read(recvBuf) if err != nil { return nil, err } retData := recvBuf[0:recvLen] retItems, err := ParserMMtlsResponseData(retData) if err != nil { return nil, err } return retItems, nil } // MMTCPRecvOneItem func MMTCPRecvOneItem(mmInfo *MMInfo) (*PackItem, error) { if mmInfo == nil || mmInfo.Conn == nil || mmInfo.reader == nil { return nil, errors.New("mmInfo.Conn or mmInfo.reader is nil") } // 设置读超时 readTimeout := time.Duration(mmInfo.LongConnReadTimeout) * time.Second if readTimeout <= 0 { readTimeout = time.Duration(mmInfo.LongConnTimeout) * time.Second if readTimeout <= 0 { readTimeout = 15 * time.Second } } if err := mmInfo.Conn.SetReadDeadline(time.Now().Add(readTimeout)); err != nil { return nil, fmt.Errorf("SetReadDeadline failed: %w", err) } // 读取头部数据 recordHeadData := make([]byte, 5) if _, err := io.ReadFull(mmInfo.reader, recordHeadData); err != nil { return nil, fmt.Errorf("read header failed: %w", err) } // 读取Content recordHead := RecordHeadDeSerialize(recordHeadData) bodyData := make([]byte, recordHead.Size) // 重新设置读超时(针对body) if err := mmInfo.Conn.SetReadDeadline(time.Now().Add(readTimeout)); err != nil { return nil, fmt.Errorf("SetReadDeadline for body failed: %w", err) } if _, err := io.ReadFull(mmInfo.reader, bodyData); err != nil { return nil, fmt.Errorf("read body failed: %w", err) } return &PackItem{ RecordHead: recordHeadData, PackData: bodyData, }, nil } // MMTCPSendReq 长链接发送请求 func MMTCPSendReq(mmInfo *MMInfo, opCode uint32, data []byte) error { sendData, err := MMLongPackData(mmInfo, opCode, data) if err != nil { return err } // 发送数据 err = MMTCPSendData(mmInfo, sendData) if err != nil { return err } return nil } // MMTCPRecvData 接受MMTLS信息 func MMTCPRecvData(mmInfo *MMInfo) (*LongRecvInfo, error) { // 接收响应数据 recvItem, err := MMTCPRecvOneItem(mmInfo) if err != nil { fmt.Println("MMTCPRecvData err", err.Error()) // log.("接收mmtls长链接响应失败", err.Error()) return nil, err } // 解包响应数据 recvInfo, err := MMLongUnPackData(mmInfo, recvItem) if err != nil { fmt.Println("MMTCPRecvData err", err.Error()) //log.Info("解包响应数据失败", err.Error()) return nil, err } return recvInfo, nil }