| | |
| | | using UnityEngine; |
| | | using System; |
| | | using System.Collections; |
| | | using System.Collections.Generic; |
| | | |
| | | #if !UNITY_WEBGL |
| | | using System.Net; |
| | | using System.Net.Sockets; |
| | | using System.Threading; |
| | | #else |
| | | using NativeWebSocket; |
| | | using Cysharp.Threading.Tasks; |
| | | #endif |
| | | |
| | | /// <summary> |
| | | /// 统一的网络Socket类 - 自动适配TCP Socket和WebSocket |
| | | /// TCP Socket: Windows, Mac, iOS, Android等平台 |
| | | /// WebSocket: WebGL/微信小游戏等Web平台 |
| | | /// </summary> |
| | | public class ClientSocket |
| | | { |
| | | GameNetEncode encoder = new GameNetEncode(); |
| | | |
| | | #if !UNITY_WEBGL |
| | | // TCP Socket 实现(非WebGL平台) |
| | | Socket m_Socket; |
| | | public Socket socket { get { return m_Socket; } } |
| | | |
| | | private Thread m_packageThread; |
| | | private byte[] bufferBytes = new byte[4096]; // 4K,单包字节数组缓存 |
| | | private byte[] fragmentBytes; //留包后的内容 |
| | | private long getBytesTotal = 0; //发送的数据总量 |
| | | private long sendBytesTotal = 0; //发送的数据总量 |
| | | private byte[] bufferBytes = new byte[4096]; |
| | | private byte[] fragmentBytes; // TCP分包缓存 |
| | | bool isStopTreading = false; |
| | | #else |
| | | // WebSocket 实现(WebGL平台) |
| | | WebSocket webSocket; |
| | | public WebSocket socket { get { return webSocket; } } |
| | | private byte[] fragmentBytes; // TCP-to-WS网关按TCP缓冲区拆包,需要跨消息重组 |
| | | #endif |
| | | |
| | | public bool connected { get { return m_Socket == null ? false : m_Socket.Connected; } } |
| | | public Action OnDisconnected; |
| | | |
| | | private long getBytesTotal = 0; |
| | | private long sendBytesTotal = 0; |
| | | |
| | | public bool connected |
| | | { |
| | | get |
| | | { |
| | | #if !UNITY_WEBGL |
| | | return m_Socket == null ? false : m_Socket.Connected; |
| | | #else |
| | | return webSocket != null && webSocket.State == WebSocketState.Open; |
| | | #endif |
| | | } |
| | | } |
| | | |
| | | ServerType socketType = ServerType.Main; |
| | | DateTime m_LastPackageTime; |
| | | public DateTime lastPackageTime { get { return m_LastPackageTime; } } |
| | | |
| | | bool isStopTreading = false; |
| | | |
| | | string ip; |
| | | int port; |
| | | Action<bool> onConnected = null; |
| | | Queue<byte[]> sendQueue = new Queue<byte[]>(); |
| | | static byte[] vCmdBytes = new byte[2]; |
| | | #if UNITY_WEBGL |
| | | string webSocketUrl; |
| | | bool webSocketOpened; |
| | | #endif |
| | | |
| | | public ClientSocket(ServerType type) |
| | | { |
| | | this.socketType = type; |
| | | } |
| | | |
| | | #if !UNITY_WEBGL |
| | | // ==================== TCP Socket 实现 ==================== |
| | | |
| | | public void Connect(string _ip, int _port, Action<bool> _onConnected) |
| | | { |
| | | Debug.unityLogger.logEnabled = true; |
| | | try |
| | | { |
| | | ip = _ip; |
| | | port = _port; |
| | | onConnected = _onConnected; |
| | | Debug.Log($"[ClientSocket][Connect] 尝试连接: ip={_ip}, port={_port}"); |
| | | //目前测试到异步两个问题 |
| | | // 1. BeginGetHostAddresses 不明情况下会很久才回调,导致触发超时 |
| | | // 2. 超时的情况下多次尝试登录后,会触发多次OnGetHostAddresses,导致登录异常 |
| | |
| | | ipAddress = ipAddresses[0]; |
| | | #endif |
| | | |
| | | |
| | | Debug.Log($"[ClientSocket][Connect] 解析到ipAddress={ipAddress}, family={ipAddress.AddressFamily}"); |
| | | if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) |
| | | { |
| | | Debug.Log("当前使用的网络: IPV6"); |
| | | Debug.Log("[ClientSocket][Connect] 当前使用的网络: IPV6"); |
| | | m_Socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); |
| | | } |
| | | else |
| | | { |
| | | Debug.Log("当前使用的网络: IPV4"); |
| | | Debug.Log("[ClientSocket][Connect] 当前使用的网络: IPV4"); |
| | | m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
| | | } |
| | | |
| | | var ipEndPoint = new IPEndPoint(ipAddress, port); |
| | | if (ipEndPoint == null) |
| | | { |
| | | Debug.Log("IpEndPoint is null"); |
| | | Debug.LogError("[ClientSocket][Connect] IpEndPoint is null"); |
| | | } |
| | | |
| | | m_Socket.BeginConnect(ipEndPoint, new AsyncCallback(ConnectCallBack), null); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Debug.LogError(e.Message); |
| | | Debug.LogError($"[ClientSocket][Connect] 异常: {e.Message}"); |
| | | } |
| | | |
| | | |
| | |
| | | /// <param name="_result"></param> |
| | | private void ConnectCallBack(IAsyncResult _result) |
| | | { |
| | | Debug.unityLogger.logEnabled = true; |
| | | if (!_result.IsCompleted) |
| | | { |
| | | Debug.Log("链接超时!"); |
| | | Debug.LogError("[ClientSocket][ConnectCallBack] 链接超时!"); |
| | | CloseConnect(); |
| | | if (onConnected != null) |
| | | { |
| | | Debug.LogError("[ClientSocket][ConnectCallBack] onConnected(false) 超时"); |
| | | onConnected(false); |
| | | onConnected = null; |
| | | } |
| | |
| | | { |
| | | if (m_Socket != null && m_Socket.Connected) |
| | | { |
| | | Debug.Log("确认的链接实现"); |
| | | Debug.Log("[ClientSocket][ConnectCallBack] 确认的链接实现"); |
| | | OnConnectSuccess(); |
| | | } |
| | | else |
| | | { |
| | | Debug.LogError("[ClientSocket][ConnectCallBack] m_Socket为null或未连接"); |
| | | if (m_Socket != null) |
| | | { |
| | | m_Socket.Disconnect(true); |
| | |
| | | } |
| | | catch (System.Exception ex) |
| | | { |
| | | Debug.Log(ex); |
| | | Debug.LogError($"[ClientSocket][ConnectCallBack] 异常: {ex}"); |
| | | } |
| | | finally |
| | | { |
| | | if (onConnected != null) |
| | | { |
| | | Debug.Log($"[ClientSocket][ConnectCallBack] onConnected({m_Socket != null && m_Socket.Connected})"); |
| | | onConnected(m_Socket != null && m_Socket.Connected); |
| | | onConnected = null; |
| | | } |
| | |
| | | public void CloseConnect() |
| | | { |
| | | Debug.Log("==== CloseConnect"); |
| | | Debug.unityLogger.logEnabled = true; |
| | | Debug.Log("[ClientSocket][CloseConnect] ==== CloseConnect"); |
| | | try |
| | | { |
| | | isStopTreading = true; |
| | | if (m_packageThread != null) |
| | | { |
| | | m_packageThread.Abort(); |
| | | m_packageThread = null; |
| | | } |
| | | } |
| | | catch (System.Exception ex) |
| | | { |
| | | Debug.Log(ex); |
| | | m_packageThread = null; |
| | | } |
| | | |
| | | try |
| | |
| | | { |
| | | if (m_packageThread != null) |
| | | { |
| | | Debug.LogWarning("[ClientSocket][OnConnectSuccess] m_packageThread已存在,先Abort"); |
| | | m_packageThread.Abort(); |
| | | m_packageThread = null; |
| | | } |
| | | |
| | | fragmentBytes = null; // 清除上次连接残留的碎片缓存 |
| | | m_LastPackageTime = DateTime.Now; |
| | | isStopTreading = false; |
| | | m_packageThread = new Thread(new ThreadStart(ReceiveInfo)); // 启动线程接收信息 |
| | | m_packageThread.IsBackground = true; |
| | | m_packageThread.Start(); |
| | | isStopTreading = false; |
| | | Debug.unityLogger.logEnabled = true; |
| | | Debug.Log("[ClientSocket][OnConnectSuccess] 连接成功,启动接收线程"); |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | /// </summary> |
| | | private void ReceiveInfo() |
| | | { |
| | | Debug.unityLogger.logEnabled = true; |
| | | Debug.Log("[ClientSocket][ReceiveInfo] 接收线程启动"); |
| | | while (!isStopTreading) |
| | | { |
| | | try |
| | |
| | | var shutdown = false; |
| | | if (!m_Socket.Connected) |
| | | { |
| | | Debug.LogWarning("[ClientSocket][ReceiveInfo] m_Socket 已断开"); |
| | | shutdown = true; |
| | | } |
| | | |
| | | if (!shutdown) |
| | | { |
| | | var dataLength = m_Socket.Receive(bufferBytes); |
| | | Debug.Log($"[ClientSocket][ReceiveInfo] 收到数据长度: {dataLength}"); |
| | | if (dataLength <= 0) |
| | | { |
| | | Debug.LogWarning("[ClientSocket][ReceiveInfo] dataLength <= 0,准备断开"); |
| | | shutdown = true; |
| | | } |
| | | else |
| | |
| | | |
| | | if (shutdown) |
| | | { |
| | | Debug.LogWarning("[ClientSocket][ReceiveInfo] shutdown=true,关闭Socket"); |
| | | isStopTreading = true; |
| | | m_Socket.Shutdown(SocketShutdown.Both); |
| | | m_Socket.Close(); |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Debug.Log(e); |
| | | Debug.LogError($"[ClientSocket][ReceiveInfo] 异常: {e}"); |
| | | } |
| | | } |
| | | Debug.Log("[ClientSocket][ReceiveInfo] 接收线程退出"); |
| | | |
| | | } |
| | | |
| | | static byte[] vCmdBytes = new byte[2]; |
| | | /// <summary> |
| | | /// 解析数据包: FFCC+封包长度+封包(封包头+数据)结构体内容 |
| | | /// </summary> |
| | |
| | | if (fragmentBytes != null && fragmentBytes.Length > 0) |
| | | { |
| | | Array.Resize(ref fixBytes, vBytes.Length + fragmentBytes.Length); |
| | | Debug.Log($"[ClientSocket][ReadInfo] 存在fragmentBytes, 长度: {fragmentBytes.Length}"); |
| | | Array.Copy(fragmentBytes, 0, fixBytes, 0, fragmentBytes.Length); |
| | | Array.Copy(vBytes, 0, fixBytes, fragmentBytes.Length, vBytes.Length); |
| | | } |
| | |
| | | Array.Copy(fixBytes, vReadIndex, fragmentBytes, 0, vLeavingLeng); |
| | | break; |
| | | } |
| | | // 打印包头原始字节,便于排查 OverflowException |
| | | byte h0 = fixBytes[vReadIndex], h1 = fixBytes[vReadIndex + 1], |
| | | h2 = fixBytes[vReadIndex + 2], h3 = fixBytes[vReadIndex + 3], |
| | | h4 = fixBytes[vReadIndex + 4], h5 = fixBytes[vReadIndex + 5]; |
| | | vBodyLeng = BitConverter.ToInt32(fixBytes, vReadIndex + 2); |
| | | Debug.Log($"[ClientSocket][ReadInfo] vReadIndex={vReadIndex} vTotalLeng={vTotalLeng} vLeavingLeng={vLeavingLeng} header=[{h0:X2} {h1:X2} {h2:X2} {h3:X2} {h4:X2} {h5:X2}] vBodyLeng={vBodyLeng}"); |
| | | if (vBodyLeng < 0) |
| | | { |
| | | Debug.LogError($"[ClientSocket][ReadInfo] vBodyLeng异常({vBodyLeng}),丢弃剩余数据!fragmentBytes总长={fixBytes.Length} vReadIndex={vReadIndex}"); |
| | | fragmentBytes = null; |
| | | break; |
| | | } |
| | | if (vBodyLeng > vLeavingLeng - 6)// 未完整的包则留包 |
| | | { |
| | | Debug.Log($"[ClientSocket][ReadInfo] 包不完整,留包: vBodyLeng={vBodyLeng} vLeavingLeng={vLeavingLeng}"); |
| | | fragmentBytes = new byte[vLeavingLeng]; |
| | | Array.Copy(fixBytes, vReadIndex, fragmentBytes, 0, vLeavingLeng); |
| | | break; |
| | |
| | | vNetpack = PackageRegedit.TransPack(socketType, cmd, vPackBytes); |
| | | if (vNetpack != null) |
| | | { |
| | | if (Launch.Instance.EnableNetLog) |
| | | { |
| | | Debug.LogFormat("收包:{0}", vNetpack.GetType().Name); |
| | | } |
| | | // if (Launch.Instance.EnableNetLog) |
| | | // { |
| | | // Debug.LogFormat("收包:{0}", vNetpack.GetType().Name); |
| | | // } |
| | | m_LastPackageTime = DateTime.Now; |
| | | GameNetSystem.Instance.PushPackage(vNetpack, this.socketType); |
| | | isRegist = true; |
| | |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 发送信息 |
| | | /// </summary> |
| | | public void SendInfo(GameNetPackBasic protocol) |
| | | { |
| | | if (!connected) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | if (protocol == null) |
| | | { |
| | | Debug.LogError("要发的信息对象为空"); |
| | | return; |
| | | } |
| | | |
| | | if (Launch.Instance.EnableNetLog) |
| | | { |
| | | Debug.LogFormat("发包:{0}", protocol.GetType().Name); |
| | | } |
| | | |
| | | if (protocol.combineBytes == null) |
| | | { |
| | | protocol.WriteToBytes(); |
| | | } |
| | | protocol.CombineDatas(encoder); |
| | | #if UNITY_EDITOR |
| | | NetPkgCtl.RecordPackage(socketType, protocol.vInfoCont, NetPackagetType.Client, protocol.ToString(), FieldPrint.PrintFields(protocol), FieldPrint.PrintFieldsExpand(protocol, true)); |
| | | #endif |
| | | sendBytesTotal += protocol.combineBytes.Length; |
| | | SendBytes(protocol.combineBytes); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 发送信息 |
| | | /// </summary> |
| | | /// <param name="vBytes"></param> |
| | | public void SendInfo(byte[] vBytes) |
| | | { |
| | | if (!connected) |
| | | { |
| | | Debug.LogError("尚未与该后端链接!无法发送信息"); |
| | | return; |
| | | } |
| | | |
| | | if (vBytes == null || vBytes.Length < 2) |
| | | { |
| | | Debug.LogError("要发的信息数据为空或数据不足"); |
| | | return; |
| | | } |
| | | |
| | | vBytes = encoder.BaseXorAdd(vBytes); |
| | | byte[] vFrameHead = new byte[] { 255, 204 }; |
| | | byte[] vMsgBodyLength = BitConverter.GetBytes(vBytes.Length); |
| | | byte[] vTotal = new byte[vBytes.Length + 6]; |
| | | Array.Copy(vFrameHead, 0, vTotal, 0, vFrameHead.Length); |
| | | Array.Copy(vMsgBodyLength, 0, vTotal, 2, vMsgBodyLength.Length); |
| | | Array.Copy(vBytes, 0, vTotal, 6, vBytes.Length); |
| | | |
| | | SendBytes(vTotal); |
| | | } |
| | | |
| | | Queue<byte[]> sendQueue = new Queue<byte[]>(); |
| | | // TCP Socket 的 SendBytes(私有方法,由统一的 SendInfo 调用) |
| | | private void SendBytes(byte[] bytes) |
| | | { |
| | | // 调试日志:输出发送的字节数据 |
| | | // string hexString = "[TCP] SendBytes Length=" + bytes.Length + " Data=" + BitConverter.ToString(bytes, 0, Math.Min(bytes.Length, 32)).Replace("-", " "); |
| | | // if (bytes.Length > 32) hexString += "..."; |
| | | // Debug.Log(hexString); |
| | | |
| | | try |
| | | { |
| | | if (sendQueue.Count > 0) |
| | |
| | | } |
| | | } |
| | | |
| | | public void DispatchMessageQueue() |
| | | { |
| | | // TCP不需要轮询 |
| | | } |
| | | |
| | | #else |
| | | // ==================== WebSocket 实现(WebGL平台)==================== |
| | | |
| | | public async void Connect(string _ip, int _port, Action<bool> _onConnected) |
| | | { |
| | | Debug.unityLogger.logEnabled = true; |
| | | ip = _ip; |
| | | port = _port; |
| | | onConnected = _onConnected; |
| | | |
| | | var scheme = Application.absoluteURL.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ? "wss" : "ws"; |
| | | webSocketUrl = $"{scheme}://{_ip}:{_port}"; |
| | | webSocketOpened = false; |
| | | Debug.Log($"[ClientSocket-WebSocket] 开始连接: {webSocketUrl}, pageUrl={Application.absoluteURL}"); |
| | | |
| | | try |
| | | { |
| | | webSocket = new WebSocket(webSocketUrl); |
| | | |
| | | // 注册WebSocket回调 |
| | | webSocket.OnOpen += OnWebSocketOpen; |
| | | webSocket.OnMessage += OnWebSocketMessage; |
| | | webSocket.OnError += OnWebSocketError; |
| | | webSocket.OnClose += OnWebSocketClose; |
| | | |
| | | await webSocket.Connect(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"[ClientSocket-WebSocket] 连接异常: {ex.Message}"); |
| | | if (onConnected != null) |
| | | { |
| | | onConnected(false); |
| | | onConnected = null; |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void OnWebSocketOpen() |
| | | { |
| | | webSocketOpened = true; |
| | | Debug.Log($"[ClientSocket-WebSocket] 连接成功: {webSocketUrl}"); |
| | | m_LastPackageTime = DateTime.Now; |
| | | |
| | | if (onConnected != null) |
| | | { |
| | | onConnected(true); |
| | | onConnected = null; |
| | | } |
| | | } |
| | | |
| | | private void OnWebSocketMessage(byte[] data) |
| | | { |
| | | try |
| | | { |
| | | getBytesTotal += data.Length; |
| | | |
| | | byte[] fixBytes = data; |
| | | // TCP-to-WS网关按TCP缓冲区大小拆分,需跨消息重组(与TCP ReadInfo逻辑一致) |
| | | if (fragmentBytes != null && fragmentBytes.Length > 0) |
| | | { |
| | | fixBytes = new byte[fragmentBytes.Length + data.Length]; |
| | | Array.Copy(fragmentBytes, 0, fixBytes, 0, fragmentBytes.Length); |
| | | Array.Copy(data, 0, fixBytes, fragmentBytes.Length, data.Length); |
| | | } |
| | | fragmentBytes = null; |
| | | |
| | | int vReadIndex = 0; |
| | | byte[] vPackBytes; |
| | | int vLeavingLeng = 0; |
| | | int vBodyLeng = 0; |
| | | int vTotalLeng = fixBytes.Length; |
| | | GameNetPackBasic vNetpack; |
| | | |
| | | while (vReadIndex < vTotalLeng) |
| | | { |
| | | vLeavingLeng = vTotalLeng - vReadIndex; |
| | | if (vLeavingLeng < 6) |
| | | { |
| | | fragmentBytes = new byte[vLeavingLeng]; |
| | | Array.Copy(fixBytes, vReadIndex, fragmentBytes, 0, vLeavingLeng); |
| | | break; |
| | | } |
| | | |
| | | // 校验FFCC包头,防止数据错位 |
| | | if (fixBytes[vReadIndex] != 0xFF || fixBytes[vReadIndex + 1] != 0xCC) |
| | | { |
| | | Debug.LogError($"[ClientSocket-WebSocket] FFCC包头异常: {fixBytes[vReadIndex]:X2} {fixBytes[vReadIndex + 1]:X2}, 丢弃剩余 {vLeavingLeng} 字节"); |
| | | fragmentBytes = null; |
| | | break; |
| | | } |
| | | |
| | | vBodyLeng = BitConverter.ToInt32(fixBytes, vReadIndex + 2); |
| | | if (vBodyLeng <= 0) |
| | | { |
| | | Debug.LogError($"[ClientSocket-WebSocket] 包体长度非法: {vBodyLeng}, 丢弃"); |
| | | fragmentBytes = null; |
| | | break; |
| | | } |
| | | if (vBodyLeng > vLeavingLeng - 6) |
| | | { |
| | | fragmentBytes = new byte[vLeavingLeng]; |
| | | Array.Copy(fixBytes, vReadIndex, fragmentBytes, 0, vLeavingLeng); |
| | | break; |
| | | } |
| | | |
| | | vPackBytes = new byte[vBodyLeng]; |
| | | Array.Copy(fixBytes, vReadIndex + 6, vPackBytes, 0, vBodyLeng); |
| | | |
| | | vPackBytes = encoder.BaseXorSub(vPackBytes); |
| | | Array.Copy(vPackBytes, 0, vCmdBytes, 0, 2); |
| | | var cmd = (ushort)((ushort)(vCmdBytes[0] << 8) + vCmdBytes[1]); |
| | | bool isRegist = false; |
| | | |
| | | if (PackageRegedit.Contain(cmd)) |
| | | { |
| | | vNetpack = PackageRegedit.TransPack(socketType, cmd, vPackBytes); |
| | | if (vNetpack != null) |
| | | { |
| | | m_LastPackageTime = DateTime.Now; |
| | | GameNetSystem.Instance.PushPackage(vNetpack, socketType); |
| | | isRegist = true; |
| | | } |
| | | } |
| | | |
| | | vReadIndex += 6 + vBodyLeng; |
| | | |
| | | if (!isRegist) |
| | | { |
| | | #if UNITY_EDITOR |
| | | PackageRegedit.TransPack(socketType, cmd, vPackBytes); |
| | | #endif |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"[ClientSocket-WebSocket] 收包异常:{ex}"); |
| | | } |
| | | } |
| | | |
| | | private void OnWebSocketError(string error) |
| | | { |
| | | Debug.LogError($"[ClientSocket-WebSocket] 错误: {error}"); |
| | | if (!webSocketOpened && onConnected != null) |
| | | { |
| | | onConnected(false); |
| | | onConnected = null; |
| | | } |
| | | } |
| | | |
| | | private void OnWebSocketClose(WebSocketCloseCode code) |
| | | { |
| | | Debug.LogError($"[ClientSocket-WebSocket] 连接关闭: code={code}, url={webSocketUrl}, opened={webSocketOpened}"); |
| | | if (!webSocketOpened && onConnected != null) |
| | | { |
| | | onConnected(false); |
| | | onConnected = null; |
| | | } |
| | | OnDisconnected?.Invoke(); |
| | | } |
| | | |
| | | public async void CloseConnect() |
| | | { |
| | | Debug.Log("[ClientSocket-WebSocket] ==== CloseConnect\n" + System.Environment.StackTrace); |
| | | fragmentBytes = null; |
| | | |
| | | if (webSocket != null) |
| | | { |
| | | sendQueue.Clear(); |
| | | await webSocket.Close(); |
| | | webSocket = null; |
| | | } |
| | | } |
| | | |
| | | private bool isSending = false; |
| | | |
| | | private async void SendBytes(byte[] bytes) |
| | | { |
| | | // 调试日志:输出发送的字节数据 |
| | | // string hexString = "[WebSocket] SendBytes Length=" + bytes.Length + " Data=" + BitConverter.ToString(bytes, 0, Math.Min(bytes.Length, 32)).Replace("-", " "); |
| | | // if (bytes.Length > 32) hexString += "..."; |
| | | // Debug.Log(hexString); |
| | | |
| | | if (webSocket == null || webSocket.State != WebSocketState.Open) |
| | | { |
| | | Debug.LogError("[ClientSocket-WebSocket] 未连接,无法发送"); |
| | | return; |
| | | } |
| | | |
| | | // 队列机制 |
| | | lock (sendQueue) |
| | | { |
| | | if (isSending) |
| | | { |
| | | sendQueue.Enqueue(bytes); |
| | | return; |
| | | } |
| | | isSending = true; |
| | | } |
| | | |
| | | try |
| | | { |
| | | await webSocket.Send(bytes); |
| | | |
| | | // 处理队列中的下一个 |
| | | lock (sendQueue) |
| | | { |
| | | if (sendQueue.Count > 0) |
| | | { |
| | | byte[] nextBytes = sendQueue.Dequeue(); |
| | | isSending = false; |
| | | SendBytes(nextBytes); // 递归发送 |
| | | } |
| | | else |
| | | { |
| | | isSending = false; |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"[ClientSocket-WebSocket] 发送异常: {ex.Message}"); |
| | | lock (sendQueue) |
| | | { |
| | | isSending = false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // WebGL需要在Update中轮询消息 |
| | | public void DispatchMessageQueue() |
| | | { |
| | | #if UNITY_EDITOR |
| | | webSocket?.DispatchMessageQueue(); |
| | | #endif |
| | | } |
| | | |
| | | #endif |
| | | |
| | | // ==================== 统一的公共接口(两个平台通用)==================== |
| | | |
| | | /// <summary> |
| | | /// 发送协议包 |
| | | /// </summary> |
| | | public void SendInfo(GameNetPackBasic protocol) |
| | | { |
| | | if (!connected) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | if (protocol == null) |
| | | { |
| | | Debug.LogError("要发的信息对象为空"); |
| | | return; |
| | | } |
| | | |
| | | if (protocol.combineBytes == null) |
| | | { |
| | | protocol.WriteToBytes(); |
| | | } |
| | | protocol.CombineDatas(encoder); |
| | | |
| | | #if UNITY_EDITOR |
| | | NetPkgCtl.RecordPackage(socketType, protocol.vInfoCont, NetPackagetType.Client, |
| | | protocol.ToString(), FieldPrint.PrintFields(protocol), FieldPrint.PrintFieldsExpand(protocol, true)); |
| | | #endif |
| | | |
| | | // 调试日志:查看 combineBytes 的内容 |
| | | string hexString = "[SendInfo] Protocol=" + protocol.ToString() + " combineBytes.Length=" + protocol.combineBytes.Length + |
| | | " Data=" + BitConverter.ToString(protocol.combineBytes, 0, Math.Min(protocol.combineBytes.Length, 32)).Replace("-", " "); |
| | | if (protocol.combineBytes.Length > 32) hexString += "..."; |
| | | Debug.Log(hexString); |
| | | |
| | | sendBytesTotal += protocol.combineBytes.Length; |
| | | SendBytes(protocol.combineBytes); |
| | | } |
| | | |
| | | #if UNITY_EDITOR |
| | | /// <summary> |
| | | /// 发送原始字节数据 |
| | | /// </summary> |
| | | public void SendInfo(byte[] vBytes) |
| | | { |
| | | if (!connected) |
| | | { |
| | | Debug.LogError("尚未与该后端链接!无法发送信息"); |
| | | return; |
| | | } |
| | | |
| | | if (vBytes == null || vBytes.Length < 2) |
| | | { |
| | | Debug.LogError("要发的信息数据为空或数据不足"); |
| | | return; |
| | | } |
| | | |
| | | // 加密并组装包头 |
| | | vBytes = encoder.BaseXorAdd(vBytes); |
| | | byte[] vFrameHead = new byte[] { 255, 204 }; |
| | | byte[] vMsgBodyLength = BitConverter.GetBytes(vBytes.Length); |
| | | byte[] vTotal = new byte[vBytes.Length + 6]; |
| | | Array.Copy(vFrameHead, 0, vTotal, 0, vFrameHead.Length); |
| | | Array.Copy(vMsgBodyLength, 0, vTotal, 2, vMsgBodyLength.Length); |
| | | Array.Copy(vBytes, 0, vTotal, 6, vBytes.Length); |
| | | |
| | | SendBytes(vTotal); |
| | | } |
| | | #endif |
| | | } |