在java中socket传输数据时,数据类型往往比较难选择。可能要考虑带宽、跨语言、版本的兼容等问题。比较常见的做法有两种:一是把对象包装成JSON字符串传输,二是采用java对象的序列化和反序列化。随着Google工具protoBuf的开源,protobuf也是个不错的选择。对JSON,Object Serialize,ProtoBuf 做个对比。

定义一个待传输的对象UserVo:

Java代码
  1. publicclass UserVo{

  2. private String name;

  3. privateint age;

  4. privatelong phone;

  5. private List<UserVo> friends;

  6. ……

  7. }

public class UserVo{private String name;private int age;private long phone;private List<UserVo> friends;
……
}

初始化UserVo的实例src:

Java代码
  1. UserVo src = new UserVo();

  2. src.setName("Yaoming");

  3. src.setAge(30);

  4. src.setPhone(13789878978L);

  5. UserVo f1 = new UserVo();

  6. f1.setName("tmac");

  7. f1.setAge(32);

  8. f1.setPhone(138999898989L);

  9. UserVo f2 = new UserVo();

  10. f2.setName("liuwei");

  11. f2.setAge(29);

  12. f2.setPhone(138999899989L);

  13. List<UserVo> friends = new ArrayList<UserVo>();

  14. friends.add(f1);

  15. friends.add(f2);

  16. src.setFriends(friends);

UserVo src = new UserVo();
src.setName("Yaoming");
src.setAge(30);
src.setPhone(13789878978L);UserVo f1 = new UserVo();
f1.setName("tmac");
f1.setAge(32);
f1.setPhone(138999898989L);
UserVo f2 = new UserVo();
f2.setName("liuwei");
f2.setAge(29);
f2.setPhone(138999899989L);List<UserVo> friends = new ArrayList<UserVo>();
friends.add(f1);
friends.add(f2);
src.setFriends(friends);

JSON格式

采用Google的gson-2.2.2.jar 进行转义

Java代码
  1. Gson gson = new Gson();

  2. String json = gson.toJson(src);

Gson gson = new Gson();
String json = gson.toJson(src);

得到的字符串:

Js代码
  1. {"name":"Yaoming","age":30,"phone":13789878978,"friends":[{"name":"tmac","age":32,"phone":138999898989},{"name":"liuwei","age":29,"phone":138999899989}]}

{"name":"Yaoming","age":30,"phone":13789878978,"friends":[{"name":"tmac","age":32,"phone":138999898989},{"name":"liuwei","age":29,"phone":138999899989}]}

字节数为153

Json的优点:明文结构一目了然,可以跨语言,属性的增加减少对解析端影响较小。缺点:字节数过多,依赖于不同的第三方类库。

Object Serialize

UserVo实现Serializalbe接口,提供唯一的版本号:

Java代码
  1. publicclass UserVo implements Serializable{

  2. privatestaticfinallong serialVersionUID = -5726374138698742258L;

  3. private String name;

  4. privateint age;

  5. privatelong phone;

  6. private List<UserVo> friends;

public class UserVo implements Serializable{private static final long serialVersionUID = -5726374138698742258L;private String name;private int age;private long phone;private List<UserVo> friends;

序列化方法:

Java代码
  1. ByteArrayOutputStream bos = new ByteArrayOutputStream();

  2. ObjectOutputStream os = new ObjectOutputStream(bos);

  3. os.writeObject(src);

  4. os.flush();

  5. os.close();

  6. byte[] b = bos.toByteArray();

  7. bos.close();

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
byte[] b = bos.toByteArray();
bos.close();

字节数是238

反序列化:

Java代码
  1. ObjectInputStream ois = new ObjectInputStream(fis);

  2. vo = (UserVo) ois.readObject();

  3. ois.close();

  4. fis.close();

ObjectInputStream ois = new ObjectInputStream(fis);
vo = (UserVo) ois.readObject();
ois.close();
fis.close();

Object Serializalbe 优点:java原生支持,不需要提供第三方的类库,使用比较简单。缺点:无法跨语言,字节数占用比较大,某些情况下对于对象属性的变化比较敏感。

对象在进行序列化和反序列化的时候,必须实现Serializable接口,但并不强制声明唯一的serialVersionUID

是否声明serialVersionUID对于对象序列化的向上向下的兼容性有很大的影响。我们来做个测试:

思路一

把UserVo中的serialVersionUID去掉,序列化保存。反序列化的时候,增加或减少个字段,看是否成功。

Java代码
  1. publicclass UserVo implements Serializable{

  2. private String name;

  3. privateint age;

  4. privatelong phone;

  5. private List<UserVo> friends;

public class UserVo implements Serializable{private String name;private int age;private long phone;private List<UserVo> friends;

保存到文件中:

Java代码
  1. ByteArrayOutputStream bos = new ByteArrayOutputStream();

  2. ObjectOutputStream os = new ObjectOutputStream(bos);

  3. os.writeObject(src);

  4. os.flush();

  5. os.close();

  6. byte[] b = bos.toByteArray();

  7. bos.close();

  8. FileOutputStream fos = new FileOutputStream(dataFile);

  9. fos.write(b);

  10. fos.close();

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
byte[] b = bos.toByteArray();
bos.close();FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(b);
fos.close();

增加或者减少字段后,从文件中读出来,反序列化:

Java代码
  1. FileInputStream fis = new FileInputStream(dataFile);

  2. ObjectInputStream ois = new ObjectInputStream(fis);

  3. vo = (UserVo) ois.readObject();

  4. ois.close();

  5. fis.close();

FileInputStream fis = new FileInputStream(dataFile);
ObjectInputStream ois = new ObjectInputStream(fis);
vo = (UserVo) ois.readObject();
ois.close();
fis.close();

结果:抛出异常信息

Java代码
  1. Exception in thread "main" java.io.InvalidClassException: serialize.obj.UserVo; local class incompatible: stream classdesc serialVersionUID = 3305402508581390189, local class serialVersionUID = 7174371419787432394

  2. at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)

  3. at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)

  4. at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)

  5. at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)

  6. at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)

  7. at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)

  8. at serialize.obj.ObjectSerialize.read(ObjectSerialize.java:74)

  9. at serialize.obj.ObjectSerialize.main(ObjectSerialize.java:27)

