using Cysharp.Threading.Tasks;
using NativeWebSocket;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
///
/// WebSocket 网络服务 - 用于小游戏平台
///
public class WebSocketNetworkService : INetworkService
{
private WebSocket webSocket;
private NetworkState _state = NetworkState.Disconnected;
private string currentUrl;
// 统计信息(与 ClientSocket 对齐)
private long getBytesTotal = 0; // 接收的数据总量
private long sendBytesTotal = 0; // 发送的数据总量
private DateTime m_LastPackageTime = DateTime.MinValue;
///
/// 最后收包时间(实现接口)
///
public DateTime LastPackageTime { get { return m_LastPackageTime; } }
// 发送队列(与 ClientSocket 对齐)
private Queue sendQueue = new Queue();
private bool isSending = false;
// 编解码器(与 ClientSocket 对齐)
private GameNetEncode encoder = new GameNetEncode();
// 服务器类型(与 ClientSocket 对齐)
private ServerType socketType = ServerType.Main;
///
/// 构造函数(与 ClientSocket 对齐)
///
public WebSocketNetworkService(ServerType type)
{
this.socketType = type;
}
public NetworkState State
{
get => _state;
private set
{
if (_state != value)
{
_state = value;
OnStateChanged?.Invoke(value);
}
}
}
public bool IsConnected => State == NetworkState.Connected;
public event Action OnStateChanged;
///
/// 连接到 WebSocket 服务器
///
public async UniTask ConnectAsync(string url)
{
if (IsConnected)
{
Debug.LogWarning($"[WebSocket] 已经连接到 {currentUrl}");
return true;
}
try
{
State = NetworkState.Connecting;
currentUrl = url;
Debug.Log($"[WebSocket] 开始连接: {url}");
// 创建 WebSocket 连接(不传 headers,使用默认行为)
webSocket = new WebSocket(url);
// 注册事件监听
webSocket.OnOpen += OnWebSocketOpen;
webSocket.OnMessage += OnWebSocketMessage;
webSocket.OnError += OnWebSocketError;
webSocket.OnClose += OnWebSocketClose;
// 开始连接
Debug.Log($"[WebSocket] 调用 Connect()...");
// 启动连接任务(不等待它完成,因为它会一直运行到连接关闭)
var connectTask = webSocket.Connect();
// 等待 OnOpen 触发(最多5秒)
var timeout = DateTime.Now.AddSeconds(5);
while (State != NetworkState.Connected && DateTime.Now < timeout)
{
await UniTask.Yield();
}
Debug.Log($"[WebSocket] 等待结束,当前状态: {State}, WebSocket.State: {webSocket.State}");
if (IsConnected)
{
Debug.Log("[WebSocket] ✓ 连接成功");
return true;
}
else
{
Debug.LogError($"[WebSocket] ✗ 连接超时或失败,State={State}, WebSocket.State={webSocket?.State}");
return false;
}
}
catch (Exception ex)
{
Debug.LogError($"[WebSocket] 连接异常: {ex.Message}\n{ex.StackTrace}");
return false;
}
}
///
/// 断开连接(异步)
///
public async UniTask DisconnectAsync()
{
Debug.LogWarning($"[WebSocket] DisconnectAsync 被调用! State={State}");
Debug.LogWarning($"[WebSocket] 调用堆栈:\n{System.Environment.StackTrace}");
if (webSocket != null)
{
// 清空发送队列
lock (sendQueue)
{
sendQueue.Clear();
isSending = false;
}
await webSocket.Close();
webSocket = null;
}
State = NetworkState.Disconnected;
}
///
/// 断开连接(同步,适配 ClientSocket.CloseConnect)
///
public void Disconnect()
{
Debug.LogWarning($"[WebSocket] Disconnect 被调用! State={State}");
Debug.LogWarning($"[WebSocket] 调用堆栈:\n{System.Environment.StackTrace}");
DisconnectAsync().Forget();
}
///
/// 发送协议包(与 ClientSocket.SendInfo 完全对齐)
///
public void SendInfo(GameNetPackBasic protocol)
{
if (!IsConnected)
{
Debug.LogError("[WebSocket] 未连接,无法发送协议包");
return;
}
if (protocol == null)
{
Debug.LogError("[WebSocket] 要发的信息对象为空");
return;
}
try
{
// 1. 准备协议数据
if (protocol.combineBytes == null)
{
protocol.WriteToBytes();
}
// 2. 组合数据(与 ClientSocket.SendInfo 一致)
protocol.CombineDatas(encoder);
#if UNITY_EDITOR
// 3. 记录包信息到 NetPackageWindow(与 ClientSocket 对齐)
NetPkgCtl.RecordPackage(socketType, protocol.vInfoCont, NetPackagetType.Client,
protocol.ToString(), FieldPrint.PrintFields(protocol), FieldPrint.PrintFieldsExpand(protocol, true));
#endif
byte[] data = protocol.dataBytes;
// 4. 加密
byte[] encryptedData = encoder.BaseXorAdd(data);
// 5. 添加包头(6字节:0xFF 0xCC + 4字节长度小端序,与 ClientSocket 一致)
int bodyLength = encryptedData.Length;
byte[] sendData = new byte[bodyLength + 6];
sendData[0] = 0xFF; // 与 ClientSocket 一致
sendData[1] = 0xCC; // 与 ClientSocket 一致
// 使用 BitConverter 小端序(与 ClientSocket 一致)
byte[] lengthBytes = BitConverter.GetBytes(bodyLength);
Array.Copy(lengthBytes, 0, sendData, 2, 4);
Array.Copy(encryptedData, 0, sendData, 6, bodyLength);
// 6. 更新统计(与 ClientSocket 对齐)
sendBytesTotal += sendData.Length;
// 7. 发送(异步)
SendAsync(sendData).Forget();
}
catch (Exception ex)
{
Debug.LogError($"[WebSocket] 发送协议包异常: {ex.Message}\n{ex.StackTrace}");
}
}
///
/// 发送二进制数据(同步重载,适配 ClientSocket.SendInfo)
///
public void SendInfo(byte[] data)
{
SendAsync(data).Forget();
}
///
/// 发送二进制数据(带队列机制,与 ClientSocket 对齐)
///
public async UniTask SendAsync(byte[] data)
{
if (!IsConnected)
{
Debug.LogError("[WebSocket] 未连接,无法发送数据");
return;
}
if (data == null || data.Length == 0)
{
Debug.LogError("[WebSocket] 发送数据为空");
return;
}
// 检查消息大小(最大 64KB)
if (data.Length > 65536)
{
Debug.LogError($"[WebSocket] 消息过大: {data.Length} bytes(最大 64KB)");
return;
}
// 队列机制:如果正在发送,加入队列
lock (sendQueue)
{
if (isSending)
{
sendQueue.Enqueue(data);
Debug.Log($"[WebSocket] 数据加入发送队列,队列长度: {sendQueue.Count}");
return;
}
else
{
isSending = true;
}
}
// 发送数据
await SendBytesInternal(data);
}
///
/// 内部发送方法(处理队列)
///
private async UniTask SendBytesInternal(byte[] data)
{
try
{
await webSocket.Send(data);
sendBytesTotal += data.Length;
Debug.Log($"[WebSocket] 发送成功: {data.Length} bytes,总发送: {sendBytesTotal} bytes");
// 处理队列中的下一个消息
byte[] nextData = null;
lock (sendQueue)
{
if (sendQueue.Count > 0)
{
nextData = sendQueue.Dequeue();
}
else
{
isSending = false;
}
}
if (nextData != null)
{
await SendBytesInternal(nextData);
}
}
catch (Exception ex)
{
Debug.LogError($"[WebSocket] 发送失败: {ex.Message}");
lock (sendQueue)
{
isSending = false;
}
}
}
///
/// WebSocket 连接成功回调
///
private void OnWebSocketOpen()
{
Debug.Log($"[WebSocket] ✓✓✓ OnOpen 事件触发 - 连接成功: {currentUrl}");
Debug.Log($"[WebSocket] WebSocket.State: {webSocket?.State}");
State = NetworkState.Connected;
Debug.Log($"[WebSocket] 当前状态已更新为: {State}");
}
///
/// WebSocket 消息接收回调(与 ClientSocket.ReadInfo 对齐)
///
private void OnWebSocketMessage(byte[] data)
{
// 完整的包处理流程(与 ClientSocket.ReadInfo 对齐)
try
{
if (data == null || data.Length < 6)
{
Debug.LogError($"[WebSocket] 收到无效数据包: {data?.Length ?? 0} bytes");
return;
}
// 更新统计信息
getBytesTotal += data.Length;
Debug.Log($"[WebSocket] 收到原始消息: {data.Length} bytes");
// 检查包头(0xFF 0xCC,与 ClientSocket 一致)
if (data[0] != 0xFF || data[1] != 0xCC)
{
Debug.LogError($"[WebSocket] 包头错误: 0x{data[0]:X2} 0x{data[1]:X2},期望 0xFF 0xCC");
return;
}
// 读取包体长度(小端序,与 ClientSocket 一致)
int bodyLength = BitConverter.ToInt32(data, 2);
if (data.Length < bodyLength + 6)
{
Debug.LogError($"[WebSocket] 包长度不匹配: 声明 {bodyLength + 6}, 实际 {data.Length}");
return;
}
// 提取加密的包体(跳过6字节包头)
byte[] encryptedBody = new byte[bodyLength];
Array.Copy(data, 6, encryptedBody, 0, bodyLength);
// 解密包体(与 ClientSocket.ReadInfo 一致)
byte[] decryptedData = encoder.BaseXorSub(encryptedBody);
// 解析 CMD(前2字节)
byte[] cmdBytes = new byte[2];
Array.Copy(decryptedData, 0, cmdBytes, 0, 2);
var cmd = (ushort)((ushort)(cmdBytes[0] << 8) + cmdBytes[1]);
Debug.Log($"[WebSocket] 解密成功,CMD=0x{cmd:X4}");
bool isRegist = false;
// 转换协议包(与 ClientSocket.ReadInfo 一致)
if (PackageRegedit.Contain(cmd))
{
GameNetPackBasic pack = PackageRegedit.TransPack(socketType, cmd, decryptedData);
if (pack != null)
{
Debug.Log($"[WebSocket] 收到协议: {pack.GetType().Name}");
m_LastPackageTime = DateTime.Now;
// 直接推送到 GameNetSystem(与 ClientSocket 一致)
GameNetSystem.Instance.PushPackage(pack, socketType);
isRegist = true;
}
}
// 未注册封包处理(与 ClientSocket 一致)
if (!isRegist)
{
#if UNITY_EDITOR
PackageRegedit.TransPack(socketType, cmd, decryptedData);
#endif
}
}
catch (Exception ex)
{
Debug.LogError($"[WebSocket] 消息处理异常: {ex.Message}\n{ex.StackTrace}");
}
}
///
/// WebSocket 错误回调
///
private void OnWebSocketError(string error)
{
Debug.LogError($"[WebSocket] ✗ OnError 事件触发: {error}");
Debug.LogError($"[WebSocket] 当前状态: {State}, WebSocket.State: {webSocket?.State}");
}
///
/// WebSocket 关闭回调(不自动重连,与 ClientSocket 一致)
///
private void OnWebSocketClose(WebSocketCloseCode code)
{
Debug.LogError($"[WebSocket] ⊗ OnClose 事件触发!");
Debug.LogError($"[WebSocket] 关闭代码: {code} ({(int)code})");
Debug.LogError($"[WebSocket] 之前状态: {State}\n");
// 判断是谁关闭的
if (code == WebSocketCloseCode.Normal)
{
Debug.LogWarning("[WebSocket] → 正常关闭(客户端主动)");
}
else if (code == WebSocketCloseCode.Abnormal)
{
Debug.LogError("[WebSocket] → 异常关闭(连接丢失或服务器断开)");
}
else
{
Debug.LogError($"[WebSocket] → 其他原因关闭: {code}");
}
State = NetworkState.Disconnected;
// 注意:不自动重连,与 ClientSocket 一致,由 GameNetSystem 控制重连逻辑
Debug.Log("[WebSocket] 连接已断开,等待 GameNetSystem 处理");
}
///
/// 获取接收统计信息
///
public long GetBytesTotal() => getBytesTotal;
///
/// 获取发送统计信息
///
public long GetSendBytesTotal() => sendBytesTotal;
///
/// 获取发送队列长度
///
public int GetSendQueueCount()
{
lock (sendQueue)
{
return sendQueue.Count;
}
}
///
/// 每帧处理 WebSocket 消息(必须在 MonoBehaviour 的 Update 中调用)
///
public void Update()
{
#if !UNITY_WEBGL || UNITY_EDITOR
webSocket?.DispatchMessageQueue();
#endif
}
}