c# unity protobuf-net和Java netty/io-game 的protobuf序列化问题解决

我的unity端用的是protobuf-net框架,Java服务端是io-game框架,他是基于netty的。
c#的byte和Java byte不太一样,但是他们的二进制是完全一样的。
不需要担心。
但是我们一般在tcp发送消息的时候,头部会带一个int代表protobuf的序列化长度。但是c#和Java的int二进制是颠倒的,比如:30
java中是00000000 00000000 00000000 00011110
而c#中是00011110 00000000 00000000 00000000
这好像叫大小端问题。
只需要在c#代码中使用Array.Reverse(b);倒转即可
在这里插入图片描述
combine是把两个数组结合。一起发去服务端。服务端根据头部的int,判断数据长度。

如果:你想要做一个分布式大型的或者单体的服务端。io-game是一个不错的选择。比之netty要简单许多。

我的客户端代码:

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;

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) {
                    byte[] buffer = new byte[1024];
                    int dataLength = clientSocket.Receive(buffer);

                    int available = clientSocket.Available;

                    if (dataLength == 0) {
                        //长度为0表示没接受到内容。断开。
                        break;
                    }

                    string strMsg = Encoding.UTF8.GetString(buffer, 0, dataLength);
                    Console.WriteLine("客户端接受到消息:" + strMsg);
                }
            } catch (Exception e) { }
        }


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

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


    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 {
        /// <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;
            }
        }
    }
}
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; }
    }
}
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();

        Test01 hellorep = new Test01() {
            name = "张江南嘿嘿嘿"
        };
        
        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));

        client.SubmitMsgToServer(bytes);


        // 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() { }
}

代码只是demo,我只测试了发送。接收应该有问题的。
Socket01是挂载在游戏中启动的类。
c#端需要引入protobuf-net的dll文件。需要的话可以找我要。
服务端的话是io-game可以搜一下,去看文档。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值