yyl
2026-02-06 18e418ca0b5354d464f242be08958b90ee8d779a
TCP/WEBSOCKET SUPPORT
7个文件已删除
3个文件已修改
1342 ■■■■ 已修改文件
Main/Core/NetworkPackage/ClientSocketAdapter.cs 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientSocketAdapter.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/GameNetSystem.cs 179 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/INetworkService.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/INetworkService.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/Network.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/Socket/ClientSocket.cs 397 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/WebSocketNetworkService.cs 466 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/WebSocketNetworkService.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientSocketAdapter.cs
File was deleted
Main/Core/NetworkPackage/ClientSocketAdapter.cs.meta
File was deleted
Main/Core/NetworkPackage/GameNetSystem.cs
@@ -2,24 +2,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
public enum NetworkType
{
    TCP,        // 使用原有的 TCP Socket
    WebSocket   // 使用 WebSocket(小游戏平台)
}
public class GameNetSystem : Singleton<GameNetSystem>
{
    //限制客户端的下一个包是登录包C0101_tagCPlayerLogin,如果不是登录包不允许发送
    bool waitLogin = false; //等待发送登录包,如果有其他包直接屏蔽,避免断线重连的情况发了攻击包之类的
    //等待服务端0403的包后才能发其他的功能包,只有C0123_tagCClientPackVersion 和 C0101_tagCPlayerLogin 可以发送  
    bool waitLoginMap = false;
    // 网络服务接口(统一支持 TCP 和 WebSocket)
    private INetworkService networkService;
    bool waitLoginMap = false;
    NetUpdateBehaviour m_NetUpdateBehaviour;
    NeverConnectState neverConnectState;
    AccountLoginState accountLoginState;
@@ -84,26 +74,12 @@
        }
    }
    private ClientSocket mainSocket;  // 保留兼容,但不再直接使用
    public bool mainSocketConnected
    {
        get { return networkService?.IsConnected ?? false; }
    }
    private ClientSocket mainSocket;
    public bool mainSocketConnected { get { return mainSocket == null ? false : mainSocket.connected; } }
    public float timeSinceMainSocketLastProtocol
    {
        get
        {
            if (networkService == null)
                return Time.time;
            var lastTime = networkService.LastPackageTime;
            if (lastTime == DateTime.MinValue)
                return Time.time; // 从未收包
            return (float)(DateTime.Now - lastTime).TotalSeconds;
        }
        get { return mainSocket == null ? Time.time : (float)(DateTime.Now - mainSocket.lastPackageTime).TotalSeconds; }
    }
