unity c#与java io-game游戏服务器框架的结合,tcp序列化协议protobuf

io-game文档地址https://www.yuque.com/iohao/game/ywe7uc#ooyta

io-game游戏服务端

序列化使用的是百度的jprotobuf,io-game文档里有详细教程。
简单看说一下配置,使用tcp协议。
在这里插入图片描述
不贴代码了。io-game文档地址https://www.yuque.com/iohao/game/ywe7uc#ooyta

unity客户端

我用的是protobuf-net框架序列化c#类。
protobuf-net导入unity网上有教程的。
直接贴代码。

SocketClient负责发送、接收数据。
CmdMgr是cmd工具类
ProtobufHelper是protobuf-net序列化工具类和创建消息体

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using ProtoBuf;
using System.IO;
using System.Text;
using code.socket.pojo;
using LitJson;
using Object = System.Object;

namespace code.socket {
    public class SocketClient {
        //客户端socket
        private Socket clientSocket;

        //服务器IP和端口
        public string ServerIP;
        public int ServerPort;

        //收消息的线程
        private Thread acceptMsgThread;


        //构造函数
        public SocketClient(string _serverIP, int _serverPort) {
            ServerIP = _serverIP;
            ServerPort = _serverPort;
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <returns></returns>
        public bool ConnectServer() {
            if (ServerIP == null || ServerPort == 0) {
                return false;
            }

            try {
                //初始化socket对象
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //初始化地址和端口号
                IPAddress serverIp = IPAddress.Parse(ServerIP);
                IPEndPoint endPoint = new IPEndPoint(serverIp, ServerPort);
                //连接服务器
                clientSocket.Connect(endPoint);

                //新建线程,接受服务端下发的消息
                acceptMsgThread = new Thread(AcceptMsgs);
                acceptMsgThread.IsBackground = true;
                acceptMsgThread.Start();

                return true;
            } catch (Exception e) {
                Debug.Log("出现异常连接失败:" + e.Message);
                return false;
            }
        }

        /// <summary>
        /// 收数据。
        /// </summary>
        private void AcceptMsgs() {
            try {
                while (true) {
                    Thread.Sleep(5);
                    int available = clientSocket.Available;

                    byte[] len = new byte[4];
                    int l = clientSocket.Receive(len);
                    if (l == 0) {
                        continue;
                    }

                    if (l != 4) {
                        // break;
                        byte[] bytes = new byte[clientSocket.ReceiveBufferSize];
                        int receive = clientSocket.Receive(buffer: bytes);
                        Debug.Log("出现错误了,已清空缓冲区,消息可能丢失");
                    }

                    Array.Reverse(len);
                    int length = System.BitConverter.ToInt32(len, 0);
                    byte[] dataBytes = new byte[length];
                    int dataLength = 0;
                    while (true) {
                        if (dataLength == length) {
                            break;
                        }
                        dataLength += clientSocket.Receive(dataBytes, dataLength, dataBytes.Length, SocketFlags.None);
                    }

                    int ll = dataLength + l;
                    Debug.Log("收到:" + available + ",,," + ll + ",,");
                    ProtobufHelper.HandleMsg(dataBytes);

                    // ProtobufHelper.Split(buffer, dataLength);
                }
            } catch (Exception e) {
                Debug.Log(e.Message);
            }
        }


        /// <summary>
        /// 发送文本消息
        /// </summary>
        /// <param name="msg"></param>
        public bool SubmitMsgToServer(byte[] bytes) {
            if (clientSocket == null || !clientSocket.Connected) {
                return false;
            }

            try {
                //发送给服务端
                clientSocket.Send(bytes);
                return true;
            } catch (Exception e) {
                Console.WriteLine("客户端发送数据发生异常:" + e.ToString());
                return false;
            }
        }

        public void SendToServerAsync(byte[] data) {
            try {
                if (clientSocket == null || !clientSocket.Connected) {
                    Debug.Log("socket未连接,发送消息失败");
                    return;
                }

                //创建发送参数
                SocketAsyncEventArgs sendEventArgs = new SocketAsyncEventArgs();
                // sendEventArgs.RemoteEndPoint = endPoint;
                //设置要发送的数据
                sendEventArgs.SetBuffer(data, 0, data.Length);
                //异步发送数据
                clientSocket.SendAsync(sendEventArgs);
            } catch (Exception e) {
                Debug.Log("异步发送数据异常:" + e);
            }
        }
    }


    public class CmdMgr {
        //获取cmd
        public static int getCmd(int merge) {
            return merge >> 16;
        }

        //获取subCmd
        public static int getSubCmd(int merge) {
            return merge & 0xFFFF;
        }

