• 协议分析处理工具ProtoBuf


      protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍。后面将会有简单的demo对于这两种格式的数据转化效率的对比。

    简单设计协议, 通过自带工具转换成为对应的语言代码, 协议是二进制协议, 设计时只需要描述各个类的关系, 简单明了

    Protobuf 提供了C++、java、python语言的支持,提供了windows(proto.exe)和linux平台动态编译生成proto文件对应的源文件。proto文件定义了协议数据中的实体结构(message ,field)

    关键字message: 代表了实体结构,由多个消息字段(field)组成。

    消息字段(field): 包括数据类型、字段名、字段规则、字段唯一标识、默认值

    数据类型:常见的原子类型都支持(在FieldDescriptor::kTypeToName中有定义)

    字段规则:(在FieldDescriptor::kLabelToName中定义)

            required:必须初始化字段,如果没有赋值,在数据序列化时会抛出异常

            optional:可选字段,可以不必初始化。

            repeated:数据可以重复(相当于java 中的Array或List)

            字段唯一标识:序列化和反序列化将会使用到。

    默认值:在定义消息字段时可以给出默认值。

    常用序列化格式介绍

    目前JAVA常用的序列化有protobuf,json,xml,Serializable,hessian,kryo。他们的优缺点如下:

    • JSON:不多说了,用途广泛,序列化方式还衍生了阿里的fastjson,美团的MSON,谷歌的GSON等更加优秀的转码工具。
      优点:使用方便。
      缺点:数据冗长,转码性能一般。

    • XML:很久之前的转码方法了,现在用的不多。
      优点:暂时没发现。
      缺点:数据冗长,转码性能一般。

    • Serialzable:JDK自带的序列化。
      优点:使用方便。
      缺点:转码性能低下。

    • hessian:基于 binary-RPC实现的远程通讯library,使用二进制传输数据。
      优点:数据长度小。
      缺点:性能低下。

    用法:

    • 设计协议是在fileName.proto文件中, 其中fileName是自己定义, 在通过protoc转换成为对应的代码。

    • 关键字:

      • message 表示一个消息体, 相当于一个类。
      • 每个message中都有对应的变量, 每个变量有个对应的编号, 每个message内不同变量的编号不能重复。
      • 新版本的protobuf没有了required, optional等说明关键字, 都默认为optional
    • 基本语法

      //指定版本 使用protobuf3
      syntax = "proto3";
      
      message Account {
      	//账号
      	uint64 ID = 1;
      	//名字
      	string name = 2;
      	//密码
      	string password = 3;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      ​ 语法很简单, 上面例子就是指定使用protob3, 然后定义了一个Account类, 里面包含ID, name, password, 对应的类型是 uint64, string, string。

      ​ 所以基本的语法规则跟C++类似。 只不过多了后面变量的编号。

    • 生成代码:

      需要生成什么语言的代码需要直接指定,例如C++

      protoc --cpp_out=./ project.proto

      protoc是工具名, 可以直接运行的命令。

      --cpp_out是参数, 指定生成C++代码, =后面指定生成的目录。

      project.proto是定义的文件。

      一共会生成两个文件。 project.pb.h 和 project.pb.cc

      其中有生成的Account类中有这几个设置属性的方法

      void clear_name();
      static const int kNameFieldNumber = 2;
      const std::string& name() const;
      void set_name(const std::string& value);
      void set_name(std::string&& value);
      void set_name(const char* value);
      void set_name(const char* value, size_t size);
      std::string* mutable_name();
      std::string* release_name();
      void set_allocated_name(std::string* name);
      
      // string password = 3;
      void clear_password();
      static const int kPasswordFieldNumber = 3;
      const std::string& password() const;
      void set_password(const std::string& value);
      void set_password(std::string&& value);
      void set_password(const char* value);
      void set_password(const char* value, size_t size);
      std::string* mutable_password();
      std::string* release_password();
      void set_allocated_password(std::string* password);
      
      // uint64 ID = 1;
      void clear_id();
      static const int kIDFieldNumber = 1;
      ::PROTOBUF_NAMESPACE_ID::uint64 id() const;
      void set_id(::PROTOBUF_NAMESPACE_ID::uint64 value);
      
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30

      及get/set方法。

      可以直接操作该类的属性。

    • 使用该代码

      • 在要使用的代码中包含此头文件project.pb.h

        直接使用该对象即可。编译的时候需要把project.pb.cc编译, 并在链接的时候链接动态链接库 libprotobuf.so

    • demo

      #include <iostream>
      #include <string>
      #include "project.pb.h"
      
      
      int main()
      {
      	Account account;
      	account.set_id(1000);
      	account.set_name("name");
      	account.set_password("password");
      
      	//序列化
      	std::string s = account.SerializeAsString();
      	if(s.size() == 0) {
      		std::cout << "error in SerializeAsString" << std::endl;
      	}
      	Account nAccount;
          //反序列化
      	if(nAccount.ParseFromString(s)) {
      		std::cout << nAccount.id() << std::endl;
      		std::cout << nAccount.name() << std::endl;
      		std::cout << nAccount.password() << std::endl;
      	} else {
      		std::cout << "error in ParseFromString" << std::endl;
      	}
      
      	return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
    • 编译

      project.pb.cc也需要编译, 如果是工程中, 可以单独编译, 由于只有一个文件所以就放到一块编译了g++ demo.cpp project.pb.cc -lprotobuf -o main

      输出结果为:

      1000

      name

      password

    更复杂的demo

    • message的嵌套

      message就像类一样, 所以它也是可以嵌套的。

      可以直接在message内写, 也可以在外部写, 但是要注意的是, 一个message内的编号不能重复。

      所以令写一个message会节省编号。

      节省编号的所有是节省空间。 前15号(0~15)用一个字节, 后面以此类推, 两个三个字节, 所以前15编号比较珍贵。

    • demo

      //指定版本 使用protobuf3
      syntax = "proto3";
      
      message Account {
      	//账号
      	uint64 ID = 1;
      	//名字
      	string name = 2;
      	//密码
      	string password = 3;
      	//宠物狗
      	Dog dog = 4;
      }
      
      message Dog {
      	string name = 0;
      	bool sex = 1;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      还有几个比较重要的方法

      set_allocated_dog dog不是必须的, 是类的名字。 可以是其他的名字

      • 参数是一个对应类型的指针, 调用这个函数, 它会自动释放这个指针的对象, 所以不需要delete, 否则会段错误。
      • 再次调用这个参数时, 如果之前已经设置过参数, 那么再次调用的时候会之前设置的属性删除, 并且delete越来的对象。 在设置成新的属性。

      除了这些方法之外还有一些clear之类的方法, 需要可以看文档或者给的头文件。

    oneof

    oneof 是设置多个属性中的一个, 例如, 我的宠物可以是狗, 也可以是猫, 但是每个人只有一个的话。 设置两个占的空间就有点大。 因此只需要有一个就够了, 所以这个oneof就像union一样。

    //指定版本 使用protobuf3
    syntax = "proto3";
    
    message Account {
    	//账号
    	uint64 ID = 1;
    	//名字
    	string name = 2;
    	//密码
    	string password = 3;
    	//宠物
    	oneof pet {
    		Dog dog = 4;
    		Cat cat = 5;
    	}
    }
    
    message Dog {
    	string name = 1;
    	bool sex = 2;
    }
    
    message Cat {
    	string name = 1;
    	//属性可以与Dog不同
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 方法
      • has_cat 或者 has_dog 方法用于检测是否有cat/dog
      • 添加cat和dog直接用原来的方法添加即可。 但是当添加dog的时候会自动删除cat, 反之亦然。

          protobuf: install:https://blog.csdn.net/hailong0715/article/details/52057873

    https://blog.csdn.net/skh2015java/article/details/78404235

    参考:其他的协议分析工具:https://www.cnblogs.com/protosec/p/11673374.html

      protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍。后面将会有简单的demo对于这两种格式的数据转化效率的对比。

  • 相关阅读:
    省选测试28
    省选测试27
    省选测试26
    省选测试25
    省选测试24
    省选测试23
    省选测试22
    省选测试21
    关于maven 导入依赖的最终处理问题
    Dubbo 2 之抽取公共接口
  • 原文地址:https://www.cnblogs.com/klb561/p/13950329.html
Copyright © 2020-2023  润新知