2026-02-17 13:06:23 +08:00
|
|
|
|
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)
|
2026-02-26 10:44:13 +08:00
|
|
|
|
// mminfoDialer := "0"
|
|
|
|
|
|
// if mmInfo.Dialer != nil {
|
|
|
|
|
|
// mminfoDialer = "1"
|
|
|
|
|
|
// }
|
2026-02-17 13:06:23 +08:00
|
|
|
|
// 打印 mmInfo.Dialer
|
2026-02-26 10:44:13 +08:00
|
|
|
|
// 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)
|
|
|
|
|
|
|
2026-02-17 13:06:23 +08:00
|
|
|
|
// 定义
|
|
|
|
|
|
var conn net.Conn
|
|
|
|
|
|
var err error
|
2026-02-26 10:44:13 +08:00
|
|
|
|
proxyFailed := false // 标记代理是否已经失败过
|
|
|
|
|
|
|
|
|
|
|
|
for i := 0; i < maxRetries; i++ {
|
|
|
|
|
|
// 如果有代理且代理没有被标记为失败,尝试代理连接
|
|
|
|
|
|
if mmInfo.Dialer != nil && !proxyFailed {
|
|
|
|
|
|
// 使用代理连接
|
2026-02-17 13:06:23 +08:00
|
|
|
|
conn, err = mmInfo.Dialer.Dial("tcp4", serverAddr)
|
|
|
|
|
|
if err != nil {
|
2026-02-26 10:44:13 +08:00
|
|
|
|
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)
|
2026-02-17 13:06:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
mmInfo.Conn = conn
|
|
|
|
|
|
mmInfo.reader = bufio.NewReader(conn)
|
2026-02-26 10:44:13 +08:00
|
|
|
|
fmt.Println("MMLongConnect with proxy success!")
|
2026-02-17 13:06:23 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 10:44:13 +08:00
|
|
|
|
// 没有使用代理或代理已失败 - 直连
|
|
|
|
|
|
conn, err = net.DialTimeout("tcp4", serverAddr, connTimeout)
|
2026-02-17 13:06:23 +08:00
|
|
|
|
if err != nil {
|
2026-02-26 10:44:13 +08:00
|
|
|
|
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)
|
2026-02-17 13:06:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
mmInfo.Conn = conn
|
|
|
|
|
|
mmInfo.reader = bufio.NewReader(conn)
|
2026-02-26 10:44:13 +08:00
|
|
|
|
if proxyFailed {
|
|
|
|
|
|
fmt.Println("MMLongConnect direct success (after proxy fallback)!")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
fmt.Println("MMLongConnect direct success!")
|
|
|
|
|
|
}
|
2026-02-17 13:06:23 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
if err != nil {
|
2026-02-26 10:44:13 +08:00
|
|
|
|
return fmt.Errorf("MMLongConnect failed after %d attempts: %w", maxRetries, err)
|
2026-02-17 13:06:23 +08:00
|
|
|
|
}
|
2026-02-26 10:44:13 +08:00
|
|
|
|
return errors.New("MMLongConnect failed: unknown error")
|
2026-02-17 13:06:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MMTCPSendData MMTCPSendData 长链接发送数据
|
|
|
|
|
|
func MMTCPSendData(mmInfo *MMInfo, data []byte) error {
|
|
|
|
|
|
// 连接服务器
|
|
|
|
|
|
if mmInfo.Conn == nil {
|
|
|
|
|
|
// 提前设置好长链接Host Port
|
|
|
|
|
|
err := MMLongConnect(mmInfo)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 10:44:13 +08:00
|
|
|
|
// 设置写超时
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-17 13:06:23 +08:00
|
|
|
|
// 发送数据
|
|
|
|
|
|
length, err := mmInfo.Conn.Write(data)
|
|
|
|
|
|
// 判断是否出错
|
|
|
|
|
|
if err != nil {
|
2026-02-26 10:44:13 +08:00
|
|
|
|
return fmt.Errorf("write failed: %w", err)
|
2026-02-17 13:06:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 判断数据是否发送完毕
|
|
|
|
|
|
if length != len(data) {
|
|
|
|
|
|
return errors.New("MMTCPSendData err: length != len(data)")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MMTCPRecvItems 循环接收长链接数据
|
|
|
|
|
|
// Deprecated
|
|
|
|
|
|
func MMTCPRecvItems(mmInfo *MMInfo) ([]*PackItem, error) {
|
2026-02-26 10:44:13 +08:00
|
|
|
|
// 设置读超时
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-17 13:06:23 +08:00
|
|
|
|
// 接收返回数据
|
|
|
|
|
|
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")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 10:44:13 +08:00
|
|
|
|
// 设置读超时
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-17 13:06:23 +08:00
|
|
|
|
// 读取头部数据
|
|
|
|
|
|
recordHeadData := make([]byte, 5)
|
|
|
|
|
|
if _, err := io.ReadFull(mmInfo.reader, recordHeadData); err != nil {
|
2026-02-26 10:44:13 +08:00
|
|
|
|
return nil, fmt.Errorf("read header failed: %w", err)
|
2026-02-17 13:06:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 读取Content
|
|
|
|
|
|
recordHead := RecordHeadDeSerialize(recordHeadData)
|
|
|
|
|
|
bodyData := make([]byte, recordHead.Size)
|
2026-02-26 10:44:13 +08:00
|
|
|
|
|
|
|
|
|
|
// 重新设置读超时(针对body)
|
|
|
|
|
|
if err := mmInfo.Conn.SetReadDeadline(time.Now().Add(readTimeout)); err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("SetReadDeadline for body failed: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-17 13:06:23 +08:00
|
|
|
|
if _, err := io.ReadFull(mmInfo.reader, bodyData); err != nil {
|
2026-02-26 10:44:13 +08:00
|
|
|
|
return nil, fmt.Errorf("read body failed: %w", err)
|
2026-02-17 13:06:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|