《从零开始搭建游戏服务器》 java与C#的protobuf序列化不兼容
引言:
今天在联调Java服务器与Unity(C#编写)的客户端的网络协议数据,由于网络通信的核心数据是通过序列化之后的字节数组经过加密和压缩的结果,但是发现两者使用Protobuf协议序列化工具出来的结果是不同的(Java使用 Protostuff
工具,C#使用 Protobuf-net.dll
自带的 ProtoBuf
工具),现在Java序列化之后的字节数组长度要比C#序列化结果长得多。
方案选择:
网上推荐兼容的序列化方案有:
1.WOX工具:
WOX 是一个开源项目: woxserializer,它可以序列化Java和C#对象到XML,也可以还原。他的主要特点是使用了新一代的XML标准,其目的是让语言更独立。这意味着,如果我们序列化一个Java对象到XML,我们可以采用XML来重建对象成C#,反之亦然。
例如:
创建两个类:
Student
和Course
:-
public class Student { private String name; private int registrationNumber; private Course[] courses; //constructors and methods omitted } public class Course { private int code; private String name; private int term; //constructors and methods omitted }
Student
对象序列化为XML,首先创建对象:java中的实现:需要引入
woxserializer.jar
Course[] courses = { new Course(6756, "XML and Related Technologies", 2), new Course(9865, "Object Oriented Programming", 2), new Course(1134, "E-Commerce Programming", 3) }; Student student = new Student ("Carlos Jaimez", 76453, courses);
C#中的实现:需要引入
woxserializer.dll
Course[] courses = { new Course(6756, "XML and Related Technologies", 2), new Course(9865, "Object Oriented Programming", 2), new Course(1134, "E-Commerce Programming", 3) }; Student student = new Student ("Carlos Jaimez", 76453, courses);
使用
Easy
类中的静态方法save
,实现序列化:
String filename = "student.xml"; Easy.save(student, filename);
序列化对象后的XML:
<object type="Student" id="0"> <field name="name" type="string" value="Carlos Jaimez" /> <field name="registrationNumber" type="int" value="76453" /> <field name="courses"> <object type="array" elementType="Course" length="3" id="1"> <object type="Course" id="2"> <field name="code" type="int" value="6756" /> <field name="name" type="string" value="XML and Related Technologies" /> <field name="term" type="int" value="2" /> </object> <object type="Course" id="3"> <field name="code" type="int" value="9865" /> <field name="name" type="string" value="Object Oriented Programming" /> <field name="term" type="int" value="2" /> </object> <object type="Course" id="4"> <field name="code" type="int" value="1134" /> <field name="name" type="string" value="E-Commerce Programming" /> <field name="term" type="int" value="3" /> </object> </object> </field> </object>
2.字节序列化:
Java版本:
ObjectOutputStream
public class SerObject { public static void main(String[] args) throws Exception { //序列 Student stu = new Student("张三", "男"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( new File("student.ini"))); oos.writeObject(stu); //反序列 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("student.ini"))); Student stu2 = (Student) ois.readObject() ; System.out.println(stu.getSname()); } }
C#版本:
BinaryFormatter
//序列 static void BinarySerialize() { Student stu = new Student("张三","男"); //1.定义文件流 FileStream fs = new FileStream("E:\\stu.obj",FileMode.Create); //2.实例化序列化对象 BinaryFormatter bf = new BinaryFormatter(); //3.执行序列化 bf.Serialize(fs, stu); //4.关闭文件流 fs.Close(); } //反序列 static void BinaryDeserialize() { //1.定义文件流 FileStream fs = new FileStream("E:\\stu.obj", FileMode.Open,FileAccess.Read); //2.实例化序列化对象 BinaryFormatter bf = new BinaryFormatter(); //3.执行序列化 Student stu = (Student)bf.Deserialize(fs); //4.关闭文件流 fs.Close(); Console.WriteLine(stu.Name "\t" stu.Age); }
3.IKVM.NET
方案:
也就是Java不变,而C#端引入 IKVM.NET,这是一个用.net实现的java虚拟机。这样就可以都通过java来实现序列化和反序列化,只是C#中的此操作是通过调用Java代码来实现的。当然,跨语言的编程无疑会带来性能上的下降,而且会使框架变得很笨重。
4.Json:
Json
是Java和C#之间通信的利器,Java端将Java对象转变为Json串后发出,C#端接收到Json串后转换为C#对象;C#发出转变为Json串的对象,Java收到后解析成Java对象,Json串在不同语言之间起到一个桥梁的作用。
对定义的Java或C#对象生成Json字串,以及从Json字串生成Java或C#对象,有很方便的方法,那就是Java下使用 jackson,C#下使用 Newtonsoft.Json,其中还有一些问题需要注意,如关于时间这种常见类型转换的问题。
5.Java使用 toByteArray()
序列化
在Java端假如直接对protobuf数据结构对象调用toByteArray()
来进行对象的序列化,发现数据长度与C#中使用Protobuf-net.dll自带的 ProtoBuf.Serializer.Serialize<T>(stream, msg);
序列化出来结果的数据长度是一样的。
- 问题:
使用此方案存在另外一个问题,那就是:- Java的字节
byte
是带符号的,数据范围是-128~127
; - C#的字节
byte
是无符号的,数据范围是0~255
。
- Java的字节
也就是说数据对不上,例如C#中的225,在Java中就变成了-31。
解决方案:
Java采用int[]
来存储数据可以避免溢出:这只是一个初步想法,目前还未想到完整的实现方案,预想是将数据转化为Java和C#兼容的数据类型来进行传递。
//假设原本已经有一个byte[]数据datas int[] ns = new int[datas.length]; for(int i=0;i<ns.length;i ){ //将数据范围从带符号数据范围转为无符号范围数据 ns[i] = datas[i]&0xff; }
小结:
虽然C#最初是由Java衍生出来的语言,但是经过多个版本的优化之后,两者已经有了很大的差别,这就使得跨语言通信更为不便,假如仍然需要在坚持Java与C#之间建立通信,序列化和反序列化使用xml
或json
的格式是最优的选择。
http://blog.csdn.net/linshuhe1/article/details/73289181