初涉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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容

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