        //获取mergeCmd
        public static int getMergeCmd(int cmd, int subCmd) {
            return (cmd << 16) + subCmd;
        }
    }


    public class ProtobufHelper {
        public static byte[] CreateMsg(int cmd, int subCmd, Object obj) {
            byte[] toByte = ProtobufHelper.SerializeToByte(obj);
            Message message = new Message() {
                cmdCode = cmd,
                cmdMerge = CmdMgr.getMergeCmd(cmd, subCmd),
                protocolSwitch = 1,
                data = toByte
            };
            byte[] serializeToByte = ProtobufHelper.SerializeToByte(message);

            byte[] b = BitConverter.GetBytes(serializeToByte.Length);
            Array.Reverse(b);
            byte[] bytes = ProtobufHelper.Combine2(b, serializeToByte);
            return bytes;
        }

        public static void HandleMsg(byte[] data) {
            Message message = ProtobufHelper.DeserializeFromByteAry_PB<Message>(data);
            // Debug.Log(JsonMapper.ToJson(message));

            Test01 test01 = ProtobufHelper.DeserializeFromByteAry_PB<Test01>(message.data);
            Debug.Log(test01.name);
            // Debug.Log(JsonMapper.ToJson(test01));
        }

        public static void Split(byte[] a, int length) {
            byte[] c = new byte[4];
            byte[] d = new byte[length - 4];

            Array.Copy(a, 0, c, 0, 4);
            Array.Copy(a, 4, d, 0, length - 4);
            Array.Reverse(c);
            int g = System.BitConverter.ToInt32(c, 0);
            Debug.Log(g);
            Message message = ProtobufHelper.DeserializeFromByteAry_PB<Message>(d);
            Debug.Log(JsonMapper.ToJson(message));

            Test01 test01 = ProtobufHelper.DeserializeFromByteAry_PB<Test01>(message.data);
            Debug.Log(test01.name);
            Debug.Log(JsonMapper.ToJson(test01));
        }

        /// <summary>
        /// 将数组a,b进行合并
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="c"></param>
        public static byte[] Combine2(byte[] a, byte[] b) {
            byte[] c = new byte[a.Length + b.Length];

            Array.Copy(a, 0, c, 0, a.Length);
            Array.Copy(b, 0, c, a.Length, b.Length);
            // Array.Reverse(c);
            return c;
        }

        //对象序列化成序列化成文件
        public static string Serialize<T>(T t) {
            //MemoryStream继承了IDisposable接口,所以才能被using自动释放
            //using 的作用为使用完之后就释放流对象
            using (MemoryStream ms = new MemoryStream()) {
                //使用protobuf程序集内置方法将对象t序列化到ms中
                Serializer.Serialize<T>(ms, t);
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        public static byte[] SerializeToByte<T>(T t) {
            //MemoryStream继承了IDisposable接口,所以才能被using自动释放
            //using 的作用为使用完之后就释放流对象
            using (MemoryStream ms = new MemoryStream()) {
                //使用protobuf程序集内置方法将对象t序列化到ms中
                Serializer.Serialize<T>(ms, t);
                return ms.ToArray();
            }
        }

        public static string byteToString(byte[] bytes) {
            string strResult = "";
            for (var i = 0; i < bytes.Length; i++) {
                string strTemp = System.Convert.ToString(bytes[i], 2);
                strTemp = strTemp.Insert(0, new string('0', 8 - strTemp.Length));
                strResult += strTemp + " ";
            }

            return strResult;
        }

        /// <summary>
        /// 将字节数组反序列化为对象实例——ProtoBuf
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="arr">字节数组</param>
        /// <returns></returns>
        public static T DeserializeFromByteAry_PB<T>(byte[] arr) {
            using (MemoryStream ms = new MemoryStream(arr))
                return ProtoBuf.Serializer.Deserialize<T>(ms);
        }

        //根据字符串反序列化成对象
        public static T DeSerialize<T>(string content) {
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(content))) {
                T t = Serializer.Deserialize<T>(ms);
                return t;
            }
        }
    }
}

Message是发往服务器的协议,data属性就是我们需要的内容。
Test01就是测试类,序列化为byte[]赋值到Message data属性中。
Person没有大用

using System.Collections.Generic;
using ProtoBuf;

namespace code.socket.pojo {
    