@@ -131,9 +107,9 @@
    {
        try
        {
            if (networkService != null && networkService.IsConnected)
            if (mainSocketConnected)
            {
                networkService.Disconnect();
                mainSocket.CloseConnect();
            }
        }
        catch (System.Exception ex)
@@ -141,98 +117,22 @@
            Debug.Log(ex);
        }
        mainProtocolQueue.Clear();
        // 根据 Main.CurrentNetworkType 选择网络类型
        Debug.Log($"[GameNetSystem] 连接服务器 {ip}:{port},使用{(Main.CurrentNetworkType == NetworkType.WebSocket ? "WebSocket" : "TCP")}");
        if (Main.CurrentNetworkType == NetworkType.WebSocket)
        {
            // 使用 WebSocket
            ConnectWithWebSocket(ip, port, onConnected);
        }
        else
        {
            // 使用 TCP Socket(通过适配器)
            ConnectWithTCP(ip, port, onConnected);
        }
    }
    private async void ConnectWithTCP(string ip, int port, Action<bool> onConnected)
    {
        try
        {
            // 创建 TCP Socket 适配器(不修改 ClientSocket)
            networkService = new ClientSocketAdapter(ServerType.Main);
            // 订阅状态事件
            networkService.OnStateChanged += OnNetworkStateChanged;
            // 连接
            string url = $"{ip}:{port}";
            Debug.Log($"[GameNetSystem] TCP 开始连接: {url}");
            bool success = await networkService.ConnectAsync(url);
            Debug.Log($"[GameNetSystem] TCP 连接{(success ? "成功" : "失败")}: {url}");
            // 兼容:保留 mainSocket 引用(但通过 networkService 访问)
            mainSocket = (networkService as ClientSocketAdapter)?.GetClientSocket();
            if (onConnected != null)
            {
                onConnected(success);
            }
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"[GameNetSystem] TCP 连接异常: {ex.Message}");
            if (onConnected != null)
            {
                onConnected(false);
            }
        }
    }
    private async void ConnectWithWebSocket(string ip, int port, Action<bool> onConnected)
    {
        try
        {
            // 创建 WebSocket 服务(传入 ServerType,与 ClientSocket 一致)
            networkService = new WebSocketNetworkService(ServerType.Main);
            // 订阅状态事件
            networkService.OnStateChanged += OnNetworkStateChanged;
            string url = $"ws://{ip}:{port}";
            Debug.Log($"[GameNetSystem] WebSocket 开始连接: {url}");
            bool success = await networkService.ConnectAsync(url);
            Debug.Log($"[GameNetSystem] WebSocket 连接{(success ? "成功" : "失败")}: {url}");
            if (onConnected != null)
            {
                onConnected(success);
            }
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"[GameNetSystem] WebSocket 连接异常: {ex.Message}");
            if (onConnected != null)
            {
                onConnected(false);
            }
        }
    }
    private void OnNetworkStateChanged(NetworkState state)
    {
        Debug.Log($"[GameNetSystem] 网络状态变化: {state}");
        // 断开连接时触发断线处理
        if (state == NetworkState.Disconnected || state == NetworkState.Closed)
        mainSocket = new ClientSocket(ServerType.Main);
        //  websocket的断开链接需要处理一下
        mainSocket.OnDisconnected = () =>
        {
            netState = NetState.DisConnected;
        }
            LoginManager.Instance.busy = false;
        };
        mainProtocolQueue.Clear();
        mainSocket.Connect(ip, port, (bool ok) =>
        {
            if (onConnected != null)
            {
                onConnected(ok);
            }
        });
    }
    //限制客户端的下一个包是登录包C0101_tagCPlayerLogin,如果不是登录包不允许发送
@@ -258,17 +158,18 @@
    public void SendCachePackage()
    {
        int cnt = sendQueue.Count;
        while (sendQueue.Count > 0)
        if (mainSocket != null)
        {
            SendInfo(sendQueue.Dequeue());
            while (sendQueue.Count > 0)
            {
                SendInfo(sendQueue.Dequeue());
            }
        }
        Debug.Log($"重点提醒:0403登录后 发送缓存包数量 {cnt} 个");
        Debug.LogError($"重点提醒:0403登录后 发送缓存包数量 {cnt} 个");
    }
    public void SendInfo(GameNetPackBasic protocol)
    {
        Debug.LogError("protocol to send: " + protocol.ToString());
        if (waitLogin)
        {
            if (protocol is not C0101_tagCPlayerLogin)
@@ -289,21 +190,22 @@
            }
        }
        if (networkService != null)
        if (mainSocket != null)
        {
            networkService.SendInfo(protocol);
            mainSocket.SendInfo(protocol);
            DebugPkgCache.Push(protocol);
        }
    }
#if UNITY_EDITOR
    public void SendInfo(byte[] vBytes)
    {
        if (networkService != null)
        if (mainSocket != null)
        {
            networkService.SendInfo(vBytes);
            mainSocket.SendInfo(vBytes);
        }
    }
