unity 遇见websocket

如题,项目背景:手机端H5需要实时控制PC端参与游戏互动。

服务端用java语言编写,为手机端和PC端消息通信做中间媒介,实时和两端保持长连接通信。通信消息用google开发的protobuf对数据结构进行序列化和反序列化。

手机端H5:

1、js引入第三方protobuf第三方js库,

2、制定通信协议和字段类型,字段名。把它放入一个消息文件中,便于protobuf解析:部分代码如下:

package message;

syntax = "proto3";
message send{
    int32 pid = 1;
    bytes data = 2;
}
message receive{
    int32 pid=1;
    int64 time=2;
    bytes data=3;
}

3、自行封装消息解析和发送。消息发送: var message = resolver.create(data);
        var databuff = resolver.encode(message).finish();
        var msg = {
            pid: Number(pid),
            data: databuff
        }
        var msg = webSocketProtobuf.send.create(msg);
        var buff = webSocketProtobuf.send.encode(msg).finish();        
        if (webSocketProtobuf.socket.readyState === 1) {
            webSocketProtobuf.socket.send(buff);
        } else {
            if (errorCB)
                errorCB();
        }

消息解析:socket.onmessage = function (evt) {            
            if (typeof (evt.data) == "string") {
                console.log(evt.data);
            } else {
                var reader = new FileReader();
                reader.readAsArrayBuffer(evt.data);
                reader.onload = function (e) {
                    var buf = new Uint8Array(reader.result);
                    var de = webSocketProtobuf.receive.decode(buf);                
                    if (onMessage) {
                        onMessage(de);
                    }
                    reader.onload = null;
                }
            }
        }

4、H5主要调用代码如下:初始化通信: webSocketProtobuf.setWebSocket(wsUrl,OnError,OnOpen,OnMessage);

消息接收根据事先定义好的协议号进行区分以及后续的逻辑处理:

function OnMessage(msg) {
    switch(msg.pid){
        case 3:
            //倒计时等待
            if(loadProgress!=100 && !unfinish){
                InitData();
            }
            let serverTime = webSocketProtobuf.receivetime.decode(msg.data);
            ShowCountTime(serverTime.time);            
        break;

}

}

消息发送接口:

function SendMessage(pid,data) {
    if(webSocketProtobuf.socket == null && !isOver){
        if (null != wsUrl) {
            webSocketProtobuf.setWebSocket(wsUrl,OnError,OnOpen,OnMessage);
        }
        return;
    }
    if(data.length != 0){
        webSocketProtobuf.sendData(pid,data,null);
    }
}

5、加一个守护线程定时发送心跳协议,维护长连接。

至此移动端暂告一段落。

PC端:

unity websocket这边我用的BestHttp插件。

1、导入BestHttp插件,用于websocket通信。

2、导入protobuf的dll动态库, 用于后续的消息解析和序列化。官网下载的时候会有一个C#的消息生成bat文件,首先创建一个文件,文件内容包括和服务器通信的消息协议,内容如下:

message basemessage{
    required int32 pid=1;
    required bytes data=2;
}
message receivemessage{
    required int32 pid=1;
    required int64 time=2;
    required bytes data=3;
}

点击生成对应的c#脚本,注意文件的路径点击生成dll直接导入直接代替刚才生成的.cs文件,还是很方便的。

3、接下来就是unity接收消息和发送消息:

 //连接服务器
    public void ConnectWebSocket( string webUrl, string screenId, System.Action receiveCallBack )
    {
        if( null != webSocket )
        {
            CloseWebsocket();
        }

        if( null != receiveCallBack )
        {
            callBack = receiveCallBack;
        }

        webAddress = string.Format( webAddress, webUrl, screenId );

        webSocket = new WebSocket( new System.Uri( webAddress ) );

        webSocket.StartPingThread = true;

        if( HTTPManager.Proxy != null )
        {
            webSocket.InternalRequest.Proxy = new HTTPProxy( HTTPManager.Proxy.Address, HTTPManager.Proxy.Credentials, false );
        }

        webSocket.OnOpen += OnOpen;

        webSocket.OnBinary += OnBinaryReceived;

        webSocket.OnClosed += OnClosed;

        webSocket.OnError += OnError;

        webSocket.Open();
    }

