美文网首页我爱编程
初涉ProtoBuf,从Server到Client

初涉ProtoBuf,从Server到Client

作者: s1991721 | 来源:发表于2018-06-09 12:21 被阅读0次

    目录:
    简介
    简单使用
    —— 1、编写.proto 文件
    —— 2、编译proto文件成目标文件(本文为java)
    —— 3、将目标文件导入项目
    —— 4、将依赖包导入项目
    ———— 自动依赖
    ———— 手动依赖
    Maven项目
    —— 创建Maven项目
    —— 安装ProtoBuf插件
    —— 编译proto文件
    —— Java
    —— Web
    Android
    —— 插件
    —— 依赖
    —— 编码
    —— 结果

    简介

    常见的序列化协议有xml、json以及本文将介绍的protobuf。

    ProtoBuf由Google出品,由其二进制格式,所以在体积和传输方面性能优越,但也因此可读性差。

    简单使用

    流程:
    1、编写.proto 文件
    2、编译proto文件成目标文件(本文为java)
    3、将目标文件导入项目
    4、将依赖包导入项目

    1、编写.proto 文件

    .proto 文件用来定义消息类型,可理解为java中的类

    syntax = "proto3";//使用proto3的语法,不写默认为proto2
    
    message SearchRequest {
      string query = 1;//数字为该字段在二进制文件中的标识(字段号),使用过一次后不应该被改变。字段号1-15占一个字节16-2047占两个字节,常用字段字段号最好用一个字节。19000-19999为保留字段号不可用
      int32 page_number = 2;
      int32 result_per_page = 3;
    }
    
    message SearchResponse {//一个.proto文件可以定义多个message
     ...
    }
    

    复杂点的

    syntax = "proto2";
    
    package tutorial;//如果没有java_package则使用此package,最好不要为空因为不同语言都会使用,java_package只java使用
    
    option java_package = "com.example.tutorial";//生成java文件的包
    option java_outer_classname = "AddressBookProtos";//生成java文件的名字
    
    message Person {
      required string name = 1;//必填字段
      required int32 id = 2;
      optional string email = 3;//可选字段
    
      enum PhoneType {//枚举
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }
    
      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }
    
      repeated PhoneNumber phones = 4;//java中的list
    }
    
    message AddressBook {
      repeated Person people = 1;
    }
    

    官方参考: https://developers.google.cn/protocol-buffers/docs/proto3#simple

    2、编译proto文件成目标文件(本文为java)

    编译前需下载protobuf的编译器,也可以下载源码。下载地址

    我的机器是Mac所以下载了第一项


    解压后


    安装protobuf
    进入到刚刚的解压目录
    $ ./configure
    $ make
    $ make check
    $ make install

    成功后


    编译proto文件三个参数
    protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
    $SRC_DIR:proto文件路径
    $DST_DIR:生成java文件的路径

    3、将目标文件导入项目

    复制粘贴的工作,将生成的java文件连同包结构一起移到项目。如果包名与项目的包名相同则合并,不相同则在同级。

    4、将依赖包导入项目

    自动依赖

    如果项目使用Maven或Gradle构建的,可以直接添加依赖。

    Maven
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java</artifactId>
                <version>3.5.1</version>
            </dependency>
    
    Gradle
        implementation 'com.google.protobuf:protobuf-java:3.5.1'
    

    若不是的话,需要找jar包放入项目。哪里来的jar包呢??自己打。打包的话需要用到Maven,顺便写下安装流程

    手动依赖

    安装Maven

    Maven下载地址

    解压后重命名放在Library下


    接着配置环境变量

    touch ~/.bash_profile 
    vi ~/.bash_profile  
    
    具体配置:
    # maven所在的目录  
    export M2_HOME=/Users/mr.lin/Library/Maven  
    # maven bin所在的目录  
    export M2=$M2_HOME/bin  
    # 将maven bin加到PATH变量中  
    export PATH=$M2:$PATH
    
    环境变量刷新
    source ~/.bash_profile  
    

    最后


    参考 https://blog.csdn.net/kepoon/article/details/55251913

    打包

    源码已经有了,Maven也配置好了


    copy一个protoc文件(上述mac安装protobuf时bin目录下的文件)到src目录下

    再copy一个protoc文件到java/core/src/目录下:


    然后回到java目录,执行:
    mvn package

    如果中途失败请注意,是否在此路径下生成过文件

    protobuf-3.5.1/java/core/src/main/java/com/google/protobuf
    

    我就失败过,将生成的文件删除重新执行即可。但失败原因不止这一个具体还要看错误信息。

    最终结果


    剩下的就是将jar包放入项目了

    参考 https://blog.csdn.net/u010277446/article/details/79203433

    Maven项目

    真正做项目的时候,我们总不可能每次都按照简单使用的步骤,先写.proto文件、然后编译成java或其他语言文件才能够使用。

    因此便有了自动化构建,我们只需写好.proto文件,剩下的工作交由编译器执行。

    创建Maven项目

    创建空的Maven项目


    填写项目信息


    空项目结构


    安装ProtoBuf插件

    编译proto文件

    在pom.xml中添加依赖

        <build>
            <pluginManagement>
                <plugins>
                    <plugin>
                        <!--protobuf 插件默认的 Phase 为 GenerateCode-->
                        <groupId>org.xolstice.maven.plugins</groupId>
                        <artifactId>protobuf-maven-plugin</artifactId>
                        <version>0.5.1</version>
                        <executions>
                            <execution>
                                <!--把 Compile mojo和 test compile mojo 绑定到 GenerateCode 阶段。
                                这样,在 GenerateCode 阶段,会执行此插件的两个 mojo。否则,在Maven 默认的 Compile 或 Test 阶段
                                ,不会执行编译动作。-->
                                <goals>
                                    <goal>compile</goal>
                                    <goal>test-compile</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.xolstice.maven.plugins</groupId>
                    <artifactId>protobuf-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
        <dependencies>
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java</artifactId>
                <version>3.5.1</version>
            </dependency>
        </dependencies>
    

    添加之前


    image.png

    添加之后


    写.proto文件


    参考 https://blog.csdn.net/wpengch/article/details/80192230

    Java

    在空项目的基础上

    写程序入口main


    配置启动


    测试环境是否搭好


    试一下使用情况

        public static void main(String[] args) {
    
            AddressBookProtos.Person person = AddressBookProtos.Person
                    .newBuilder()
                    .setName("Tom")
                    .setId(1)
                    .setEmail("Email")
                    .addPhones(0,
                            AddressBookProtos.Person.PhoneNumber.newBuilder()
                                    .setNumber("1234")
                                    .setType(AddressBookProtos.Person.PhoneType.HOME)
                                    .build())
                    .build();
    
            AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.newBuilder()
                    .addPeople(0, person)//这是一个list所以用add,set的话同list使用
                    .addPeople(1, person)
                    .build();
    
            System.out.println(addressBook.toByteString());
    
            try {
    
                AddressBookProtos.AddressBook addressBook1 = AddressBookProtos.AddressBook.parseFrom(addressBook.toByteArray());
    
                System.out.println(addressBook1.toString());
            } catch (InvalidProtocolBufferException e) {
                e.printStackTrace();
            }
    
        }
    

    内容已经成功打印了


    Web

    在空项目的基础上,创建web

    修改web路径


    创建Artifact


    配置启动


    给module添加依赖包,才可以使用servlet


    参考 https://www.cnblogs.com/wql025/p/5215570.html

    书写Controller


    配置servlet


    请求


    注意:NoClassDefFoundError 与 ClassNotFoundException
    NoClassDefFoundError连接时找不到
    ClassNotFoundException编译时找不到

    报错原因是找不到类

    jar包不在了


    完成之后运行!Chrome会报404但

    Android

    当使用kotlin编写protobuf相关当代码时

    查了写资料发现protobuf不支持kotlin语言编写,发生原因与编译的顺序有关,网上也有相关的解决办法。

    虽然kotlin可以与java混用,但此时此刻protobuf官方还没支持kotlin语言,所以还是乖乖用java,这里就不硬走kotlin的路线了。

    插件

    先来安装AndroidStudio的插件


    项目插件

    classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3'
    

    依赖

    apply plugin: 'com.android.application'
    
    apply plugin: 'com.google.protobuf'
    
    android {
        compileSdkVersion 26
        defaultConfig {
            applicationId "com.ljf.protobuf_android"
            minSdkVersion 19
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    
        sourceSets {
            main {
                java {
                    srcDir 'src/main/java'
                }
                proto {//用于识别proto文件的目录
                    srcDir 'src/main/proto'
                }
            }
        }
    }
    
    
    protobuf {//编译proto文件的任务
        protoc {
            artifact = 'com.google.protobuf:protoc:3.5.1'
        }
    
        generateProtoTasks {
            all().each { task ->
                task.builtins {
                    remove java
                }
                task.builtins {
                    java {}
                }
            }
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.google.protobuf:protobuf-java:3.5.1'//依赖包
    }
    
    

    编码

    这里做了两种处理,一个是本地数据的存取,一个是获取网络数据

    public class MainActivity extends Activity {
    
        private TextView resultTv;
        private Button readFileBt;
        private Button sendToServerBt;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            initView();
    
            AddressBookProtos.Person person = AddressBookProtos.Person
                    .newBuilder()
                    .setName("Tom")
                    .setId(1)
                    .setEmail("Email")
                    .addPhones(0,
                            AddressBookProtos.Person.PhoneNumber.newBuilder()
                                    .setNumber("1234")
                                    .setType(AddressBookProtos.Person.PhoneType.HOME)
                                    .build())
                    .build();
    
            AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.newBuilder()
                    .addPeople(0, person)
                    .addPeople(1, person)
                    .build();//用protobuf生成对象
            try {
                FileOutputStream fileOutputStream = openFileOutput("proto", MODE_PRIVATE);
                fileOutputStream.write(addressBook.toByteArray());//将protobuf对象的数据存到文件
                fileOutputStream.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private void initView() {
            resultTv = findViewById(R.id.resultTv);
            readFileBt = findViewById(R.id.readFileBt);
            sendToServerBt = findViewById(R.id.sendToServerBt);
    
            readFileBt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        FileInputStream fileInputStream = openFileInput("proto");
                        AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.parseFrom(fileInputStream);//由数据流生成protobuf对象
                        resultTv.setText("from local:\n" + addressBook.toString());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
    
            //获取网络数据
            sendToServerBt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                URL url = new URL("http://10.2.24.243:8080/test");
                                InputStream is = url.openStream();
    
                                final AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.parseFrom(is);
                                is.close();
    
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        resultTv.setText("from net:\n" + addressBook.toString());
                                    }
                                });
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();
                }
            });
        }
    
    }
    

    结果

    由于服务开在本地所以用模拟器才可以连本地服务,但注意IP地址并不是127.0.0.1。模拟器与本地是局域网关系


    GitHub

    相关文章

      网友评论

        本文标题:初涉ProtoBuf,从Server到Client

        本文链接:https://www.haomeiwen.com/subject/dkzrsftx.html