您的位置:首页 > 编程语言 > Java开发

《从零开始搭建游戏服务器》 java与C#的protobuf序列化不兼容

2017-06-15 15:01 447 查看

引言:

今天在联调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#,反之亦然。

使用方法可以参考:用WOX来序列化Java和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代码来实现的,具体实现方式可以查看:利用IKVM在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
,其中还有一些问题需要注意,如关于时间这种常见类型转换的问题

详细的使用方法可以参考:使用Json让Java和C#沟通的方法

5.Java使用
toByteArray()
序列化

在Java端假如直接对protobuf数据结构对象调用
toByteArray()
来进行对象的序列化,发现数据长度与C#中使用Protobuf-net.dll自带的
ProtoBuf.Serializer.Serialize<T>(stream, msg);
序列化出来结果的数据长度是一样的。

问题:

使用此方案存在另外一个问题,那就是:

Java的字节
byte
带符号的,数据范围是
-128~127


C#的字节
byte
无符号的,数据范围是
0~255


也就是说数据对不上,例如C#中的225,在Java中就变成了-31。

解决方案:

Java采用
int[]
来存储数据可以避免溢出:

//假设原本已经有一个byte[]数据datas
int[] ns = new int[datas.length];
for(int i=0;i<ns.length;i++){
//将数据范围从带符号数据范围转为无符号范围数据
ns[i] = datas[i]&0xff;
}


这只是一个初步想法,目前还未想到完整的实现方案,预想是将数据转化为Java和C#兼容的数据类型来进行传递。

小结:

虽然C#最初是由Java衍生出来的语言,但是经过多个版本的优化之后,两者已经有了很大的差别,这就使得跨语言通信更为不便,假如仍然需要在坚持Java与C#之间建立通信,序列化和反序列化使用
xml
json
的格式是最优的选择。

参考资料:

WOX―an xml serializer for java or c# objects

c#、java的序列化、反序列化

利用IKVM在C#中调Java程序(总结版)

使用Json让Java和C#沟通的方法

C#到Java byte类型冲突的解决

C#与Java通过protobuf进行网络通信过程中遇到的问题

关于java各种类型转化为无符号和类型转化为byte数组的方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