zoukankan      html  css  js  c++  java
  • Unity3D客户端和Java服务端使用Protobuf

    转自:http://blog.csdn.net/kakashi8841/article/details/17334493

    前几天有位网友问我关于Unity3D里面使用Protobuf的方法,一时有事拖到现在才写这篇文章,不好意思哈。

    本文测试环境:

    系统:WINDOWS 7(第3、6步)、OS X 10.9(第4步)

    软件:VS 2012(第3、6步)、Eclipse(第5、6步)

    硬件:iPad 2(第4步)、Macbook Pro Mid 2012(第4步)

    文章目录:

    1、关于Protobuf的C#实现

    2、为什么有些Protobuf发布到iOS就用不了,甚至有些在PC都用不了?

    3、手动处理C#版本的Protobuf

        3.1、创建一个C#工程,先手动创建每一个要通过Protobuf序列化或反序列化的数据模型类,然后导出dll

        3.2、创建一个用于序列化的C#工程,然后运行生成dll

        3.3、将上面两个工程生成的dll拖到unity中

    4、在Unity中反序列化Protobuf

    5、服务端Java也用Protobuf

    6、太烦了?!客户端也要自动处理Protobuf

    1、关于Protobuf的C#实现

    首先,U3D里面Protobuf使用的是C#的实现,那么目前有几个可选的C#实现:

    C#: http://code.google.com/p/protobuf-csharp-port
    C#: http://code.google.com/p/protosharp/
    C#: https://silentorbit.com/protobuf/
    C#/.NET/WCF/VB: http://code.google.com/p/protobuf-net/

    我这里选用的是http://code.google.com/p/protobuf-net/(你可以在https://code.google.com/p/protobuf-net/downloads/list 这里下载到他的代码和工具),它比较好的一点是,提供了各种平台的支持,解压后在“Full”目录中可以看到各个平台的支持。(现在google被各种封杀,如果你打不开上面的地址,可以下载我上传到CSDN的,里面的csharp文件夹就是各个平台的protobuf需要的dll,点击下载protobuf-net)。

    看到里面的unity了吗,它里面的protobuf-net.dll将是我们准备用到的。

    2、为什么有些Protobuf发布到iOS就用不了,甚至有些在PC都用不了?

    a、Protobuf使用了JIT,即在运行时动态编译,而这个特性在Unity发布到iOS时候是不支持的。因此,会导致你在PC上可以正常运行,发布到iOS就有问题。

    b、Protobuf是基于.net 2.0以上框架写的,而Unity仅支持.net 2.0,或者有些使用2.0中比较多的特性,而你在Unity中发布设置了.net 2.0的子集。后者你只需要在Player setting中修改设置就可以了。

    上面两项也可适用于其它第三方类库,如果你自己下载了一个在PC上或C#里面能正常使用的类库,在U3D里面就不能用了,那么请检查是否是上面两条原因导致的。

    3、手动处理C#版本的Protobuf

    知道了上面问题,我们只要选一个.net2.0的Protobuf,然后它又不是JIT,那就可以正常使用了。

    这里用的思路是:

        3.1、创建一个C#工程,先手动创建每一个要通过Protobuf序列化或反序列化的数据模型类,然后导出dll

            以VS为例,首先,创建一个类库工程:“文件”>"新建">"项目">"类库"(记得选择 .net framework 2.0)

    将unity的protobuf的dll添加到项目引用

    然后假设你有一个类WorkerInfo是需要通过Protobuf进行序列化和反序列化的,那么创建一个WorkerInfo类,内容如下:

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Text;  
    4. using ProtoBuf;  
    5. namespace Com.YourCompany.Project.Proto.Module{  
    6.     [ProtoContract]  
    7.     public class WorkerInfo {   
    8.   
    9.   
    10.         [ProtoMember(1)]  
    11.         public int workerId;  
    12.   
    13.   
    14.         [ProtoMember(2)]  
    15.         public int leftClosingTimeSec;  
    16.   
    17.   
    18.         [ProtoMember(3)]  
    19.         public int buildingId;  
    20.   
    21.   
    22.     }  
    23. }  

    按下Shift+F6生成dll,在项目的binDebug目录下就可以找到ProtoModelDLL.dll了

        3.2、创建一个用于序列化的C#工程,然后运行生成dll
            也是以VS为例,首先创建一个控制台应用程序:“文件”>"新建">"项目">"控制台应用程序"(记得选择 .net framework 2.0)

    将Protobuf和3.1生成的dll添加到引用

    在项目生成的Program.cs中写入:

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Text;  
    4. using ProtoBuf.Meta;  
    5. using ProtoBuf;  
    6. using ProtoBuf.Compiler;  
    7. using Com.YourCompany.Project.Proto.Module;  
    8.   
    9.   
    10. namespace ProtoModelSerializerCreator  
    11. {  
    12.     class Program  
    13.     {  
    14.         static void Main(string[] args)  
    15.         {  
    16.             var model = TypeModel.Create();  
    17.   
    18.             model.Add(typeof(object), true);  
    19.             model.Add(typeof(WorkerInfo), true);  
    20.   
    21.             model.AllowParseableTypes = true;  
    22.             model.AutoAddMissingTypes = true;  
    23.             model.Compile("ProtoModelSerializer", "ProtoModelSerializer.dll");  
    24.         }  
    25.     }  
    26. }  


    然后ctrl+F5运行,这时候你就可以在binDebug中看到ProtoModelSerializer.dll。

        3.3、将上面两个工程生成的dll(ProtoModelDLL.dll和ProtoModelSerializer.dll)以及protobuf-net.dll拖到unity中

            怎么用?看第4步

    4、在Unity中反序列化Protobuf

    由于一般游戏客户端请求的数据量比较简单,也比较少,因此我们前端请求是不直接二进制请求。而前端收到后端返回才采用了Protobuf,因此。这里只讨论Protobuf的反序列化。

    代码很简单,下面写个测试代码:

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. using UnityEngine;  
    2. using System.Collections;  
    3. using ProtoBuf.Meta;  
    4. using Com.YourCompany.Project.Proto.Module;  
    5. using System.IO;  
    6. using Com.Duoyu001.Proto.Building;  
    7. using Com.Duoyu001.Proto.Worker;  
    8.   
    9. public class TestProto : MonoBehaviour  
    10. {  
    11.   
    12.     // init  
    13.     void Start ()  
    14.     {  
    15.         byte[] dataFromServ = new byte[]{8, 233, 7, 16, 100, 24, 1};    //these bytes are generated by server  
    16.           
    17.         RuntimeTypeModel serializer = ProtoModelSerializer.Create ();  
    18.         System.IO.MemoryStream memStream = new System.IO.MemoryStream ();  
    19.         WorkerInfo w = new WorkerInfo ();  
    20.         serializer.Deserialize (memStream, w, w.GetType ());    //asign value to proto model  
    21.           
    22.         Debug.Log (w.workerId + ", " + w.buildingId + ", " + w.leftClosingTimeSec);  
    23.     }  
    24. }  

    运行后Unity控制台输出了worker的信息。代码中的dataFromServ字节数组的内容实际应该是通信时候后端返回的。这里测试就不涉及Socket通信的知识了。

    5、服务端Java也用Protobuf

    看到一个客户端用Protobuf这么麻烦,那后端会怎样呢?其实后端是比较简单的,有Google官方的支持。

    下载:https://code.google.com/p/protobuf/downloads/list

    下完解压是这样的(2.5.0):

    进入“protobuf-2.5.0java”文件夹,里面是一个maven项目,你直接用maven clean install在target目录就会生成一个protobuf-java-2.5.0.jar的jar包了,没maven的在这里下载吧,我用maven生成的http://download.csdn.net/detail/kakashi8841/6723689。这个要时候导入你的Java项目。就可以了。

    然后你写一个proto文件,调用“protobuf-2.5.0src”里面的protoc.exe进行生成,它会帮你生成一个java文件。(详细看https://developers.google.com/protocol-buffers/docs/javatutorial),我这里有提供一个bat,用于调用protoc来生成java文件的,手动输入的话太麻烦了。在windows下把它保存到.bat然后双击运行就可以了。

    [vb] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. @echo off  
    2. echo ** setting runtime variable  
    3.   
    4. REM _protoSrc 是你的proto文件目录的位置  
    5. set _protoSrc=F:project_proto_src runkxgame-controllersprotos  
    6.   
    7. REM protoExe 是用于从proto生成java的protoc.exe程序的位置  
    8. set protoExe=C:UsersjohnDesktopprotobuf-2.5.0srcprotoc.exe  
    9.   
    10. REM java_out_file 存放生成的Java文件目录的位置  
    11. set java_out_file=F:project_proto_src runkxgame-controllerssrcmainjava  
    12.   
    13. for /R "%_protoSrc%" %%i in (*) do (   
    14.     set filename=%%~nxi   
    15.     if "%%~xi"  == ".proto" (  
    16.         %protoExe% --proto_path=%_protoSrc% --java_out=%java_out_file% %%i  
    17.     )  
    18. )  


    OK啦,这样你只要把生成的Java复制到或直接生成到你的Java项目源码目录中,然后就可以使用了。比如:
    以前面说的WorkerInfo为例

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.duoyu001.xgame;  
    2.   
    3. import java.util.Arrays;  
    4.   
    5. import com.duoyu001.xgame.worker.proto.WorkerInfoBuilder.WorkerInfo;  
    6.   
    7. public class TestProto {  
    8.   
    9.     public static void main(String[] args) {  
    10.         WorkerInfo w = WorkerInfo.newBuilder().setBuildingId(1)  
    11.                 .setLeftClosingTimeSec(100).setWorkerId(1001).build();  
    12.         byte[] byteArray = w.toByteArray();  
    13.         System.out.println(Arrays.toString(byteArray));  
    14.     }  
    15. }  


    控制台就会输出:

    细心的同学会发现这里的字节和上面的“8, 233, 7, 16, 100, 24, 1”有点不太一样。第二个数字分别为-23和233.

    其实,这个只是byte的有无符号的表示差异而已。

    他们的二进制表示都是:11101001

    6、太烦了?!客户端也要自动处理Protobuf

    我们看到客户端没加一个类,都需要在两个VS项目中增加代码,而后端是直接根据proto文件生成代码的。这样,好像有点不公平,而且这样前后端还是可能会写出不一样的结构。其实用protobuf我个人觉得最大的好处:

        a、数据量小

        b、通过proto模板生成代码,减少前后端联调

    但是现在只是后端减少了工作,前端并没有减少,因此第二个好处不是很明显。

    那好吧。我没有就这样满足。因此,我决定,前端也要根据proto来生成cs文件。

    因此,我使用Java编写了一个解析Proto文件的工具,并且根据proto生成cs文件,然后用bat调用vs的命令行执行vs项目的构建,最后生成两个dll。

    我把上面的命令都整合到一个bat中,因此这个bat的任务是:

    执行java程序,把proto文件生成cs文件。

    调用vs接口构建两个vs项目,生成两个dll。

    通过svn把这两个dll提交到客户端的主干上。

    调用上面根据proto生成java的bat片段,生成java代码。

    通过svn把生成的java代码提交到服务端主干上。

    因此,这样一来,现在只要编写好proto文件,然后双击bat,前后端就都可以通过更新svn来获取最新的Protobuf数据对象。

    bat运行

    根据proto生成cs文件并编译执行两个vs项目,然后把生成的dll提交到svn上

    生成java代码并提交svn

    这一步相关操作大家有兴趣可以自行试试,有问题欢迎在本博客讨论。

  • 相关阅读:
    分布式监控系统开发【day38】:报警自动升级代码解析及测试(八)
    分布式监控系统开发【day38】:报警阈值程序逻辑解析(四)
    分布式监控系统开发【day38】:监控trigger表结构设计(一)
    ubuntu 14.04 gitlab 的搭建
    u-boot2011.09 u-boot.img 的流程跟踪
    am335x u-boot2011.09 SPL 流程跟踪
    ubuntu apt-get 安装指定版本软件
    am335x Lan8710a 双网口配置
    Linux 使用tty0 显示10分钟自动关闭功能
    am335x uboot, kernel 编译
  • 原文地址:https://www.cnblogs.com/123ing/p/4132935.html
Copyright © 2011-2022 走看看