• 用Unity同时开发【微信小游戏】【安卓】【IOS】游戏#6.2 WebSocket通信


    【系列文章目录】



    前言

    本篇来实现WebSocket通信


    一、导入UnityWebSocket插件

    要在Unity中使用WebSocket我查看了官方文档
    我一直觉得Unity是一个把所有东西都为开发者准备好的引擎
    然而这次我错了,它摆烂了,Unity并没有为我们提供WebScoket的内容
    Unity中没有直接能用的WebSocketAPI

    所以只好使用插件了,这里我使用的是UnityWebSocket
    网上也有很多教程用的是BestHttp
    我也心动过,但是它好贵,坐等打折好了

    言归正传,首先我们先在UnityWebSocketRelease页面下载插件
    然后将它导入到我们的项目中
    这个插件提供的Demo来供我们参考

    二、在TS中使用

    这个插件的使用是在C#中的,我们需要封装一层来让它能被TS使用
    这里我定义了一个WebSocketClient

    public class WebSocketClient
    {
        private string connectAddress;
        protected IWebSocket _socket;
    
        public Action<string> JSOnOpen;
        public Action<string> JSOnMessage;
        public Action<CloseStatusCode, string> JSOnClose;
        public Action<string> JSOnError;
    
        public ushort State
        {
            get
            {
                if (_socket == null)
                {
                    return 0;
                }
                else
                {
                    return (ushort)(_socket.ReadyState + 1);
                }
            }
        }
        //连接
        public void Connect(string address)
        {
            connectAddress = address;
            if (_socket == null)
            {
                _socket = new WebSocket(address);
                _socket.OnOpen += Socket_OnOpen;
                _socket.OnMessage += Socket_OnMessage;
                _socket.OnClose += Socket_OnClose;
                _socket.OnError += Socket_OnError;
                _socket.ConnectAsync();
            }
        }
    
        //发送string
        public void SendStr(string str)
        {
            try
            {
                _socket.SendAsync(str);
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
        }
    
        //发送byte
        public void SendByte(string data)
        {
            try
            {
                var bytes = System.Text.Encoding.UTF8.GetBytes(data);
                _socket.SendAsync(bytes);
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
        }
    
        //关闭
        public void Close()
        {
            _socket.CloseAsync();
        }
    
        private void Socket_OnOpen(object sender, OpenEventArgs e)
        {
            JSOnOpen?.Invoke(connectAddress);
        }
    
        private void Socket_OnMessage(object sender, MessageEventArgs e)
        {
            try
            {
                if (e.IsBinary)
                {
                    JSOnMessage?.Invoke(e.Data);
                    //Debug.Log("WebSocket:" + string.Format("Receive Bytes ({1}): {0}", e.Data, e.RawData.Length));
                }
                else if (e.IsText)
                {
                    JSOnMessage?.Invoke(e.Data);
                    //Debug.Log("WebSocket:" + string.Format("Receive: {0}", e.Data));
                }
            }
            catch (Exception err)
            {
                Debug.LogError(err);
            }
        }
    
        private void Socket_OnClose(object sender, CloseEventArgs e)
        {
            JSOnClose?.Invoke(e.StatusCode, e.Reason);
            //Debug.Log("WebSocket:" + string.Format("Closed: StatusCode: {0}, Reason: {1}", e.StatusCode, e.Reason));
        }
    
        private void Socket_OnError(object sender, ErrorEventArgs e)
        {
            JSOnError?.Invoke(e.Message);
            //Debug.Log("WebSocket:" + string.Format("Error: {0}", e.Message));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    需要注意的是,我们需要声明使用的Action类型,才能正常使用

    _jsEnv.UsingAction<string>();
    _jsEnv.UsingAction<CloseStatusCode, string>();
    
    • 1
    • 2

    别忘了为它生成一下胶水代码

    接下来我们来在TS中继续工作

    import { UnityWebSocket, WebSocketClient } from "csharp";
    import { TSLog } from "../../CustomLog/TSLog";
    import { IBaseMessageData, SocketMessageType, WebSocketState } from "../NetDefine";
    
    //C#中我增加了一个Null类型
    export enum WebSocketState {
        Null = 0,
        Connecting = 1,
        Open = 2,
        Closing = 3,
        Closed = 4
    }
    
    export interface IWebSocket {
        /**
         * 连接WebSocket
         * @param address 连接地址
         */
        Connect(address: string): void;
        /**
         * 发送数据
         * @param data Json数据
         * @param droppable 是否可抛弃,true在未连接状态下会被抛弃,false会等待连接后发送数据
         */
        Send(data: IBaseMessageData, droppable?: boolean): void;
        /**
         * 关闭连接
         */
        Close(): void;
        /**
         * 监听消息
         * @param state 监听的类型
         * @param fun 回调函数
         */
        ListenMessage(state: SocketMessageType, fun: Function): void;
        /**
         * 移除监听
         * @param state 监听的类型
         * @param fun 回调函数
         */
        RemoveListen(state: SocketMessageType, fun: Function): void;
    }
    
    export class JSWebSocket extends WebSocketClient implements IWebSocket {
        private _waitSendCash: Array<string> = new Array<string>();
        private _messageHandler: Map<SocketMessageType, Array<Function>> = new Map<SocketMessageType, Array<Function>>();
    
        constructor() {
            super();
            this.JSOnOpen = this.Socket_OnOpen.bind(this);
            this.JSOnMessage = this.Socket_OnMessage.bind(this);
            this.JSOnClose = this.Socket_OnClose.bind(this);
            this.JSOnError = this.Socket_OnError.bind(this);
        }
    
        RemoveListen(state: SocketMessageType, fun: Function): void {
            if (this._messageHandler.has(state)) {
                let array = this._messageHandler.get(state);
                let newArray = new Array<Function>();
                array.forEach(v => {
                    if (v != fun) {
                        newArray.push(v);
                    }
                });
                this._messageHandler.set(state, newArray);
                array.length = 0;
                array = null;
            }
        }
    
        ListenMessage(state: SocketMessageType, fun: Function): void {
            if (!this._messageHandler.has(state)) {
                this._messageHandler.set(state, new Array<Function>());
            }
            this._messageHandler.get(state).push(fun);
        }
    
        public Send(data: IBaseMessageData, droppable: boolean = true): void {
            let jstr = JSON.stringify(data);
            TSLog.Log("stringify:" + jstr)
            //判断连接
            if (this.State != WebSocketState.Open) {
                //未连接且非可抛弃数据加入待发送池
                if (!droppable) {
                    this._waitSendCash.push(jstr);
                }
            }
            else {
                //已连接直接发送
                this.SendByte(jstr);
            }
        }
    
        private Socket_OnOpen(address: string) {
            TSLog.Log("OnConnect---->" + address);
            if (this._waitSendCash.length > 0) {
                TSLog.Log("sendcash")
                this._waitSendCash.forEach(jstr => {
                    this.SendByte(jstr);
                });
            }
    
            this._waitSendCash.length = 0;
        }
    
        private Socket_OnMessage(jstr: string) {
            TSLog.Log("OnMessage---->" + jstr);
            if (jstr == "echo.websocket.events sponsored by Lob.com") return;
            let jsdata: IBaseMessageData = JSON.parse(jstr) as IBaseMessageData;
    
            //解析Json根据类型分发Action
            if (this._messageHandler.has(jsdata.type)) {
                let handlers = this._messageHandler.get(jsdata.type);
                for (let i = 0; i < handlers.length; i++) {
                    if (handlers[i] != null) {
                        try {
                            handlers[i](jsdata);
                        }
                        catch (e) {
                            TSLog.Error(e);
                        }
    
                    }
                }
            }
        }
    
        private Socket_OnClose(errCode: UnityWebSocket.CloseStatusCode, reason: string) {
            TSLog.Log("OnClose---->" + reason);
        }
    
        private Socket_OnError(message: string) {
            TSLog.Log("Connect---->" + message);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135

    这样我们在TS中就可以使用WebSocket了

    这里我定义了一个全局的mainSocket

    export const mainSocket:IWebSocket = new JSWebSocket();
    
    • 1

    以及一些配置和数据接口

    export let mainSocketAddress: string = "wss://echo.websocket.events";
    export let httpAddress: string = "";
    
    export interface IBaseMessageData {
        type: SocketMessageType;
    }
    
    export enum SocketMessageType {
        Null = 0,
        Test = 1
    }
    
    export enum WebSocketState {
        Null = 0,
        Connecting = 1,
        Open = 2,
        Closing = 3,
        Closed = 4
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在游戏中我们会有很多个功能使用到Socket
    我不想在Socket中去为每个功能写处理
    而是在每个功能的Hanlder中去处理
    这里我写了一个TestHanlder来测试

    export interface Req_Test_MessageData extends IBaseMessageData {
        data: {
            str: number,
            str2: number
        }
    }
    
    export interface Resp_Test_MessageData extends IBaseMessageData {
        data: {
            str: number,
            str2: number
        }
    }
    
    export interface ITestHandler{
        Connect(): ITestHandler;
        ReqTest(n1: number, n2: number): ITestHandler;
        Close(time: number): ITestHandler;
    }
    
    export class TestHanler extends Singleton<TestHanler> implements ITestHandler {
        constructor() {
            super();
            mainSocket.ListenMessage(SocketMessageType.Test, this.OnRespTest.bind(this))
        }
    
        public OnRespTest(resp: Resp_Test_MessageData): void {
            TSLog.Log(resp.data.str)
            TSLog.Log(resp.data.str2)
        }
    
        public ReqTest(n1: number, n2: number): ITestHandler {
            let req: Req_Test_MessageData = {
                type: SocketMessageType.Test,
                data: {
                    str: n1,
                    str2: n2
                }
            }
    
            mainSocket.Send(req, false);
    
            return this;
        }
    
        public Connect(): ITestHandler {
            mainSocket.Connect(mainSocketAddress);
            return this;
        }
    
        public Close(time: number = 5000): ITestHandler {
            setTimeout(() => {
                mainSocket.Close();
            }, time)
            return this;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    然后我们就可以调用了

    new TestHandler()
        .Connect()  //连接服务器
        .ReqTest(5, 1)  //请求数据
        .Close(5000);   //关闭连接
    
    • 1
    • 2
    • 3
    • 4

    嗯,挺简洁的,测试了也没有什么问题
    测试结果
    具体内容还需要与服务端联调,目前以及实现了功能


  • 相关阅读:
    NOSQL Redis十大数据类型
    基于SpringCloud的面试刷题系统,项目经验统统搞定
    【Web安全】SQL注入攻击几种常见防御手法总结
    一、网络基础知识
    Android Studio中的模拟器一直在加载(Connecting to the emulator···)
    VS Code里使用Debugger for Unity插件进行调试(2023最新版)
    RocketMQ的消费流程及最佳实践
    卷积神经网络 作业
    SD模块上线切换-问题预判及对策清单
    Angular使用指令配合RXJS使用节流throttle
  • 原文地址:https://blog.csdn.net/ruanlinxi1994/article/details/125508685