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类,内容如下:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using ProtoBuf;
- namespace Com.YourCompany.Project.Proto.Module{
- [ProtoContract]
- public class WorkerInfo {
- [ProtoMember(1)]
- public int workerId;
- [ProtoMember(2)]
- public int leftClosingTimeSec;
- [ProtoMember(3)]
- public int buildingId;
- }
- }
按下Shift+F6生成dll,在项目的bin\Debug目录下就可以找到ProtoModelDLL.dll了
3.2、创建一个用于序列化的C#工程,然后运行生成dll
也是以VS为例,首先创建一个控制台应用程序:“文件”>"新建">"项目">"控制台应用程序"(记得选择 .net framework 2.0)
将Protobuf和3.1生成的dll添加到引用
在项目生成的Program.cs中写入:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using ProtoBuf.Meta;
- using ProtoBuf;
- using ProtoBuf.Compiler;
- using Com.YourCompany.Project.Proto.Module;
- namespace ProtoModelSerializerCreator
- {
- class Program
- {
- static void Main(string[] args)
- {
- var model = TypeModel.Create();
- model.Add(typeof(object), true);
- model.Add(typeof(WorkerInfo), true);
- model.AllowParseableTypes = true;
- model.AutoAddMissingTypes = true;
- model.Compile("ProtoModelSerializer", "ProtoModelSerializer.dll");
- }
- }
- }
然后ctrl+F5运行,这时候你就可以在bin\Debug中看到ProtoModelSerializer.dll。
3.3、将上面两个工程生成的dll(ProtoModelDLL.dll和ProtoModelSerializer.dll)以及protobuf-net.dll拖到unity中
怎么用?看第4步
4、在Unity中反序列化Protobuf
由于一般游戏客户端请求的数据量比较简单,也比较少,因此我们前端请求是不直接二进制请求。而前端收到后端返回才采用了Protobuf,因此。这里只讨论Protobuf的反序列化。
代码很简单,下面写个测试代码:
- using UnityEngine;
- using System.Collections;
- using ProtoBuf.Meta;
- using Com.YourCompany.Project.Proto.Module;
- using System.IO;
- using Com.Duoyu001.Proto.Building;
- using Com.Duoyu001.Proto.Worker;
- public class TestProto : MonoBehaviour
- {
- // init
- void Start ()
- {
- byte[] dataFromServ = new byte[]{8, 233, 7, 16, 100, 24, 1}; //these bytes are generated by server
- RuntimeTypeModel serializer = ProtoModelSerializer.Create ();
- System.IO.MemoryStream memStream = new System.IO.MemoryStream ();
- WorkerInfo w = new WorkerInfo ();
- serializer.Deserialize (memStream, w, w.GetType ()); //asign value to proto model
- Debug.Log (w.workerId + ", " + w.buildingId + ", " + w.leftClosingTimeSec);
- }
- }
运行后Unity控制台输出了worker的信息。代码中的dataFromServ字节数组的内容实际应该是通信时候后端返回的。这里测试就不涉及Socket通信的知识了。
5、服务端Java也用Protobuf
看到一个客户端用Protobuf这么麻烦,那后端会怎样呢?其实后端是比较简单的,有Google官方的支持。
下载:https://code.google.com/p/protobuf/downloads/list
下完解压是这样的(2.5.0):
进入“protobuf-2.5.0\java”文件夹,里面是一个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.0\src”里面的protoc.exe进行生成,它会帮你生成一个java文件。(详细看https://developers.google.com/protocol-buffers/docs/javatutorial),我这里有提供一个bat,用于调用protoc来生成java文件的,手动输入的话太麻烦了。在windows下把它保存到.bat然后双击运行就可以了。
- @echo off
- echo ** setting runtime variable
- REM _protoSrc 是你的proto文件目录的位置
- set _protoSrc=F:\project_proto_src\trunk\xgame-controllers\protos
- REM protoExe 是用于从proto生成java的protoc.exe程序的位置
- set protoExe=C:\Users\john\Desktop\protobuf-2.5.0\src\protoc.exe
- REM java_out_file 存放生成的Java文件目录的位置
- set java_out_file=F:\project_proto_src\trunk\xgame-controllers\src\main\java\
- for /R "%_protoSrc%" %%i in (*) do (
- set filename=%%~nxi
- if "%%~xi" == ".proto" (
- %protoExe% --proto_path=%_protoSrc% --java_out=%java_out_file% %%i
- )
- )
OK啦,这样你只要把生成的Java复制到或直接生成到你的Java项目源码目录中,然后就可以使用了。比如:
以前面说的WorkerInfo为例
- package com.duoyu001.xgame;
- import java.util.Arrays;
- import com.duoyu001.xgame.worker.proto.WorkerInfoBuilder.WorkerInfo;
- public class TestProto {
- public static void main(String[] args) {
- WorkerInfo w = WorkerInfo.newBuilder().setBuildingId(1)
- .setLeftClosingTimeSec(100).setWorkerId(1001).build();
- byte[] byteArray = w.toByteArray();
- System.out.println(Arrays.toString(byteArray));
- }
- }
控制台就会输出:
细心的同学会发现这里的字节和上面的“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
这一步相关操作大家有兴趣可以自行试试,有问题欢迎在本博客讨论。
Unity3D客户端和Java服务端使用Protobuf的更多相关文章
- C#使用Thrift简介,C#客户端和Java服务端相互交互
C#使用Thrift简介,C#客户端和Java服务端相互交互 本文主要介绍两部分内容: C#中使用Thrift简介 用Java创建一个服务端,用C#创建一个客户端通过thrift与其交互. 用纯C#实 ...
- RPC学习--C#使用Thrift简介,C#客户端和Java服务端相互交互
本文主要介绍两部分内容: C#中使用Thrift简介 用Java创建一个服务端,用C#创建一个客户端通过thrift与其交互. 用纯C#实现Client和Server C#服务端,Java客户端 其中 ...
- Socket通讯-C#客户端与Java服务端通讯(发送消息和文件)
设计思路 使用websocket通信,客户端采用C#开发界面,服务端使用Java开发,最终实现Java服务端向C#客户端发送消息和文件,C#客户端实现语音广播的功能. Java服务端设计 packag ...
- C++客户端访问Java服务端发布的SOAP模式的WebService接口
gSOAP是一个绑定SOAP/XML到C/C++语言的工具,使用它可以 简单快速地开发出SOAP/XML的服务器端和客户端 Step1 使用gsoap-2.8\gsoap\bin\win32\wsdl ...
- android客户端向java服务端post发送json
android 端: private void HttpPostData() { try { HttpClient httpclient = new DefaultHttpClient( ...
- “快的打车”创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - V2EX
"快的打车"创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - ...
- thrift例子:python客户端/java服务端
java服务端的代码请看上文. 1.说明: 这两篇文章其实解决的问题是,当使用python去访问大数据线上集群的时候,遇到两个问题: 1)python-hadoop和python-hive相关包链接不 ...
- java版gRPC实战之六:客户端动态获取服务端地址
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- NIO【同步非阻塞io模型】关于 NIO socket 的详细总结【Java客户端+Java服务端 + 业务层】【可以客户端间发消息】
1.前言 以前使用 websocket来实现双向通信,如今深入了解了 NIO 同步非阻塞io模型 , 优势是 处理效率很高,吞吐量巨大,能很快处理大文件,不仅可以 做 文件io操作, 还可以做sock ...
随机推荐
- MYSQL 常用函数(数学、字符串、日期时间、系统信息、加密)
一.数学函数 数学函数主要用于处理数字,包括整型.浮点数等. ABS(x) 返回x的绝对值 SELECT ABS(-1) -- 返回1 CEIL(x),CEILING(x) 返回大于或等于x的最小整数 ...
- nodejs安装及环境配置(windows系统)
作为服务端运行javascript的平台的NodeJs,把前台javascript移到了服务器端,Google V8引擎使其运行效率非常高,它可以异步,无任何阻塞运行程序.nodejs包含http服务 ...
- 自然语言18_Named-entity recognition
https://en.wikipedia.org/wiki/Named-entity_recognition http://book.51cto.com/art/201107/276852.htm 命 ...
- easyUI draggable插件使用不当,导致拖动div内部文本框无法输入;设置echarts数据为空时就显示空白,不要动画和文字
先上一个Demo <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://ww ...
- ecshop JSON,ajax.call 异步传输
1.res = Ajax.call('user.php?act=depot_id', 'id='+v,null,"GET", "JSON",false); 2. ...
- php的if语句单行与多行
//正确: //错误写法 $b = //if前面不能带 等号
- 简单CSS布局留用
1.导航栏固定显示代码,文字居中,z-index header{ position: fixed; top: 0px; left: 10%; width: 80%; height: 80px; bor ...
- 无忧之道:Docker中容器的备份、恢复和迁移
原创:LCTT https://linux.cn/article-5967-1.html译者: GOLinux本文地址:https://linux.cn/article-5967-1.html 201 ...
- 从Microsoft.AspNet.Identity看微软推荐的一种MVC的分层架构
Microsoft.AspNet.Identity简介 Microsoft.AspNet.Identity是微软在MVC 5.0中新引入的一种membership框架,和之前ASP.NET传统的mem ...
- OC-copy
一,堆与栈 1,栈区,stack: 后进先出,由编译器自动分配并释放,一般存放函数的参数值.局部变量 2,堆区,heap:先进先出,由程序员分配和释放 3,全局区,静态区:程序结束后由系统释放, 4, ...