消息接收解析:

 void OnBinaryReceived( WebSocket ws, byte[] byteArr )
    {
        ProtoMessage.receivemessage receivemessage = DeserializeProto( byteArr, typeof( ProtoMessage.receivemessage ) ) as ProtoMessage.receivemessage;

        ProtoBuf.IExtensible extensible = null;

        Debug.LogError( receivemessage.pid );

        switch( receivemessage.pid )
        {
            case 2:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.receiveuserinfo ) ) as ProtoMessage.receiveuserinfo;                
                break;
            case 4:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.receiveplayer ) ) as ProtoMessage.receiveplayer;
                break;
            case 11:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.receivequit ) ) as ProtoMessage.receivequit;
                break;
            case 5:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.receiverole ) ) as ProtoMessage.receiverole;
                break;
            case 12:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.receiveitem ) ) as ProtoMessage.receiveitem;
                break;
            case 10:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.receivespeed ) ) as ProtoMessage.receivespeed;
                break;
            case 8:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.reveivegameover ) ) as ProtoMessage.reveivegameover;
                break;
            case 7:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.receivegetitem ) ) as ProtoMessage.receivegetitem;
                break;
            case 9:
                extensible = DeserializeProto( receivemessage.data, typeof( ProtoMessage.heart ) ) as ProtoMessage.heart;

                OnHandleHeart( extensible );
                return;
        }

        if( null != callBack )
        {
            callBack( receivemessage.pid, extensible );
        }
    }

反序列化消息内容:

 //DeserializeProto
    private ProtoBuf.IExtensible DeserializeProto( byte[] buffer, Type type )
    {
        ProtoBuf.IExtensible proto = null;

        using( System.IO.MemoryStream ms = new System.IO.MemoryStream() )
        {
            ms.Write( buffer, 0, buffer.Length );

            ms.Seek( 0, System.IO.SeekOrigin.Begin );

            proto = serializer.Deserialize( ms, null, type ) as ProtoBuf.IExtensible;

            ms.Close();                    
        }

        return proto;
    }

 

向服务端发送消息:

 //send bytearr message
    public void SendMessage( int pid, ProtoBuf.IExtensible proto )
    {
        byte[] buffer = SerializeProto( proto );

        ProtoMessage.basemessage basemessage = new ProtoMessage.basemessage();

        basemessage.pid = pid;

        basemessage.data = buffer;

        byte[] sendBuff = SerializeProto( basemessage );

        if( null != webSocket )
        {
            webSocket.Send( sendBuff );
        }
    }

序列化消息:

 //serialze the protot
    byte[] SerializeProto( ProtoBuf.IExtensible proto )
    {
        byte[] protoBuff = null;

        using( System.IO.MemoryStream ms = new System.IO.MemoryStream() )
        {
            serializer.Serialize( ms, proto );

            if( ms.Length > 0 )
            {
                protoBuff = new byte[(int)ms.Length];

                ms.Seek( 0, System.IO.SeekOrigin.Begin );

                ms.Read( protoBuff, 0, protoBuff.Length );
            }

            ms.Close();
        }        

        return protoBuff;
    }

4、和移动端一样创建一个守护线程,发送心跳协议:

 //守护线程
    private void HeartThread()
    {
        while( true )
        {
            long curTime = Global.GetTimeStamps();

            if( curTime - lastTime >= 5000 )
            {
                lock( connectObj )
                {
                    SendHeartMessage();
                }
            }

            if( !IsConnecting )
            {
                break;
            }
                                    
            Thread.Sleep( 1000 );
        }
    }

以上是用到的主要的代码,并不是全部代码,如需请留言探讨。

你可能感兴趣的:(unity,websocket,protobuf,websocket,网络通信,c#)