    [ProtoContract]
    public class Message {
        [ProtoMember(1)] 
        public int cmdCode { get; set; }
        [ProtoMember(2)] 
        public int protocolSwitch { get; set; }
        [ProtoMember(3)] 
        public int cmdMerge { get; set; }
        [ProtoMember(4)] 
        public int responseStatus { get; set; }
        [ProtoMember(5)] 
        public string validMsg { get; set; }
        [ProtoMember(6)] 
        public byte[] data { get; set; }
        [ProtoMember(7)] 
        public int msgId { get; set; }
    }
    
    [ProtoContract]
    public class Test01 {
        [ProtoMember(1)]
        public string name;
        [ProtoMember(2)]
        public int age;
        [ProtoMember(3)]
        public List<string> list;
    }

    [ProtoContract]
    class Person {
        [ProtoMember(1)]
        public string Name { get; set; }
        [ProtoMember(2)]
        public int Age { get; set; }
    }
}

Socket01是我的初始测试类

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Text;
using code.socket;
using code.socket.pojo;
using UnityEngine;

public class Socket01 : MonoBehaviour {
    void Start() {
        SocketClient client = new SocketClient("127.0.0.1", 10100);
        client.ConnectServer();

        List<string> list = new List<string>();
        list.Add("嘿嘿嘿");
        list.Add("hhhhh");

        Test01 hellorep = new Test01() {
            name = "张江南嘿嘿嘿",
            age = 20,
            list = list,
        };

        byte[] toByte = ProtobufHelper.SerializeToByte(hellorep);
        string byteToString = ProtobufHelper.byteToString(toByte);
        // Debug.Log(byteToString);

        StringBuilder builder1 = new StringBuilder();
        for (var i = 0; i < toByte.Length; i++) {
            builder1.Append(toByte[i] + " ");
        }

        // Debug.Log(builder1.ToString());

        Message message = new Message() {
            cmdCode = 1,
            cmdMerge = CmdMgr.getMergeCmd(1, 0),
            protocolSwitch = 1,
            data = toByte
        };
        byte[] serializeToByte = ProtobufHelper.SerializeToByte(message);

        // Debug.Log(ProtobufHelper.byteToString(serializeToByte));

        StringBuilder builder = new StringBuilder();
        for (var i = 0; i < serializeToByte.Length; i++) {
            builder.Append(serializeToByte[i] + " ");
        }

        // Debug.Log(builder.ToString());

        byte[] b = BitConverter.GetBytes(serializeToByte.Length);
        Array.Reverse(b);
        byte[] bytes = ProtobufHelper.Combine2(b, serializeToByte);

        // Debug.Log(ProtobufHelper.byteToString(bytes));

        Debug.Log("字节个数:" + bytes.Length);
        client.SubmitMsgToServer(bytes);
        for (int i = 1; i <= 10; i++) {
            
        }

        // Person person1 = new Person(){ Name = "小明同学", Age = 18 };
        // //序列化
        // string testInfo =  ProtobufHelper.Serialize<Person>(person1);
        // print("testInfo"+ testInfo);
        // //反序列化
        // Person person2 = ProtobufHelper.DeSerialize<Person>(testInfo);
        // print("person2" + person2.Name +"  " +person2.Age);
    }

    void Update() { }
}

Socket02是我测试数据量大的时候会不会丢失数据(接收数据的时候)。初步是没有的。

using System;
using System.Collections;
using System.Collections.Generic;
using code.socket;
using code.socket.pojo;
using UnityEngine;

public class Socket02 : MonoBehaviour {
    void Start() {
        SocketClient client = new SocketClient("127.0.0.1", 10100);
        client.ConnectServer();

        List<string> list = new List<string>();
        list.Add("嘿嘿嘿");
        list.Add("hhhhh");

        String str = "张江南嘿嘿嘿酷酷酷哈哈哈呃呃呃;";
        for (int i = 0; i < 5; i++) {
            str += str;
        }

        Test01 helloRep = new Test01() {
            name = str,
            age = 20,
            list = list,
        };

        for (int i = 1; i <= 10; i++) {
            helloRep.name = helloRep.name + str + "===========" + i;
            byte[] bytes = ProtobufHelper.CreateMsg(1, 0, helloRep);
            Debug.Log(i + "---字节个数:" + bytes.Length);
            // client.SubmitMsgToServer(bytes);
            client.SendToServerAsync(bytes);
        }
    }

    void Update() { }
}

缺点:在这里插入图片描述
接收数据的时候,如何一直没有数据。就会一直空循环,所以我线程睡眠了一会。有没有一种像Java响应式socket一样的方法,socket缓冲区有数据了,在执行接收数据的方法。
列入:
while(clientSocket.hasDate()) {// 如果socket缓冲区没有数据会阻塞线程,缓冲区有数据了,hasData()才会返回true,继续执行方法

}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值