#endif
    public void PushPackage(GameNetPackBasic protocol, ServerType type)
    {
@@ -331,9 +233,9 @@
    {
        try
        {
            if (networkService != null)
            if (mainSocket != null)
            {
                networkService.Disconnect();
                mainSocket.CloseConnect();
            }
            mainProtocolQueue.Clear();
@@ -355,9 +257,9 @@
    {
        try
        {
            if (networkService != null)
            if (mainSocket != null)
            {
                networkService.Disconnect();
                mainSocket.CloseConnect();
            }
            mainProtocolQueue.Clear();
@@ -380,9 +282,9 @@
        {
            SDKUtils.Instance.RoleLoginOut();
            if (networkService != null)
            if (mainSocket != null)
            {
                networkService.Disconnect();
                mainSocket.CloseConnect();
            }
            mainProtocolQueue.Clear();
@@ -414,10 +316,7 @@
    void OnUpdate()
    {
        if (networkService != null)
        {
            if (networkService is WebSocketNetworkService wsService) { wsService.Update(); }
        }
        mainSocket?.DispatchMessageQueue();
        lock (this)
        {
Main/Core/NetworkPackage/INetworkService.cs
File was deleted
Main/Core/NetworkPackage/INetworkService.cs.meta
File was deleted
Main/Core/NetworkPackage/Network.meta
File was deleted
Main/Core/NetworkPackage/Socket/ClientSocket.cs
@@ -1,39 +1,73 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
#if !UNITY_WEBGL && !H5_VERSION
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 && !H5_VERSION
    // 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; } }
#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 && !H5_VERSION
            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];
    public ClientSocket(ServerType type)
    {
        this.socketType = type;
    }
#if !UNITY_WEBGL && !H5_VERSION
    // ==================== TCP Socket 实现 ====================
    public void Connect(string _ip, int _port, Action<bool> _onConnected)
    {
@@ -260,7 +294,6 @@
    }
    static byte[] vCmdBytes = new byte[2];
    /// <summary>
    /// 解析数据包: FFCC+封包长度+封包(封包头+数据)结构体内容
    /// </summary>
@@ -346,71 +379,14 @@
    }
    /// <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)
@@ -448,4 +424,279 @@
        }
    }
    public void DispatchMessageQueue()
    {
        // TCP不需要轮询
    }
#else
    // ==================== WebSocket 实现(WebGL平台)====================
    public async void Connect(string _ip, int _port, Action<bool> _onConnected)
    {
        ip = _ip;
        port = _port;
        onConnected = _onConnected;
        string url = $"ws://{_ip}:{_port}";
        Debug.Log($"[ClientSocket-WebSocket] 开始连接: {url}");
        try
        {
            webSocket = new WebSocket(url);
            // 注册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()
    {
        Debug.Log("[ClientSocket-WebSocket] 连接成功");
        m_LastPackageTime = DateTime.Now;
        if (onConnected != null)
        {
            onConnected(true);
            onConnected = null;
        }
    }
    private void OnWebSocketMessage(byte[] data)
    {
        try
        {
            getBytesTotal += data.Length;
            // WebSocket是消息模式,每次收到完整包,直接处理
            byte[] fixBytes = data;
            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)
                {
                    Debug.LogError($"[ClientSocket-WebSocket] 包数据不足: {vLeavingLeng} bytes");
                    break;
                }
                vBodyLeng = BitConverter.ToInt32(fixBytes, vReadIndex + 2);
                if (vBodyLeng > vLeavingLeng - 6)
                {
                    Debug.LogError($"[ClientSocket-WebSocket] 包长度不匹配: 声明 {vBodyLeng + 6}, 实际 {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}");
    }
    private void OnWebSocketClose(WebSocketCloseCode code)
    {
        Debug.Log($"[ClientSocket-WebSocket] 连接关闭: {code}");
        OnDisconnected?.Invoke();
    }
    public async void CloseConnect()
    {
        Debug.Log("[ClientSocket-WebSocket] ==== CloseConnect");
        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
}
Main/Core/NetworkPackage/WebSocketNetworkService.cs
File was deleted
Main/Core/NetworkPackage/WebSocketNetworkService.cs.meta
File was deleted
Main/Main.cs
@@ -19,7 +19,7 @@
    /// <summary>
    /// 网络类型配置(可在代码中修改或通过 Inspector 配置)
    /// </summary>
    public static NetworkType CurrentNetworkType = NetworkType.TCP;
    // public static NetworkType CurrentNetworkType = NetworkType.WebSocket;
    public static List<IGameSystemManager> managers = new List<IGameSystemManager>();