Exception in thread "main" java.io.InvalidClassException: serialize.obj.UserVo; local class incompatible: stream classdesc serialVersionUID = 3305402508581390189, local class serialVersionUID = 7174371419787432394at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)at serialize.obj.ObjectSerialize.read(ObjectSerialize.java:74)at serialize.obj.ObjectSerialize.main(ObjectSerialize.java:27)
思路二

eclipse指定生成一个serialVersionUID,序列化保存,修改字段后反序列化

略去代码

结果:反序列化成功

结论

如果没有明确指定serialVersionUID,序列化的时候会根据字段和特定的算法生成一个serialVersionUID,当属性有变化时这个id发生了变化,所以反序列化的时候就会失败。抛出“本地classd的唯一id和流中class的唯一id不匹配”。

jdk文档关于serialVersionUID的描述:

写道
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。

Google ProtoBuf

protocol buffers 是google内部得一种传输协议,目前项目已经开源(http://code.google.com/p/protobuf/)。它定义了一种紧凑得可扩展得二进制协议格式,适合网络传输,并且针对多个语言有不同得版本可供选择。

以protobuf-2.5.0rc1为例,准备工作:

下载源码,解压,编译,安装

Shell代码
  1. tar zxvf protobuf-2.5.0rc1.tar.gz

  2. ./configure

  3. ./make

  4. ./make install

tar zxvf protobuf-2.5.0rc1.tar.gz
./configure
./make
./make install

测试:

Shell代码
  1. MacBook-Air:~ ming$ protoc --version

  2. libprotoc 2.5.0

MacBook-Air:~ ming$ protoc --version
libprotoc 2.5.0

安装成功!进入源码得java目录,用mvn工具编译生成所需得jar包,protobuf-java-2.5.0rc1.jar

1、编写.proto文件,命名UserVo.proto

Text代码
  1. package serialize;

  2. option java_package = "serialize";

  3. option java_outer_classname="UserVoProtos";

  4. message UserVo{

  5. optional string name = 1;

  6. optional int32 age = 2;

  7. optional int64 phone = 3;

  8. repeated serialize.UserVo friends = 4;

  9. }

package serialize;option java_package = "serialize";
option java_outer_classname="UserVoProtos";message UserVo{optional string name = 1;optional int32 age = 2;optional int64 phone = 3;repeated serialize.UserVo friends = 4;
}

2、在命令行利用protoc 工具生成builder类

Shell代码
  1. protoc -IPATH=.proto文件所在得目录 --java_out=java文件的输出路径  .proto的名称

protoc -IPATH=.proto文件所在得目录 --java_out=java文件的输出路径  .proto的名称

得到UserVoProtos类

3、编写序列化代码

Java代码
  1. UserVoProtos.UserVo.Builder builder = UserVoProtos.UserVo.newBuilder();

  2. builder.setName("Yaoming");

  3. builder.setAge(30);

  4. builder.setPhone(13789878978L);

  5. UserVoProtos.UserVo.Builder builder1 = UserVoProtos.UserVo.newBuilder();

  6. builder1.setName("tmac");

  7. builder1.setAge(32);

  8. builder1.setPhone(138999898989L);

  9. UserVoProtos.UserVo.Builder builder2 = UserVoProtos.UserVo.newBuilder();

  10. builder2.setName("liuwei");

  11. builder2.setAge(29);

  12. builder2.setPhone(138999899989L);

  13. builder.addFriends(builder1);

  14. builder.addFriends(builder2);

  15. UserVoProtos.UserVo vo = builder.build();

  16. byte[] v = vo.toByteArray();

UserVoProtos.UserVo.Builder builder = UserVoProtos.UserVo.newBuilder();
builder.setName("Yaoming");
builder.setAge(30);
builder.setPhone(13789878978L);UserVoProtos.UserVo.Builder builder1 = UserVoProtos.UserVo.newBuilder();
builder1.setName("tmac");
builder1.setAge(32);
builder1.setPhone(138999898989L);UserVoProtos.UserVo.Builder builder2 = UserVoProtos.UserVo.newBuilder();
builder2.setName("liuwei");
builder2.setAge(29);
builder2.setPhone(138999899989L);builder.addFriends(builder1);
builder.addFriends(builder2);UserVoProtos.UserVo vo = builder.build();byte[] v = vo.toByteArray();

字节数53

4、反序列化

Java代码
  1. UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);

  2. System.out.println(uvo.getFriends(0).getName());

UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());

结果:tmac,反序列化成功

google protobuf 优点:字节数很小,适合网络传输节省io,跨语言 。缺点:需要依赖于工具生成代码。

工作机制

proto文件是对数据的一个描述,包括字段名称,类型,字节中的位置。protoc工具读取proto文件生成对应builder代码的类库。protoc xxxxx  --java_out=xxxxxx 生成java类库。builder类根据自己的算法把数据序列化成字节流,或者把字节流根据反射的原理反序列化成对象。官方的示例:https://developers.google.com/protocol-buffers/docs/javatutorial。

proto文件中的字段类型和java中的对应关系:

详见:https://developers.google.com/protocol-buffers/docs/proto

.proto Type java Type c++ Type
double double double
float float float
int32 int int32
int64 long int64
uint32 int uint32
unint64 long uint64
sint32 int int32
sint64 long int64
fixed32 int uint32
fixed64 long uint64
sfixed32 int int32
sfixed64 long int64
bool boolean bool
string String string
bytes byte string
字段属性的描述:
写道
required: a well-formed message must have exactly one of this field.
optional: a well-formed message can have zero or one of this field (but not more than one).
repeated: this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved.

protobuf 在序列化和反序列化的时候,是依赖于.proto文件生成的builder类完成,字段的变化如果不表现在.proto文件中就不会影响反序列化,比较适合字段变化的情况。做个测试:
把UserVo序列化到文件中:
Java代码
  1. UserVoProtos.UserVo vo = builder.build();

  2. byte[] v = vo.toByteArray();

  3. FileOutputStream fos = new FileOutputStream(dataFile);

  4. fos.write(vo.toByteArray());

  5. fos.close();

UserVoProtos.UserVo vo = builder.build();
byte[] v = vo.toByteArray();
FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(vo.toByteArray());
fos.close();

为UserVo增加字段,对应的.proto文件:
Text代码
  1. package serialize;

  2. option java_package = "serialize";

  3. option java_outer_classname="UserVoProtos";

  4. message UserVo{

  5. optional string name = 1;

  6. optional int32 age = 2;

  7. optional int64 phone = 3;

  8. repeated serialize.UserVo friends = 4;

  9. optional string address = 5;

  10. }

package serialize;option java_package = "serialize";
option java_outer_classname="UserVoProtos";message UserVo{optional string name = 1;optional int32 age = 2;optional int64 phone = 3;repeated serialize.UserVo friends = 4;optional string address = 5;
}

从文件中反序列化回来:
Java代码
  1. FileInputStream fis = new FileInputStream(dataFile);

  2. byte[] dstb = newbyte[fis.available()];

  3. for(int i=0;i<dstb.length;i++){

  4. dstb[i] = (byte)fis.read();

  5. }

  6. fis.close();

  7. UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);

  8. System.out.println(uvo.getFriends(0).getName());

FileInputStream fis = new FileInputStream(dataFile);
byte[] dstb = new byte[fis.available()];
for(int i=0;i<dstb.length;i++){dstb[i] = (byte)fis.read();
}
fis.close();
UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());

成功得到结果。

三种方式对比传输同样的数据,google protobuf只有53个字节是最少的。结论:
方式 优点 缺点
JSON

跨语言、格式清晰一目了然

字节数比较大,需要第三方类库
Object Serialize java原生方法不依赖外部类库 字节数比较大,不能跨语言
Google protobuf

跨语言、字节数比较少

编写.proto配置用protoc工具生成对应的代码

