250 lines
6.9 KiB
Go
250 lines
6.9 KiB
Go
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
|
||
}
|