日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

socket通信数据类型

發布時間:2024/1/17 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 socket通信数据类型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在java中socket傳輸數據時,數據類型往往比較難選擇。可能要考慮帶寬、跨語言、版本的兼容等問題。比較常見的做法有兩種:一是把對象包裝成JSON字符串傳輸,二是采用java對象的序列化和反序列化。隨著Google工具protoBuf的開源,protobuf也是個不錯的選擇。對JSON,Object Serialize,ProtoBuf 做個對比。

定義一個待傳輸的對象UserVo:

Java代碼
  • publicclass UserVo{ ?

  • private String name; ?

  • privateint age; ?

  • privatelong phone; ?


  • private List<UserVo> friends; ?

  • …… ?

  • } ?

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

    初始化UserVo的實例src:

    Java代碼
  • 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); ?

  • 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代碼
  • Gson gson = new Gson(); ?

  • String json = gson.toJson(src); ?

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

    得到的字符串:

    Js代碼
  • {"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代碼
  • publicclass UserVo implements Serializable{ ?


  • privatestaticfinallong serialVersionUID = -5726374138698742258L; ?

  • private String name; ?

  • privateint age; ?

  • privatelong phone; ?


  • 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代碼
  • ByteArrayOutputStream bos = new ByteArrayOutputStream(); ?

  • ObjectOutputStream os = new ObjectOutputStream(bos); ?

  • os.writeObject(src); ?

  • os.flush(); ?

  • os.close(); ?

  • byte[] b = bos.toByteArray(); ?

  • 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代碼
  • ObjectInputStream ois = new ObjectInputStream(fis); ?

  • vo = (UserVo) ois.readObject(); ?

  • ois.close(); ?

  • fis.close(); ?

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

    Object Serializalbe 優點:java原生支持,不需要提供第三方的類庫,使用比較簡單。缺點:無法跨語言,字節數占用比較大,某些情況下對于對象屬性的變化比較敏感。

    對象在進行序列化和反序列化的時候,必須實現Serializable接口,但并不強制聲明唯一的serialVersionUID

    是否聲明serialVersionUID對于對象序列化的向上向下的兼容性有很大的影響。我們來做個測試:

    思路一

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

    Java代碼
  • publicclass UserVo implements Serializable{ ?

  • private String name; ?

  • privateint age; ?

  • privatelong phone; ?


  • private List<UserVo> friends; ?

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


    保存到文件中:

    Java代碼
  • 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(); ?

  • 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代碼
  • FileInputStream fis = new FileInputStream(dataFile); ?

  • ObjectInputStream ois = new ObjectInputStream(fis); ?

  • vo = (UserVo) ois.readObject(); ?

  • ois.close(); ?

  • fis.close(); ?

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


    結果:拋出異常信息

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

  • ? ?at 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) ?

  • 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代碼
  • tar zxvf protobuf-2.5.0rc1.tar.gz ?

  • ./configure ?

  • ./make ?

  • ./make install ?

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

    測試:

    Shell代碼
  • MacBook-Air:~ ming$ protoc --version ?

  • 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代碼
  • 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; ?

  • } ?

  • 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代碼
  • protoc -IPATH=.proto文件所在得目錄 --java_out=java文件的輸出路徑 ?.proto的名稱 ?

  • protoc -IPATH=.proto文件所在得目錄 --java_out=java文件的輸出路徑 .proto的名稱

    得到UserVoProtos類


    3、編寫序列化代碼

    Java代碼
  • 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(); ?

  • 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代碼
  • UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb); ?

  • 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代碼
  • UserVoProtos.UserVo vo = builder.build(); ?

  • byte[] v = vo.toByteArray(); ?

  • FileOutputStream fos = new FileOutputStream(dataFile); ?

  • fos.write(vo.toByteArray()); ?

  • 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代碼
  • 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; ?

  • } ?

  • 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代碼
  • FileInputStream fis = new FileInputStream(dataFile); ?

  • byte[] dstb = newbyte[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()); ?

  • 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 Serializejava原生方法不依賴外部類庫字節數比較大,不能跨語言
    Google protobuf

    跨語言、字節數比較少

    編寫.proto配置用protoc工具生成對應的代碼


    轉載于:https://blog.51cto.com/kunyali/1347738

    總結

    以上是生活随笔為你收集整理的socket通信数据类型的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。