转载于:https://blog.51cto.com/kunyali/1347738

socket通信数据类型相关推荐

  1. java与c 通信_Java与C之间的socket通信

    最近正在开发一个基于指纹的音乐检索应用,算法部分已经完成,所以尝试做一个Android App.Android与服务器通信通常采用HTTP通信方式和Socket通信方式.由于对web服务器编程了解较少 ...

  2. C#中Socket通信编程的同步实现

    目录(?)[+] Socket编程基础 什么是Socket编程的同步实现 如何实现Socket同步通信 服务端 基本流程 代码示例 客户端 基本流程 代码示例 总结 本文通过分析和总结C#中Socke ...

  3. linux socket ip层配置,Linux下Socket通信(TCP实现)

    近期在做的项目中,涉及到了进程间数据传输,系统的原本实现是通过管道,但是原有的实现中两个进程是在同一台机器,而且两个进程的关系为父子关系,而我们要做的是将其中一个进程移植到服务器上,因此两个进程要分开 ...

  4. AgileEAS.NET SOA 中间件平台.Net Socket通信框架-完整应用例子-在线聊天室系统-代码解析...

    一.AgileEAS.NET SOA中间件Socket/Tcp框架介绍 在文章AgileEAS.NET SOA 中间件平台.Net Socket通信框架-介绍一文之中我们对AgileEAS.NET S ...

  5. Linux 下socket通信终极指南(附TCP、UDP完整代码)

    linux下用socket通信,有TCP.UDP两种协议,网上的很多教程把两个混在了一起,或者只讲其中一种.现在我把自己这两天研究的成果汇总下来,写了一个完整的,适合初学者参考,也方便自己以后查阅. ...

  6. Java Socket通信实现多人多端网络画板聊天室

    老规矩,先上实现的效果展示! Java Socket通信实现多人多端网络画板聊天室 本文介绍了一个基于Socket实现网络画板聊天室的完整过程,聊天室具备多人文本对话.同步绘图等功能. 初尝试 Soc ...

  7. C++ socket通信详解

    Socket是什么 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面, ...

  8. socket树莓派c语言,socket通信

    ## Socket 百度百科上对Socket的解释是:Socket通常称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信. # ...

  9. Python攻防-Socket通信建立TCP反向连接

    文章目录 前言 远程控制 脚本编写 效果演示 脚本优化 getopt () 完整代码 效果演示 前言 本文将记录学习基于 Socket 通信机制建立 TCP 反向连接,借助 Python 脚本实现主机 ...

最新文章

  1. codevs 2606 约数和(分块优化数学公式 )
  2. 改变UITableView的headerView、footerView背景颜色
  3. c++基础知识——算数运算符
  4. C/Cpp / 设计模式 / 模板模式
  5. eclipse开发javaweb项目中The superclass javax.servlet.http.HttpServlet was not found on the Java Build
  6. Silverlight 3 OOB 原理
  7. css怎么更改指定字体颜色,CSS-如何更改禁用输入的字体颜色?
  8. 电商促销活动那么多,美工需要炫酷海报万能模板!可套用!救急必备!
  9. 前端多行文本溢出问题解决方案
  10. VS2005最近项目和最近文件清除
  11. java mybatis配置,mybatis配置——Java版
  12. matlab multisim,清华大学出版社-图书详情-《仿真软件教程——Multisim和MATLAB》
  13. 楚留香服务器维护时间,【9月28日维护公告】
  14. ACM程序设计大赛简介
  15. docker exec详解
  16. 园区3D可视化三维展示系统解决方案
  17. IDEA打字冒火花教程
  18. SqlConnection,SqlDataAdapter,SqlCommand,SqlParameter
  19. E. Add Modulo 10(规律)
  20. MP3Stego的使用方法

热门文章

  1. 授予数据库账号dba权限_数据库用户和权限
  2. python怎么创建列表_用Python将一个列表分割成小列表的实例讲解 Python 如何创建一个带小数的列表...
  3. 苹果出5g手机吗_华为打响5G手机第一枪,苹果却扔出620亿“王炸”,任正非:榜样...
  4. c语言输入一段字符,C语言实现输入一个字符串后打印出该字符串中字符的所有排列...
  5. java swing 禁用鼠标事件_Java学习笔记:swing中树控件,设置树节点的图标,按钮美化,鼠标事件,禁止鼠标双击...
  6. 笑脸符号怎么存入mysql_让MySql支持Emoji表情存储
  7. flask运行多个服务器,与Flask服务器同时运行while循环
  8. Java的后缀分类_JAVA根据文件后缀名分类文件,并且将文件复制到不同的文件夹,求这段代码的注释...
  9. garch预测 python_安利几个非常实用的 Python 库
  10. python函数(一)