分享

Unity3D & Java 基于 Protobuf 通信实现

 阿修罗之狮猿授 2016-04-27

Unity3D & Java 基于 Protobuf 通信实现

最近研究Unity3D,同时需要给游戏制定一套通信协议。因为本人是后端出生,对C#的 Socket相关通信框架不太熟悉,经过几天的学习,终于搞定了。在这里公布出来,大家可以共同学习,少走弯路。
本文重点:演示怎么解析和发送协议。

技术选型


服务端1
Java7
netty 4
客户端2
C#
SuperSocket.ClientEngine https://clientengine./
它是从SuperSocket中分离出来的,不人性的是竟然没使用教程
通信协议3
Protobuf https://github.com/google/protobuf/
因为Protobuf 官方只支持 Java,C++,Pythone语言,C#需要第三方支持
protobuf-csharp https://code.google.com/p/protobuf-csharp-port/

Protobuf 相关的使用,请自行Gooogle,后面的代码会展示相关API, Goole打不开买一个代理一个月20RMB

开发前准备


协议解析,无论任何语言协议解析在通信中使用中都是必须的。要成功的解析协议,必须先搞清楚协议是如何制定的。
Protobuf 是基于 变长消息头(length) + 消息体(body)

Proto生成

message Request {
    required string command = 1;
    required string data = 2;
}

message Response {
    required string command = 1;
    required string data = 2;
}

至于怎么生成,我这里就不给出详细方式了,通过Protobuf资料,有详细说明。

客户端代码


        public static DataEventArgs buffer = new DataEventArgs(); 

        public static int count = 0;

        public static void Main (string[] args)
        {

            buffer.Data = new byte[8192]; //8 KB
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8001);

            //supersocket clientengine 
            AsyncTcpSession client = new AsyncTcpSession (endPoint);
            client.Connected += OnConnected;
            client.DataReceived += OnDataReceive; //重点解析在这里

            //连接服务器
            client.Connect ();

            //构造Message,属性Protobuf的人应该都能看懂
            Request.Builder builder = Request.CreateBuilder ();
            builder.SetCommand ("110");
            builder.SetData ("1231231232131");

            Request request = builder.BuildPartial ();  
            sendMessage (client, request);

            Thread.Sleep (30000);
        }

        public static void OnConnected(Object sender, EventArgs e)
        {
            Console.WriteLine ("connect to server finish.");
        }

        /**
        * 这里 C# 的实现和Protobuf 官方给的Java实现是一样的
        */
        public static void sendMessage(AsyncTcpSession client, Request request)
        {
            using(MemoryStream stream = new MemoryStream())
            {
                CodedOutputStream os = CodedOutputStream.CreateInstance(stream);
                //一定要去看它的代码实现,
                os.WriteMessageNoTag(request); 
                /**
                * WriteMessageNoTag 等价于 WriteVarint32, WriteByte(byte[])
                * 也就是:变长消息头 + 消息体
                */

                os.Flush();

                byte[] data = stream.ToArray();
                client.Send ( new ArraySegment<byte>(data) );
            }
        }

        /**
        * 协议解析,把这里搞明白了,就没白看
        */
        public static void OnDataReceive(Object sender, DataEventArgs e) 
        {
            //DataEventArgs 里面有 byte[] Data是从协议层接收上来的字节数组,需要程序端进行缓存
            Console.WriteLine ("buff length: {0}, offset: {1}", e.Length, e.Offset);
            if( e.Length <= 0 )
            {
                return;
            }

            //把收取上来的自己全部缓存到本地 buffer 中
            Array.Copy (e.Data, 0, buffer.Data, buffer.Length, e.Length);
            buffer.Length += e.Length;

            CodedInputStream stream = CodedInputStream.CreateInstance (buffer.Data);
            while ( !stream.IsAtEnd ) 
            {
                //标记读取的Position, 在长度不够时进行数组拷贝,到下一次在进行解析
                int markReadIndex = (int)stream.Position;

                //Protobuf 变长头, 也就是消息长度
                int varint32 = (int)stream.ReadRawVarint32();
                if( varint32 <= (buffer.Length - (int)stream.Position) )
                {
                    try
                    {
                        byte[] body = stream.ReadRawBytes (varint32);

                        Response response = Response.ParseFrom (body);                      
                        Console.WriteLine("Response: " + response.ToString() + ", count: " + (++count));
                        //dispatcher message, 这里就可以用多线程进行协议分发

                    }catch(Exception exception)
                    {
                        Console.WriteLine(exception.Message);
                    }
                } 
                else 
                {
                    /**
                    * 本次数据不够长度,缓存进行下一次解析
                    */
                    byte[] dest = new byte[8192];
                    int remainSize = buffer.Length - markReadIndex;
                    Array.Copy(buffer.Data, markReadIndex, dest, 0, remainSize);

                    /**
                     * 缓存未处理完的字节 
                     */
                    buffer.Data = dest;
                    buffer.Offset = 0;
                    buffer.Length = remainSize;

                    break;
                }
            }
        }

后记


客户端完整代码打包:http://download.csdn.net/detail/zeus_9i/8748899

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多