精华内容
下载资源
问答
  • protobuf3基础语法
    千次阅读
    2020-12-22 20:33:21

    简介

    ProtoBuf (Google Protocol Buffer)是由google公司用于数据交换的序列结构化数据格式,具有跨平台、跨语言、可扩展特性,同类型有常用的XML及JSON,但具有更小的传输体积、更高的编码、解码能力,特别适合于数据存储、网络数据传输等对存储体积、实时性要求高的领域,目前已经发展到protoc3 版本。

    优点:空间效率高,时间效率要高,对于数据大小敏感,传输效率高的

    缺点:消息结构可读性不高,序列化后的字节序列为二进制序列不能简单的分析有效性

    基本结构

    syntax="proto3"; //文件第一行指定使用的protobuf版本,如果不指定,默认使用proto2

    package services; //定义proto包名,可以为.proto文件新增一个可选的package声明符,可选

    option go_package = ".;services"; //声明编译成go代码后的package名称,可选的,默认是proto包名

    message ProdRequest{ //messaage可以理解为golang中的结构体,可以嵌套

    int32 prod_id=1; //变量的定义格式为:[修饰符][数据类型][变量名] = [唯一编号] ,同一个message中变量的编号不能相同

    }

    message ProdResponse{

    int32 pro_stock=1;

    }

    service ProdService{ //定义服务

    rpc GetProdStock (ProdRequest) returns (ProdResponse); //rpc方法

    }

    变量类型

    ProtoBuf

    Golang

    int32/sint32/sfixed32

    int32

    int64/sint64/sfixed64

    int64

    uint32/fixed32

    uint32

    uint64/fixed64

    uint64

    float

    float32

    double

    float64

    bool

    bool

    string

    string

    bytes

    []byte

    enum

    数组或slice

    google.protobuf.Timestamp

    timestamp.Timestamp

    备注:最后的时间类型golang需要引入包github.com/golang/protobuf/ptypes/timestamp,定义如下

    t:=timestamp.Timestamp(time.Now().Unix())

    然后.protp文件需要导入google/protobuf/timestamp.proto

    修饰符

    repeated

    如果一个字段被repeated修饰,则表示它是一个列表类型的字段,相当于golang里的切片

    message SearchRequest {

    repeated string args = 1 // 列表类型

    }

    reserved

    如果你希望可以预留一些数字标签或者字段可以使用reserved修饰符

    message Foo {

    reserved 2, 15, 9 to 11;

    reserved "foo", "bar";

    string foo = 3 // 编译报错,因为‘foo’已经被标为保留字段

    }

    默认值

    类型

    默认值

    string

    ""

    bytes

    空字节

    bool

    false

    数字

    0

    enum

    第一个值

    message

    跟编程语言有关

    repeated

    空列表

    枚举类型

    第一个枚举值的数值必须是0且至少有一个枚举值,一个数值可以对应多个枚举值,必须标明option allow_alias = true;不推荐使用负数值

    message SearchRequest {

    string query = 1;

    int32 page_number = 2;

    int32 result_per_page = 3;

    enum Corpus {

    UNIVERSAL = 0;

    WEB = 1;

    IMAGES = 2;

    LOCAL = 3;

    NEWS = 4;

    PRODUCTS = 5;

    VIDEO = 6;

    }

    Corpus corpus = 4;

    }

    enum EnumAllowingAlias {

    option allow_alias = true;

    UNKNOWN = 0;

    STARTED = 1;

    RUNNING = 1;

    }

    导入文件

    import "myproject/other_protos.proto"; // 这样就可以引用在other_protos.proto文件中定义的message,不能导入不使用的.proto文件

    定义服务

    在你的 .proto 文件中指定 service,然后在service里定义rpc方法即可,要注意指定参数和返回值

    service RouteGuide {

    rpc GetFeature(Point) returns (Feature) {}

    }

    rpc方法

    gRPC 允许你定义4种类型的 service 方法

    简单rpc

    客户端使用存根发送请求到服务器并等待响应返回,就像平常的函数调用一样

    service RouteGuide {

    rpc GetFeature(Point) returns (Feature) {}

    }

    服务端流式rpc

    通过在 响应返回参数 类型前插入 stream 关键字,可以指定一个服务器端的流方法。客户端发送请求到服务器,拿到一个流去读取返回的消息序列。 客户端读取返回的流,直到里面没有任何消息。

    service RouteGuide {

    rpc ListFeatures(Rectangle) returns (stream Feature) {}

    }

    客户端流式rpc

    通过在 请求参数 类型前指定 stream 关键字来指定一个客户端的流方法。客户端写入一个消息序列并将其发送到服务器,同样也是使用流。一旦客户端完成写入消息,它等待服务器完成读取返回它的响应。

    service RouteGuide {

    rpc RecordRoute(stream Point) returns (RouteSummary) {}

    }

    双向流式rpc

    通过在请求和响应前加 stream 关键字去制定方法的类型。两个流独立操作,因此客户端和服务器可以以任意喜欢的顺序读写:比如, 服务器可以在写入响应前等待接收所有的客户端消息,或者可以交替的读取和写入消息,或者其他读写的组合。

    service RouteGuide {

    rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

    }

    更多相关内容
  • 主要介绍了在 Ubuntu 上安装 Protobuf 3遇到问题及解决方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下
  • protobuf3转java工具

    2019-01-24 10:23:09
    适用于protobuf3协议规则转换为java文件的一个工具类,操作简便,谷歌下载不易。
  • 包含了proto2版本的protocol buffer语言:对于老版本的proto3 符号,请见Proto2 Language Guide(以及中文译本,抄了很多这里的感谢下老版本的翻译者) 本文是一个参考指南——如果要查看如何使用本文中描述的多个...
  • protobuf3.0以后,在windows上使用时,往往需要连入.lib文件,但有些项目对连入.lib文件有限制。所以需要直接使用源码编译,但protobuf代码的vs2005上的编译存在问题。附件是修改之后的,可以在vs2005上编译通过的...
  • protobuf3-layaair_AS3-master

    2017-06-02 14:12:22
    layaair_AS3版本可用的protobuf3协议,亲测可用。官方目前为止没有提供AS3版本的protobuf3的API哟~!
  • protobuf3封装工具 c# golang windows.rar win: protogen -i:msg.proto -o:msg.cs -ns:Pb
  • unity-protobuf3

    2019-02-27 19:13:04
    unity通讯之protobuf文件传输,protobuf3序列化与反序列化。
  • 使用lua-intf将Protobuf与Lua绑定,从而支持lua53和proto3。 LuaPbIntf的灵感来自 ,但已被重写以利用优势,这使绑定更易于使用和理解。 LuaPbIntf将动态加载协议缓冲区消息定义文件.proto而不生成代码。 ...
  • Protobuf3CSharpForMac.zip

    2020-02-24 17:19:07
    此工具为Mac下的Protobuf3批量转C#工具,下载后将需要转换的Protobuf3放入文件夹,在该目录下运行终端 命令参考readme
  • 协议缓冲区 3 proto3 (PRERELEASE) 的一个水龙头。 当前支持的编译器: C++(捆绑) Python 去 安装 这是一个 PRERELEASE,所以命令更加冗长,以确保您知道自己在做什么。...λ brew link protobuf3
  • ProtoBuf3安装与使用

    万次阅读 2018-11-08 17:08:16
    一、ProtoBuf在Ubuntu环境下安装 参考:官方安装文档 安装以下工具集 sudo apt-get install autoconf automake libtool curl make g++ unzip 在线下载网址:...

    本文参考自官方文档以及大佬们博客,实测完成。

    一、ProtoBuf在Ubuntu环境下安装

    参考:官方安装文档

    安装以下工具集
    sudo apt-get install autoconf automake libtool curl make g++ unzip
    

    在线下载网址:https://github.com/protocolbuffers/protobuf/releases/,然后解压并安装。
    也可以通过git安装:

    //1. 下载最新版本
    git clone https://github.com/protocolbuffers/protobuf.git
    cd protobuf
    git submodule update --init --recursive
    ./autogen.sh
    
    //2. build 并 install
    ./configure
     make
     make check
     sudo make install
     sudo ldconfig # refresh shared library cache.
    
    //3. 验证
    protoc --version
    输出:libprotoc 3.6.1
    
    Note:

    安装位置
    默认安装位置在/usr/local,然而大部分平台中usr/local/lib并没有添加在环境变量LD_LIBRARY_PATH中。简单起见,可以直接选择在环境变量中的路径,比如/usr进行安装:

    ./configure --prefix=/usr
    

    config 出错则使用命令make clean进行清除配置。

    Compiling dependent packages:
    pkg-config --cflags protobuf         # print compiler flags
    pkg-config --libs protobuf           # print linker flags
    pkg-config --cflags --libs protobuf  # print both
    

    比如后面我们编译代码,便利用了其中的lib选项:

    g++ -std=c++11 list_person.cpp Person.pro.pb.cc -o list_people `pkg-config --cflags --libs protobuf`
    

    二、代码测试

    参考官网实验(proto2):https://developers.google.com/protocol-buffers/docs/cpptutorial
    结合这位博主已经用proto3修改:https://blog.csdn.net/hailong0715/article/details/52057873?utm_source=blogxgwz5

    定义一个ProtoBuf源文件: Person.pro

    syntax="proto3";
    package tutorial;
    message Person
    {
    	string name = 1;
    	int32 id = 2;
    	string email = 3;
     
    	enum PhoneType
    	{
    		MOBILE = 0;
    		HOME = 1;
    		WORK = 2;
    	}
     
    	message PhoneNumber
    	{
    		string number = 1;
    		PhoneType type = 2; 
    	}
     
    	repeated PhoneNumber phone = 4;
    }
     
    message AddressBook
    {
    	repeated Person person =1;
    }
    
    

    可以看到该文件中,有一点类似C语言结构体的风格,自定义数据结构类型并且可以嵌套。Person类型中包含了三个基本类型:name,id,email,同时又包含一个复合类型PhoneNumber

    编译该文件:

    protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/Person.proto
    
    //这里在当前路径下编译即可
    protoc -I=./ --cpp_out=./ ./Person.pro
    

    编译后生成Person.proto,pb.hPerson.pro.pb.cc文件,这两个文件包含了Person以及AddressBook的类,以及对应操作类的方法,在你使用者这些类必须包含头文件,最终可执行文件也必须将cc文件也编译进去。

    add_person.cpp 展示了如何设置一个联系人的各个字段的信息,并且最终序列化信息到文件中。

    //add_person.cpp
    #include "Person.pro.pb.h"
    #include <fstream>
    #include <iostream>
    using namespace std;
     
    void PromptForAddress(tutorial::Person*);
     
    int main(int argc, char* argv[])
    {
    	GOOGLE_PROTOBUF_VERIFY_VERSION;
    	if(2 != argc)
    	{
    		//必须指定电话本名称才执行程序
    	    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    	    return -1;
    	}
     
    	tutorial::AddressBook address_book;
     
    	fstream in("ADDRESS_BOOK_FILE", ios::binary | ios::in);
    	if(!in)
    	{
    		cerr << "open file ADDRESS_BOOK_FILE failed!\n";
    		return -1;
    	}
    	
    	if(!address_book.ParseFromIstream(&in))
    	{
    		cerr << "Parse File ADDRESS_BOOK_FILE failed!\n";
    		return -1;
    	}
    	
    	in.close();
    	//增加一个Person,可通过多次调用该接口增加联系人
    	//具有repeated的属性可通过add_fieldname方法增加一个属性
    	PromptForAddress(address_book.add_person());
     
    	fstream out("ADDRESS_BOOK_FILE", ios::binary | ios::out | ios::trunc);
    	if(!address_book.SerializeToOstream(&out))
    	{
    		cerr << "Failed to Write Address Book!\n";
    		return -1;	
    	}
     
    	//可选的,回收所有ProtoBuf分配的对象
    	google::protobuf::ShutdownProtobufLibrary();
    	return 0;
    }
     
     
    void PromptForAddress(tutorial::Person* person)
    {
    	cout<<"Enter a Person ID number: ";
    	int id;
    	cin >> id;
    	person->set_id(id);
    	/*忽略CIN的前256个字符,或者忽略CIN的换行符之前的字符,包括换行符
    	这样的话不会将换行符之前的其他类型的数据保留在输入缓冲中	
    	*/
    	cin.ignore(256, '\n');
    	cout<<"Enter name: ";
    	getline(cin, *person->mutable_name());
     
    	cout<< "Enter email address (blank for none): ";
    	string email;
    	getline(cin,email);
    	if(!email.empty())
    		person->set_email(email);
    	while(true)
    	{
    		cout<<"Enter a phone number (or leave blank to finish): ";
    		string number;
    		getline(cin, number);
    		if(number.empty())
    			break;
    		tutorial::Person::PhoneNumber* phone_number = person->add_phone();
    		phone_number->set_number(number);
     
    		cout<<"Is this a mobile, home, or work phone? ";
    		string type;
    		getline(cin, type);
    		if(type == "mobile")
    			phone_number->set_type(tutorial::Person::MOBILE);
    		else if( type == "home")
    			phone_number->set_type(tutorial::Person::HOME);
    		else if (type == "work")
    			phone_number->set_type(tutorial::Person::WORK);
    		else
    		{
    			cout << "Unknown phone type.  Using default." << endl;
    			phone_number->set_type(tutorial::Person::HOME);
    		}
    		
    	}
    	
    }
    
    

    list_person.cpp展示了如何从文件中读取ProtoBuf序列化的信息

    //list_person.cpp
    #include "Person.pro.pb.h"
    #include <iostream>
    #include <fstream>
     
    using namespace std;
    void ListPeople(const tutorial::AddressBook& address_book);
    int main(int argc, char * argv[])
    {
    	if(2!=argc)
    	{
    	    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    	    return -1;
    	}
     
    	fstream in("ADDRESS_BOOK_FILE", ios::in | ios::binary);
    	tutorial::AddressBook address_book;
    	if(!address_book.ParseFromIstream(&in))
    	{
    	    cerr << "Parse Input File failed!"<< endl;
    	    return -1;
    	}
     
    	ListPeople(address_book);
    	google::protobuf::ShutdownProtobufLibrary();
    	return 0;
    }
     
    void ListPeople(const tutorial::AddressBook& address_book)
    {
    	//fieldName_size方法返回具有repeated属性的个数
    	for(int i=0; i< address_book.person_size();i++)
    	{
    		const tutorial::Person& person = address_book.person(i);
    		cout<<"Person ID: "<<person.id();
    		cout<<"Name: "<<person.name();
    		cout<<"Email: "<<person.email();
    		
    		for(int j=0; j< person.phone_size();j++)
    		{
    			const tutorial::Person::PhoneNumber& phone_number = person.phone(j);
    			switch(phone_number.type())
    			{
    		        case tutorial::Person::MOBILE:
    		          cout << "  Mobile phone #: ";
    		          break;
    		        case tutorial::Person::HOME:
    		          cout << "  Home phone #: ";
    		          break;
    		        case tutorial::Person::WORK:
    		          cout << "  Work phone #: ";
    		          break;
    			}
    			cout<<phone_number.number()<<endl;	
    		}
    		
    	}
    }
    
    

    Makefile

    test:add_person list_person
    
    add_person: add_person.cpp protoMid
    	 g++ -std=c++11 list_person.cpp Person.pro.pb.cc -o add_person `pkg-config --cflags --libs protobuf`
    list_person: list_person.cpp protoMid
    	 g++ -std=c++11 add_person.cpp Person.pro.pb.cc -o list_person  `pkg-config --cflags --libs protobuf`
    protoMid: Person.pro
    	 protoc -I=./ --cpp_out=./ ./Person.pro
    
    clean: 
    	 rm -rf add_person list_person protoMid
    	 rm -rf Person.pro.pb.cc Person.pro.pb.h
    
    

    其中,protoMid则利用了protoc将Person.pro文件生成对应的.cpp和.h文件
    执行make后,生成add_people 和list_people两个可执行文件,可以先执行add_people文件增加联系人信息,再执行list_people显示所有联系人信息

    三、后期学习文章

    goole官网proto3文档
    goole官网proto2实验
    Protocol Buffer技术详解(语言规范)
    Protocol Buffer技术详解(C++实例)
    官方译文:Protobuf3语言指南

    展开全文
  • tolua中使用protobuf3—集成lua-protobuf

    千次阅读 2020-09-01 10:06:23
    lua-protobuf ... 项目使用protobuf3,pbc已经几年未更新,担心不能很好的支持或不支持protobuf3 但lua-protobuf实际上是一个纯C的protobuf协议实现,为了可以在tolua中使用,将tolua中pbc(pb.c),替换

    lua-protobuf

    最近项目中需要使用lua版本的protobuf库,在Github上找到了lua-protobuf用来替换tolua中自带的pbc库,替换的原因主要有以下:

    • 正如lua-protobuf作者所说: pbc返回的并不是纯粹的Lua表,使用不方便。
    • 项目使用protobuf3,pbc已经几年未更新,担心不能很好的支持或不支持protobuf3

    但lua-protobuf实际上是一个纯C的protobuf协议实现,为了可以在tolua中使用,将tolua中pbc(pb.c),替换成lua-protobuf(pb.c和pb.h),然后编译成相应平台支持的库文件(win tolua.dll, android libtolua.so, mac tolua.bundle, ios libtolua.a)

    lua-protobuf pb.c中luaL_newlib为lua5.2版本才支持的语法,为了支持tolua的luajit(lua版本为5.1)在如下为止做了修改(如果lua版本小于5.2,则使用luaL_register方法,并且需要在C#中导入否则lua代码中不到pb模块):

    修改为

    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
    LUALIB_API int luaopen_pb_io(lua_State *L)
    {
        // 。。。
        if (LUA_VERSION_NUM < 502)
        {
             luaL_register(L, "pb.io", libs);
        }
        else
        {
            luaL_newlib(L, libs);
        }
        return 1;
    }
    
    LUALIB_API int luaopen_pb_conv(lua_State *L)
    {
        // 。。。
        if (LUA_VERSION_NUM < 502)
        {
             luaL_register(L, "pb.io", libs);
        }
        else
        {
            luaL_newlib(L, libs);
        }
        return 1;
    }
    
    LUALIB_API int luaopen_pb(lua_State *L)
    {
        // 。。。
        if (LUA_VERSION_NUM < 502)
        {
             luaL_register(L, "pb", libs);
        }
        else
        {
            luaL_newlib(L, libs);
        }
        return 1;
    }
    

    复制

    lua-protobuf作者在评论中说,必须这么修改的原因是tolua的OpenLibs函数实现错误,它直接调用了luaopen函数而没有通过lua_call方式去调用,并且可以使用#if #else #end 做编译优化。并且修改了最近的源代码,通过HEAD得到的lua-protobuf源代码现在不需修改也可以直接使用了,大家去可以用最新的源代码编译下试试。

    自编译tolua

    如何编译请参考

    以下为上面文章补充:

    android

    如何编译各平台使用的库-以编译tolua为例中提到的

    将ndk路径添加到mingw-32的环境变量PATH中

    可以打开mingw32.ini中MSYS2_PATH_TYPE=inherit的注释,表示使用windows系统配置的环境变量,这样将ndk路径配置到window环境变量中即可正常使用。

    mingw-32.in完整配置i如下所示:

    1
    2
    
    MSYS2_PATH_TYPE=inherit
    MSYSTEM=MINGW32
    

    复制

    mac&ios

    • 在mac终端导航到tolua根目录中,直接在终端执行./build_osx.sh和./build_ios.sh即可
    • 如果执行.sh提示权限不足 可以在终端执行:chmod 777 tolua根目录 然后再次执行.sh脚本即可
    • 如果执行build_osx.sh时提示不在支持32位,请打开macnojit 在build Setting移除i386的支持即可

    完成编译之后将tolua根目录中Plugins中对于的文件拷贝到Unity Plugins文件中进行替换,并且将lua_protobuf附带的protoc.lua(位于lua-protobuf文件夹)拷贝到工程中。

    导入lua-protobuf

    在tolua C#部分 LuaDLL.cs中添加如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
    public static extern int luaopen_pb(IntPtr L);
    
    [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
    public static extern int luaopen_pb_io(IntPtr L);
    
    [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
    public static extern int luaopen_pb_conv(IntPtr L);
    
    [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
    public static extern int luaopen_pb_buffer(IntPtr L);
    
    [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
    public static extern int luaopen_pb_slice(IntPtr L);
    

    复制

    并且在启动lua时,将上述模块导入即可:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    void OpenLibs()
    {
           lua.OpenLibs(LuaDLL.luaopen_pb_io);
           lua.OpenLibs(LuaDLL.luaopen_pb_conv);
           lua.OpenLibs(LuaDLL.luaopen_pb_buffer);
           lua.OpenLibs(LuaDLL.luaopen_pb_slice);
           lua.OpenLibs(LuaDLL.luaopen_pb);
                
           //其他的库
    }
    

    复制

    验证集成

    首先需要验证是否集成成功:

    使用lua-protobuf作者提供的例子并且修改为proto3协议:

    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
    31
    32
    33
    34
    35
    
    local pb = require "pb"
    local protoc = require "3rd/lua-protobuf/protoc"
    
    -- load schema from text
    assert(protoc:load [[
        syntax = "proto3";
        
        message Phone {
            string name = 1;
            int64 phonenumber = 2;
        }
        message Person {
            string name = 1;
            int32 age = 2;
            string address = 3;
            repeated Phone contacts = 4;
        } ]])
        
    -- lua table data
    local data = {
        name = "ilse",
        age = 18,
        contacts = {
            { name = "alice", phonenumber = 12312341234 },
            { name = "bob", phonenumber = 45645674567 }
        }
    }
    
    -- encode lua table data into binary format in lua string and return
    local bytes = assert(pb.encode("Person", data))
    print(pb.tohex(bytes))
    -- and decode the binary data back into lua table
    local data2 = assert(pb.decode("Person", bytes))
    print(require "3rd/lua-protobuf/serpent".block(data2))
    

    复制

    如果可以正常打印(最好在移动平台上也进行测试),则表示集成成功。

    lua-protobuf基本用法

    首先可以使用上面的测试例子的用法:直接使用lua中字符串加载协议,优点为不用使用其额外的文件,缺点在与protobuf协议为服务端和客户端共同使用,修改不方便。

    lua-protobuf 使用说明中作者展示另一种使用方式,动态加载生成的二进制pb文件。小编推荐这种用法,并且在当前开发项目中进行了进一步的实践。

    以下为具体的做法:

    首先在protoc下载相应的版本protoc.exe版本,并且配置到环境变量中。

    然后新建用于处理proto文件的目录

     

    Proto的目录中存放了.proto协议文件,proto2pb.bat、proto2java.bat和proto2cs.bat为生成相对于代码的批处理目录,bat文件在生成对于的文件之后,将自动拷贝到对于的工程中。

    项目中proto协议设置

    在目前开发的项目中对于每一个请求返回消息流程对应一个proto协议文件,协议内部嵌套Request和Response,如果为服务端推送则只需要Response,如果不需要服务端返回则只需要Request

    以下具体的协议的实例(GetTableId.proto):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    syntax = "proto3";
    package Test;
    
    option java_package = "com.project.protocol";
    option java_outer_classname = "GetGetTableIdOuter"
    message GetTableId {
        message Request{
            string roomId = 1;
            int32 tableId = 2;
        }
        message Response {
            string tableId = 1;
    }
    

    复制

    bat文件的源代码

    proto2pb.bat代码如下:

    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    @echo off
    cd Proto
    REM 生成二进制文件pb(首先删除在重新生成)
    rd /s /q ..\pb
    md ..\pb
    set pbPath=..\pb\
    set pb=.pb
    for %%i in (*.*) do (
        echo %%i
        protoc -o %pbPath%%%i%pb% %%i
    )
    
    echo pb create success
    echo.
    
    cd ..\pb
    SetLocal EnableDelayedExpansion
    REM 要查找的文件类型
    set ext=*.pb
    REM 重新命名文件
    for /r %%a in (!ext!) do (
        REM 文件名
        set fn=%%~na
        REM 后缀
        set en=%%~xa
        REM 把字符串的最后4个字符赋值给变量hou
        set hou=!fn:~0,-6!
        echo !hou!!en!
        rename "%%a" "!hou!!en!"
    )
    echo pb rename success
    echo.
    
    REM 项目中pb文件存放目录
    set destPath=E:\source\client\xxx\Assets\StreamingAssets\lua\pb
    
    rd /s /q %destPath%
    md %destPath%
    
    echo copy to %destPath%
    xcopy ..\pb %destPath% /s /e
    pause
    

    复制

    proto2java.bat

    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
    
    @echo off
    
    cd Proto
    REM 生成java
    
    rd /s /q ..\Java
    md ..\Java
    
    set dest_path="..\Java"
    for %%i in (*.*) do (
        echo %%i
        protoc --java_out=%dest_path% %%i
    )
    
    echo java success
    
    REM 拷贝文件
    REM echo.
    
    set destPath=java代码目标目录
    rd /s /q %destPath%
    
    md %destPath%
    echo copy to %destPath%
    xcopy ..\pb %destPath% /s /e
    pause
    

    复制

    proto2cs.bat

    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
    
    @echo off
    
    cd Proto
    REM 生成java
    
    rd /s /q ..\Java
    md ..\Java
    
    set dest_path="..\Java"
    for %%i in (*.*) do (
        echo %%i
        protoc --csharp_out=%dest_path% %%i
    )
    
    echo java success
    
    REM 拷贝文件
    REM echo.
    
    set destPath=java代码目标目录
    rd /s /q %destPath%
    
    md %destPath%
    echo copy to %destPath%
    xcopy ..\pb %destPath% /s /e
    pause
    展开全文
  • Protobuf3详细介绍

    千次阅读 2019-11-07 11:23:06
    proto3语法 定义一个 Message 定义多个 message 类型 定义变量类型 分配Tag 指定变量规则 注释 保留变量不被使用 默认值 定义枚举 Enumerations 如何引用其他 proto 文件 升级 proto 文件正确的姿势 Any 的使用 One...

    简介

    Protocol Buffers 是 google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。

    至于protobuf是什么、使用场景、有什么好处,本文不做说明,这里将会为大家介绍怎么用 protobuf 来定义我们的交互协议,包括 .proto 的语法以及如何根据proto文件生成相应的代码。本文基于proto3,读者也可以点击了解proto2

    proto3语法

    定义一个 Message

    首先我们来定义一个 Search 请求,在这个请求里面,我们需要给服务端发送三个信息:

    • query:查询条件
    • page_number:你想要哪一页数据
    • result_per_page:每一页有多少条数据

    于是我们可以这样定义:

    // 指定使用proto3,如果不指定的话,编译器会使用proto2去编译
    syntax = "proto3"; //[proto2|proto3]
    
    message SearchRequests {
        // 定义SearchRequests的成员变量,需要指定:变量类型、变量名、变量Tag
        string query = 1;
        int32 page_number = 2;
        int32 result_per_page = 3;
    }
    
    

    定义多个 message 类型

    一个 proto 文件可以定义多个 message ,比如我们可以在刚才那个 proto 文件中把服务端返回的消息结构也一起定义:

    message SearchRequest {
        string query = 1;
        int32 page_number = 2;
        int32 result_per_page = 3;
    }
    
    message SearchResponse {
        repeated string result = 1;
    }
    
    

    message 可以嵌套定义,比如 message 可以定义在另一个 message 内部

    message SearchResponse {
        message Result {
            string url = 1;
            string title = 2;
            repeated string snippets = 3;
        }
        repeated Result results = 1;
    }
    

    定义在 message 内部的 message 可以这样使用:

    message SomeOtherMessage {
        SearchResponse.Result result = 1;
    }
    

    定义变量类型

    在刚才的例子之中,我们使用了2个标准值类型: string 和 int32,除了这些标准类型之外,变量的类型还可以是复杂类型,比如自定义的枚举和自定义的 message

    这里我们把标准类型列举一下protobuf内置的标准类型以及跟各平台对应的关系:

    .proto说明C++JavaPythonGoRubyC#PHP
    doubledoubledoublefloatfloat64Floatdoublefloat
    floatfloatfloatfloatfloat32Floatfloatfloat
    int32使用变长编码,对负数编码效率低,如果你的变量可能是负数,可以使用sint32int32intintint32Fixnum or Bignum (as required)intinteger
    int64使用变长编码,对负数编码效率低,如果你的变量可能是负数,可以使用sint64int64longint/longint64Bignumlonginteger/string
    uint32使用变长编码uint32intint/longuint32Fixnum or Bignum (as required)uintinteger
    uint64使用变长编码uint64longint/longuint64Bignumulonginteger/string
    sint32使用变长编码,带符号的int类型,对负数编码比int32高效int32intintint32Fixnum or Bignum (as required)intinteger
    sint64使用变长编码,带符号的int类型,对负数编码比int64高效int64longint/longint64Bignumlonginteger/string
    fixed324字节编码, 如果变量经常大于228228 的话,会比uint64高效uint64longint/longuint64Bignumulonginteger/string
    sfixed324字节编码int32intintint32Fixnum or Bignum (as required)intinteger
    sfixed648字节编码int64longint/longint64Bignumlonginteger/string
    boolboolbooleanboolboolTrueClass/FalseClassboolboolean
    string必须包含utf-8编码或者7-bit ASCII textstringStringstr/unicodestringString (UTF-8)stringstring
    bytes任意的字节序列stringByteStringstr[]byteString (ASCII-8BIT)ByteStringstring

    补充说明:

    • In Java, unsigned 32-bit and 64-bit integers are represented using their signed counterparts, with the top bit simply being stored in the sign bit.
    • In all cases, setting values to a field will perform type checking to make sure it is valid.
    • 64-bit or unsigned 32-bit integers are always represented as long when decoded, but can be an int if an int is given when setting the field. In all cases, the value must fit in the type represented when set. See 2.
    • Python strings are represented as unicode on decode but can be str if an ASCII string is given (this is subject to change).
    • Integer is used on 64-bit machines and string is used on 32-bit machines.

    关于标准值类型,还可以参考Scalar Value Types

    如果你想了解这些数据是怎么序列化和反序列化的,可以点击 Protocol Buffer Encoding 了解更多关于protobuf编码内容。

    分配Tag

    每一个变量在message内都需要自定义一个唯一的数字Tag,protobuf会根据Tag从数据中查找变量对应的位置,具体原理跟protobuf的二进制数据格式有关。Tag一旦指定,以后更新协议的时候也不能修改,否则无法对旧版本兼容。

    Tag的取值范围最小是1,最大是229229-1,但 19000~19999 是 protobuf 预留的,用户不能使用。

    虽然 Tag 的定义范围比较大,但不同 Tag 也会对 protobuf 编码带来一些影响:

    • 1 ~ 15:单字节编码
    • 16 ~ 2047:双字节编码

    使用频率高的变量最好设置为1 ~ 15,这样可以减少编码后的数据大小,但由于Tag一旦指定不能修改,所以为了以后扩展,也记得为未来保留一些 1 ~ 15 的 Tag

    指定变量规则

    在 proto3 中,可以给变量指定以下两个规则:

    • singular:0或者1个,但不能多于1个
    • repeated:任意数量(包括0)

    当构建 message 的时候,build 数据的时候,会检测设置的数据跟规则是否匹配

    在proto2中,规则为:

    • required:必须有一个
    • optional:0或者1个
    • repeated:任意数量(包括0)

    注释

    //表示注释开头,如

    message SearchRequest {
        string query = 1;
        int32 page_number = 2; // Which page number do we want
        int32 result_per_page = 3; // Number of results to return per page
    }
    
    

    保留变量不被使用

    上面我们说到,一旦 Tag 指定后就不能变更,这就会带来一个问题,假如在版本1的协议中,我们有个变量:

    int32 number = 1

    在版本2中,我们决定废弃对它的使用,那我们应该如何修改协议呢?注释掉它?删除掉它?如果把它删除了,后来者很可能在定义新变量的时候,使新的变量 Tag = 1 ,这样会导致协议不兼容。那有没有办法规避这个问题呢?我们可以用 reserved 关键字,当一个变量不再使用的时候,我们可以把它的变量名或 Tag 用 reserved 标注,这样,当这个 Tag 或者变量名字被重新使用的时候,编译器会报错

    message Foo {
        // 注意,同一个 reserved 语句不能同时包含变量名和 Tag 
        reserved 2, 15, 9 to 11;
        reserved "foo", "bar";
    }
    

    默认值

    当解析 message 时,如果被编码的 message 里没有包含某些变量,那么根据类型不同,他们会有不同的默认值:

    • string:默认是空的字符串
    • byte:默认是空的bytes
    • bool:默认为false
    • numeric:默认为0
    • enums:定义在第一位的枚举值,也就是0
    • messages:根据生成的不同语言有不同的表现,参考generated code guide

    注意,收到数据后反序列化后,对于标准值类型的数据,比如bool,如果它的值是 false,那么我们无法判断这个值是对方设置的,还是对方压根就没给这个变量设置值。

    定义枚举 Enumerations

    在 protobuf 中,我们也可以定义枚举,并且使用该枚举类型,比如:

    message SearchRequest {
        string query = 1;
        int32 page_number = 2; // Which page number do we want
        int32 result_per_page = 3; // Number of results to return per page
        enum Corpus {
            UNIVERSAL = 0;
            WEB = 1;
            IMAGES = 2;
            LOCAL = 3;
            NEWS = 4;
            PRODUCTS = 5;
            VIDEO = 6;
        }
        Corpus corpus = 4;
    }
    
    

    枚举定义在一个消息内部或消息外部都是可以的,如果枚举是 定义在 message 内部,而其他 message 又想使用,那么可以通过 MessageType.EnumType 的方式引用。定义枚举的时候,我们要保证第一个枚举值必须是0,枚举值不能重复,除非使用 option allow_alias = true 选项来开启别名。如:

    enum EnumAllowingAlias {
        option allow_alias = true;
        UNKNOWN = 0;
        STARTED = 1;
        RUNNING = 1;
    }
    

    枚举值的范围是32-bit integer,但因为枚举值使用变长编码,所以不推荐使用负数作为枚举值,因为这会带来效率问题。

    如何引用其他 proto 文件

    在proto语法中,有两种引用其他 proto 文件的方法: importimport public,这两者有什么区别呢?下面举个例子说明:
    这里写图片描述

    • 在情景1中, my.proto 不能使用 second.proto 中定义的内容
    • 在情景2中, my.proto 可以使用 second.proto 中定义的内容
    • 情景1和情景2中,my.proto 都可以使用 first.proto
    • 情景1和情景2中,first.proto 都可以使用 second.proto
    // my.proto
    import "first.proto";
    
    // first.proto
    //import "second.proto";
    import public "second.proto";
    
    // second.proto
    ...
    

    升级 proto 文件正确的姿势

    升级更改 proto 需要遵循以下原则

    • 不要修改任何已存在的变量的 Tag
    • 如果你新增了变量,新生成的代码依然能解析旧的数据,但新增的变量将会变成默认值。相应的,新代码序列化的数据也能被旧的代码解析,但旧代码会自动忽略新增的变量。
    • 废弃不用的变量用 reserved 标注
    • int32、 uint32、 int64、 uint64 和 bool 是相互兼容的,这意味你可以更改这些变量的类型而不会影响兼容性
    • sint32 和 sint64 是兼容的,但跟其他类型不兼容
    • string 和 bytes 可以兼容,前提是他们都是UTF-8编码的数据
    • fixed32 和 sfixed32 是兼容的, fixed64 和 sfixed64是兼容的

    Any 的使用

    Any可以让你在 proto 文件中使用未定义的类型,具体里面保存什么数据,是在上层业务代码使用的时候决定的,使用 Any 必须导入 import google/protobuf/any.proto

    import "google/protobuf/any.proto";
    
    message ErrorStatus {
        string message = 1;
        repeated google.protobuf.Any details = 2;
    }
    

    Oneof 的使用

    Oneof 类似union,如果你的消息中有很多可选字段,而同一个时刻最多仅有其中的一个字段被设置的话,你可以使用oneof来强化这个特性并且节约存储空间,如

    message LoginReply {
        oneof test_oneof {
            string name = 3;
            string age = 4;
        }
        required string status = 1;
        required string token = 2;
    }
    

    这样,name 和 age 都是 LoginReply 的成员,但不能给他们同时设置值(设置一个oneof字段会自动清理其他的oneof字段)。

    Proto3 Any与Oneof(转载)

    Maps 的使用

    protobuf 支持定义 map 类型的成员,如:

    map<key_type, value_type> map_field = N;
    // 举例:map<string, Project> projects = 3;
    
    • key_type:必须是string或者int
    • value_type:任意类型

    使用 map 要注意:

    • Map 类型不能使 repeated
    • Map 是无序的
    • 以文本格式展示时,Map 以 key 来排序
    • 如果有相同的键会导致解析失败

    Packages 的使用

    为了防止不同消息之间的命名冲突,你可以对特定的.proto文件提指定 package 名字。在定义消息的成员的时候,可以指定包的名字:

    package foo.bar;
    message Open { ... }
    
    message Foo {
        ...
        // 带上包名
        foo.bar.Open open = 1;
        ...
    }
    

    Options

    Options 分为 file-level options(只能出现在最顶层,不能在消息、枚举、服务内部使用)、 message-level options(只能在消息内部使用)、field-level options(只能在变量定义时使用)

    • java_package (file option):指定生成类的包名,如果没有指定此选项,将由关键字package指定包名。此选项只在生成 java 代码时有效
    • java_multiple_files (file option):如果为 true, 定义在最外层的 message 、enum、service 将作为单独的类存在
    • java_outer_classname (file option):指定最外层class的类名,如果不指定,将会以文件名作为类名
    • optimize_for (file option):可选有 [SPEED|CODE_SIZE|LITE_RUNTIME] ,分别是效率优先、空间优先,第三个lite是兼顾效率和代码大小,但是运行时需要依赖 libprotobuf-lite
    • cc_enable_arenas (file option):启动arena allocation,c++代码使用
    • objc_class_prefix (file option):Objective-C使用
    • deprecated (field option):提示变量已废弃、不建议使用
    option java_package = "com.example.foo";
    option java_multiple_files = true;
    option java_outer_classname = "Ponycopter";
    option optimize_for = CODE_SIZE;
    int32 old_field = 6 [deprecated=true];
    

    定义 Services

    这个其实和gRPC相关,详细可参考:gRPC, 这里做一个简单的介绍
    要定义一个服务,你必须在你的 .proto 文件中指定 service

    service RouteGuide {
        ...
    }
    

    然后在我们的服务中定义 rpc 方法,指定它们的请求的和响应类型。gRPC 允许你定义4种类型的 service 方法

    简单RPC

    客户端使用 Stub 发送请求到服务器并等待响应返回,就像平常的函数调用一样,这是一个阻塞型的调用

    // Obtains the feature at a given position.
    rpc GetFeature(Point) returns (Feature) {}
    

    服务器端流式 RPC

    客户端发送请求到服务器,拿到一个流去读取返回的消息序列。客户端读取返回的流,直到里面没有任何消息。从例子中可以看出,通过在响应类型前插入 stream 关键字,可以指定一个服务器端的流方法

    // Obtains the Features available within the given Rectangle.  Results are
    // streamed rather than returned at once (e.g. in a response message with a
    // repeated field), as the rectangle may cover a large area and contain a
    // huge number of features.
    rpc ListFeatures(Rectangle) returns (stream Feature) {}
    
    

    客户端流式 RPC

    客户端写入一个消息序列并将其发送到服务器,同样也是使用流。一旦客户端完成写入消息,它等待服务器完成读取返回它的响应。通过在请求类型前指定 stream 关键字来指定一个客户端的流方法

    // Accepts a stream of Points on a route being traversed, returning a
    // RouteSummary when traversal is completed.
    rpc RecordRoute(stream Point) returns (RouteSummary) {}
    
    

    双向流式 RPC

    双方使用读写流去发送一个消息序列。两个流独立操作,因此客户端和服务器可以以任意喜欢的顺序读写:比如, 服务器可以在写入响应前等待接收所有的客户端消息,或者可以交替的读取和写入消息,或者其他读写的组合。每个流中的消息顺序被预留。你可以通过在请求和响应前加 stream 关键字去制定方法的类型

    // Accepts a stream of RouteNotes sent while a route is being traversed,
    // while receiving other RouteNotes (e.g. from other users).
    rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
    

    代码生成

    使用 protoc 工具可以把编写好的 proto 文件“编译”为Java, Python, C++, Go, Ruby, JavaNano, Objective-C,或C#代码, protoc 可以从点击这里进行下载。protoc 的使用方式如下:

    protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --javanano_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
    

    参数说明:

    • IMPORT_PATH:指定 proto 文件的路径,如果没有指定, protoc 会从当前目录搜索对应的 proto 文件,如果有多个路径,那么可以指定多次--proto_path
    • 指定各语言代码的输出路径
      • –cpp_out:生成c++代码
      • java_out :生成java代码
      • python_out :生成python代码
      • go_out :生成go代码
      • ruby_out :生成ruby代码
      • javanano_out :适合运行在有资源限制的平台(如Android)的java代码
      • objc_out :生成 Objective-C代码
      • csharp_out :生成C#代码
      • php_out :生成PHP代码
    展开全文
  • Protobuf3 使用、入门教程及Demo

    万次阅读 2019-05-30 20:46:03
    文章目录Protobuf简介用法:更复杂的demooneof Protobuf简介 ​ 简单设计协议, 通过自带工具转换成为对应的语言代码, 协议是二进制协议, 设计时只需要描述各个类的关系, 简单明了 用法: 设计协议是在...
  • 【Protobuf(二)】protobuf3 默认值

    千次阅读 2020-09-03 13:30:09
    * 关于protobuff3 默认值问题 * 首先,pb对象的get方法永远不会有null,如果没有设置,会生成对应的默认值 * * 如果属性值是一个message,那么pb会为这个属性生成一个hasXXX方法,可以用这个方法判断是否设置了...
  • protobuf3 Any使用方法

    千次阅读 2021-03-08 21:03:33
    前言 Any 定义的变量就是一个基础类,就像Java中的Object.class。 类似于声明变量,然后可以转成自己需要的任意类 BaseResult.proto 文件 syntax = "proto3"; ... google.protobuf.Any foo = 3; }
  • title: “Windows下protobuf3安装小记” subtitle: “在坑里打滚的经历。。。” date: “2016-07-11” author: “cj” tags: proto3 protobuf windows c++ 一开始搞的是2,可惜我要用的gRPC只能用proto3. proto2: 在...
  • protoc3.5&protobuf3;.5

    2018-10-18 15:14:38
    protoc&protobuf;
  • protobuf3 吧所有的默认值都忽略了,但是在有些业务场景上这些字段是需要的 否则会导致加密值不一样 vendor/google/protobuf/src/google/protobuf/internal/messsage.php private function existField($field) { ...
  • google protobuf3实践教程

    千次阅读 2018-08-26 09:42:11
    google 的 protobuf 一个及其快速,以及好用的序列化框架,支持多语言之间,我们今天就使用java来进行尝试. by the way,为什么要序列化,不序列化不行吗?这么麻烦,序列化是一种对象持久化的手段,主要是用在网络传输、...
  • C# 使用 protobuf3

    2020-05-24 21:44:32
    下载源码,到protobuf-3.7.0\csharp\src\Google.Protobuf目录下编辑Google.Protobuf.csproj文件,修改导出的dll版本号<TargetFrameworks>netstandard1.0;net45; 注意,git上项目的net版本会存在不一致,修改...
  • 文章目录概述windows安装使用Protobuf3编写第一个protobuf程序 概述 proto3 是 google protocol buffer 的新版本。 Protobuf3 应用相当广泛,而且性能优秀,是序列化和网络传输及存储的优秀选择。 官方测试数据: ...
  • tolua集成lua-protobuf库,解析protobuf3

    千次阅读 2020-04-23 10:04:22
    已经重新编译好tolua库,可运行TestLuaProtobuf场景进行测试 参考: tolua集成lua-protobuf库 ...tolua中使用protobuf3—集成lua-protobuf tolua_runtime 集成 lua-protobuf 第三方pb3解析库 ...
  • 协议的处理在lua,所有数据都是通过C#的socket转发到lua,lua最后解密, Unity环境: 2018.1.0b11(64bit) protobuf环境:protobuf3.3 ...注意这只是客户端,如果你需要服务端请下载:Netty Protobuf3 测试服务器
  • Ubuntu下安装 protobuf 3

    千次阅读 2019-05-15 18:13:09
    我们用系统自动安装的 protobuf 一般都是 2 版本的,现在一些工具则对版本有要求,下面就是升级 protobuf 的具体操作: git clone https://github.com/protocolbuffers/protobuf.git cd protobuf git submodule ...
  • C#使用protobuf3步骤记录

    千次阅读 2019-10-17 09:03:16
    1.使用GitHub上的protoc.exe将定义好的.proto文件转成C#文件,导入项目中。 2.导入相关的包:VS2015-TOOLS --> NuGet Package Manager --> Package Manager Console. ...3.添加引用。 ...
  • Protobuf3入门指南

    千次阅读 2018-04-22 11:56:16
    Protobuf是google开发的一种跨语言和平台的序列化数据...最新的protobuf3支持更多的语言使用,比如go 、 object-c等等。另外proto2与proto3并非完全兼容,官方仍旧提供proto2的支持。Google内部有超过40000多个数...
  • protobuf3使用,golang语言实现

    千次阅读 2018-11-02 16:27:12
    1.安装protobuf3版本 下载安装protobuf-3.1.0版本, 把 F:\Program Files\protoc-3.3.0-win32\bin\protoc.exe 文件路径添加到环境变量中。 查看是否安装成功,在命令行输入: proto --version 安装成功之后,就...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 87,215
精华内容 34,886
关键字:

protobuf3

友情链接: Ackley.rar