分享

Protobuf简单使用及其抓包分析

 阿修罗之狮猿授 2016-07-11

早之前就用过Google的Protobuf做数据编码,一直没有深入理解其中的原理,最近做了一次通讯抓包,发现其中很多Protobuf编码的数据包,于是决定分析一下其中的数据包及其编码。

一、Protobuf的使用

首先来简单介绍一下Protobuf的使用,这里以windows下Java开发为例,几个步骤:编写*.proto ->使用google提供的protoc.exe生成*.java->项目中导入protobuf的.jar包进行开发即可。先看这里的*.proto文件:

[plain] view plain copy
在CODE上查看代码片派生到我的代码片
  1. package com;  
  2.   
  3. message CMsg  
  4. {  
  5.     required string msghead = 1;  
  6.     required string msgbody = 2;  
  7. }  
  8.   
  9. message CMsgHead  
  10. {  
  11.     required int32 msglen = 1;  
  12.     required int32 msgtype = 2;  
  13.     required int32 msgseq = 3;  
  14.     required int32 termversion = 4;  
  15.     required int32 msgres = 5;  
  16.     required string termid = 6;  
  17. }  
  18.   
  19. message CMsgReg  
  20. {  
  21.     optional int32 area = 1;  
  22.     optional int32 region = 2;  
  23.     optional int32 shop = 3;  
  24.     optional int32 ret = 4;  
  25.     optional string termid = 5;  
  26. }  

使用protoc.exe生成java文件,命令如下:

将生成的Msg.java及protobuf-java-2.3.0.jar导入项目中进行开发,这里写一个服务器端ProtobufServer及客户端ProtobufClient

  1. package com;  
  2.   
  3. import java.io.DataInputStream;  
  4. import java.io.DataOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.net.ServerSocket;  
  8. import java.net.Socket;  
  9.   
  10. import com.Msg.CMsg;  
  11. import com.Msg.CMsgHead;  
  12. import com.Msg.CMsgReg;  
  13.   
  14. public class ProtoServer implements Runnable {  
  15.   
  16.     @Override  
  17.     public void run() {  
  18.         try {  
  19.             System.out.println("beign:");  
  20.             ServerSocket serverSocket = new ServerSocket(12345);  
  21.             while (true) {  
  22.                 System.out.println("等待接收用户连接:");  
  23.                 // 接受客户端请求  
  24.                 Socket client = serverSocket.accept();  
  25.   
  26.                 DataOutputStream dataOutputStream;  
  27.                 DataInputStream dataInputStream;  
  28.   
  29.                 try {  
  30.                     InputStream inputstream = client.getInputStream();  
  31.                     dataOutputStream = new DataOutputStream(  
  32.                             client.getOutputStream());  
  33.   
  34.                     byte len[] = new byte[1024];  
  35.                     int count = inputstream.read(len);  
  36.                     byte[] temp = new byte[count];  
  37.                     for (int i = 0; i < count; i++) {  
  38.                         temp[i] = len[i];  
  39.                     }  
  40.   
  41.                     CMsg msg = CMsg.parseFrom(temp);  
  42.   
  43.                     CMsgHead head = CMsgHead.parseFrom(msg.getMsghead()  
  44.                             .getBytes());  
  45.                     System.out.println("==len===" + head.getMsglen());  
  46.                     System.out.println("==res===" + head.getMsgres());  
  47.                     System.out.println("==seq===" + head.getMsgseq());  
  48.                     System.out.println("==type===" + head.getMsgtype());  
  49.                     System.out.println("==Termid===" + head.getTermid());  
  50.                     System.out.println("==Termversion==="  
  51.                             + head.getTermversion());  
  52.   
  53.                     CMsgReg body = CMsgReg.parseFrom(msg.getMsgbody()  
  54.                             .getBytes());  
  55.                     System.out.println("==area==" + body.getArea());  
  56.                     System.out.println("==Region==" + body.getRegion());  
  57.                     System.out.println("==shop==" + body.getShop());  
  58.   
  59.                     sendProtoBufBack(dataOutputStream);  
  60.                     inputstream.close();  
  61.   
  62.                 } catch (Exception ex) {  
  63.                     System.out.println(ex.getMessage());  
  64.                     ex.printStackTrace();  
  65.                 } finally {  
  66.                     client.close();  
  67.                     System.out.println("close");  
  68.                 }  
  69.             }  
  70.               
  71.         } catch (IOException e) {  
  72.             System.out.println(e.getMessage());  
  73.         }  
  74.     }  
  75.   
  76.     private byte[] getProtoBufBack() {  
  77.   
  78.         // head  
  79.         CMsgHead head = CMsgHead.newBuilder().setMsglen(10).setMsgtype(21)  
  80.                 .setMsgseq(32).setTermversion(43).setMsgres(54)  
  81.                 .setTermid("Server:head").build();  
  82.   
  83.         // body  
  84.         CMsgReg body = CMsgReg.newBuilder().setArea(11).setRegion(22)  
  85.                 .setShop(33).setRet(44).setTermid("Server:body").build();  
  86.   
  87.         // Msg  
  88.         CMsg msg = CMsg.newBuilder()  
  89.                 .setMsghead(head.toByteString().toStringUtf8())  
  90.                 .setMsgbody(body.toByteString().toStringUtf8()).build();  
  91.   
  92.         return msg.toByteArray();  
  93.     }  
  94.   
  95.     private void sendProtoBufBack(DataOutputStream dataOutputStream) {  
  96.   
  97.         byte[] backBytes = getProtoBufBack();  
  98.           
  99.         // Integer len2 = backBytes.length;  
  100.           
  101.         // byte[] cmdHead2 = BytesUtil.IntToBytes4(len2);  
  102.   
  103.         try {  
  104.             // dataOutputStream.write(cmdHead2, 0, cmdHead2.length);  
  105.             dataOutputStream.write(backBytes, 0, backBytes.length);  
  106.             dataOutputStream.flush();  
  107.         } catch (IOException e) {  
  108.             e.printStackTrace();  
  109.         }  
  110.     }  
  111.   
  112.     public static void main(String[] args) {  
  113.         Thread desktopServerThread = new Thread(new ProtoServer());  
  114.         desktopServerThread.start();  
  115.     }  
  116.   
  117. }  

  1. package com;  
  2.   
  3. import java.io.InputStream;  
  4. import java.net.Socket;  
  5.   
  6. import com.Msg.CMsg;  
  7. import com.Msg.CMsgHead;  
  8. import com.Msg.CMsgReg;  
  9.   
  10. public class ProtoClient {  
  11.   
  12.     public static void main(String[] args) {  
  13.         ProtoClient pc=new ProtoClient();  
  14.         System.out.println("beign:");  
  15.         pc.runget();  
  16.     }  
  17.   
  18.     public void runget() {  
  19.         Socket socket = null;  
  20.         try {  
  21.             //socket = new Socket("localhost", 12345);  
  22.             socket = new Socket("192.168.85.152", 12345);  
  23.             // head  
  24.             CMsgHead head = CMsgHead.newBuilder().setMsglen(5).setMsgtype(1)  
  25.                     .setMsgseq(3).setTermversion(41).setMsgres(5)  
  26.                     .setTermid("Client:head").build();  
  27.   
  28.             // body  
  29.             CMsgReg body = CMsgReg.newBuilder().setArea(11).setRegion(22)  
  30.                     .setShop(33).setRet(44).setTermid("Clent:body").build();  
  31.               
  32.             // Msg  
  33.             CMsg msg = CMsg.newBuilder()  
  34.                     .setMsghead(head.toByteString().toStringUtf8())  
  35.                     .setMsgbody(body.toByteString().toStringUtf8()).build();  
  36.   
  37.             // 向服务器发送信息  
  38.             System.out.println("sendMsg...");  
  39.             msg.writeTo(socket.getOutputStream());  
  40.   
  41.             // 接受服务器的信息  
  42.             InputStream input = socket.getInputStream();  
  43.   
  44.             System.out.println("recvMsg:");  
  45.             byte[] by = recvMsg(input);  
  46.             printMsg(CMsg.parseFrom(by));  
  47.   
  48.             input.close();  
  49.             socket.close();  
  50.         } catch (Exception e) {  
  51.             System.out.println(e.toString());  
  52.         }  
  53.     }  
  54.   
  55.     public void printMsg(CMsg g) {  
  56.   
  57.         try {  
  58.             CMsgHead h = CMsgHead.parseFrom(g.getMsghead().getBytes());  
  59.             StringBuffer sb = new StringBuffer();  
  60.             if (h.hasMsglen())  
  61.                 sb.append("==msglen===" + h.getMsglen() + "\n");  
  62.             if (h.hasMsgres())  
  63.                 sb.append("==msgres===" + h.getMsgres() + "\n");  
  64.             if (h.hasMsgseq())  
  65.                 sb.append("==msgseq===" + h.getMsgseq() + "\n");  
  66.             if (h.hasMsgtype())  
  67.                 sb.append("==msgtype===" + h.getMsgtype() + "\n");  
  68.             if (h.hasTermid())  
  69.                 sb.append("==termid===" + h.getTermid() + "\n");  
  70.             if (h.hasTermversion())  
  71.                 sb.append("==termversion===" + h.getTermversion() + "\n");  
  72.   
  73.             CMsgReg bo = CMsgReg.parseFrom(g.getMsgbody().getBytes());  
  74.             if (bo.hasArea())  
  75.                 sb.append("==area==" + bo.getArea() + "\n");  
  76.             if (bo.hasRegion())  
  77.                 sb.append("==region==" + bo.getRegion() + "\n");  
  78.             if (bo.hasShop())  
  79.                 sb.append("==shop==" + bo.getShop() + "\n");  
  80.             if (bo.hasRet())  
  81.                 sb.append("==ret==" + bo.getRet() + "\n");  
  82.             if (bo.hasTermid())  
  83.                 sb.append("==termid==" + bo.getTermid() + "\n");  
  84.   
  85.             System.out.println(sb.toString());  
  86.   
  87.         } catch (Exception e) {  
  88.             e.printStackTrace();  
  89.         }  
  90.   
  91.     }  
  92.   
  93.     public byte[] recvMsg(InputStream inpustream) {  
  94.         byte[] temp = null;  
  95.         try {  
  96.   
  97.             byte len[] = new byte[1024];  
  98.             int count = inpustream.read(len);  
  99.   
  100.             temp = new byte[count];  
  101.             for (int i = 0; i < count; i++) {  
  102.                 temp[i] = len[i];  
  103.             }  
  104.             return temp;  
  105.         } catch (Exception e) {  
  106.             System.out.println(e.toString());  
  107.             return temp;  
  108.         }  
  109.     }  
  110. }  

运行结果:

二、抓包分析

在上面socket通信过程中我使用了wireshark对其进行抓包,结果分析如下图

由上图我们可以很清楚的看到,protobuf编码其实类似tlv(tag length value)编码,其内部就是(tag, length, value)的组合,其中tag由(field_number<<3)|wire_type计算得出,field_number由我们在proto文件中定义,wire_type由protobuf根据proto中定义的字段类型决定,length长度采用一种叫做Varint 的数字表示方法,它是一种紧凑的表示数字的方法,用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数,具体细节可以谷歌Varint。总之Protobuf 序列化后所生成的二进制消息非常紧凑,这得益于 Protobuf 采用了上面的 Encoding 方法。

参考文献:http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/

源码下载:http://download.csdn.net/detail/wangqiuyun/8294015

转载请注明:http://blog.csdn.net/wangqiuyun/article/details/42119835



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多