初涉ProtoBuf,从Server到Client

目录:
简介
简单使用
—— 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

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,282评论 6 342
  • 由于工程项目中拟采用一种简便高效的数据交换格式,百度了一下发现除了采用 xml、JSON 还有 ProtoBuf(...
    黄海佳阅读 49,185评论 1 23
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,665评论 19 139
  • 刘义宣兼有荆州、江州、兖州、豫州四个州的军事力量,其声势之浩大,威震远近四方。 孝武帝刘骏见他来势汹汹,心中生怯,...
    寒七琪阅读 467评论 2 2
  • 城市被浓的化不开的夜色覆盖,站在阳台往外看,寥寥几辆车划过,小饭店的招牌一闪一闪的迎合着夜晚的寒流,巨大的孤单袭来...
    HkingH阅读 436评论 0 0

友情链接更多精彩内容