Protobuf是什么

Protobuf是一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于网络通信数据存储

为什么要使用Protobuf

protobuf特点.png

如何使用Protobuf

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto

-I 编译源文件的目录

--java_out 编译目录文件

通过这个命令会自动编译出java代码,目前protobuf支持以下语言

Language Source
C++ src
Java java
Python python
Objective-C objectivec
C# csharp
JavaNano javanano
JavaScript js
Ruby ruby
Go golang/protobuf
PHP php
Dart dart-lang/protobuf

由于命令行的方式编译代码非常繁琐,且效率极低。谷歌提供了开源的Protobuf Gradle插件

简单说一下配置方式

在project.gradle中配置

buildscript {repositories {mavenLocal()}dependencies {classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6-SNAPSHOT'}
}

在modle.gradle中配置

apply plugin: 'com.google.protobuf'dependencies {// You need to depend on the lite runtime library, not protobuf-javacompile 'com.google.protobuf:protobuf-lite:3.0.0'
}protobuf {protoc {// You still need protoc like in the non-Android caseartifact = 'com.google.protobuf:protoc:3.0.0'}plugins {javalite {// The codegen for lite comes as a separate artifactartifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'}}generateProtoTasks {all().each { task ->task.builtins {// In most cases you don't need the full Java output// if you use the lite output.remove java}task.plugins {javalite { }}}}
}

目前有Protobuf2和Protobuf3,本文以Protobuf2为例,简单介绍一下Protobuf2的语法,更多详细内容请参考官方文档(需要翻墙)

先在Java的同级目录下新建一个名为proto的文件夹专门用于存放proto文件,编写proto文件后编译模块会根据proto文件内容生成java文件。

image

来看一下名为Test.proto的文件内容

//指定protobuf语法版本
syntax = "proto2";//包名
option java_package = "com.lhc.protobuf";
//源文件类名
option java_outer_classname = "AddressBookProtos";// class Person
message Person {//required 必须设置(不能为null)required string name = 1;//int32 对应java中的intrequired int32 id = 2;//optional 可以为空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 重复的 (集合)repeated PhoneNumber phones = 4;
}message AddressBook {repeated Person people = 1;
}

Protobuf应用------网络传输

http传输

通常在应用层我们使用的都是Http协议,Http的本质是一次socket请求的连接与断开。传输数据时将protobuf对象转换为byte[]传输即可

自定义TCP通信协议

当我们自定义TCP通信协议的时候,将面临粘包与分包的问题

分包:

  • 要发送的数据大于TCP缓冲剩余空间
  • 待发送数据大于MSS(最大报文长度)

image

粘包:

  • 要发送的数据小于TCP缓冲区,将多次写入缓冲区的数据一起发送
  • 接收端的应用层没有及时读取缓冲区的数据

[站外图片上传中...(image-f9d2d3-1528012964593)]

自定义通信协议的两种方式

  • 定义数据包包头

image

  • 在数据包之间设置边界

image

大家可以参考 JT808协议 ------交通部808协议(车联网),也是采用类似的方式定义通信协议


手写简易Gradle Protobuf编译插件

准备proto编译器工件,proto文件目录,通过参数拼接出命令行编译proto文件,将执行结果注册到编译打包列表

定义两个DSL命名空间

class ProtobufExt {/*** proto文件目录*/def srcDirsProtobufExt() {srcDirs = []}def srcDir(String srcDir) {if (!srcDirs.contians(srcDir))srcDirs << srcDir}def srcDir(String... srcDirs) {srcDirs.each { srcDir(it) }}
}
class ProtoExt {def pathdef artifact
}

定义一个插件实现Plugin接口

class ProtobufPlugin implements Plugin<Project> {static final String PROTOBUF_EXTENSION_NAME = "protobuf"static final String PROTO_SUB_EXTENSION_NAME = "protoc"Project project@Overridevoid apply(Project project) {this.project = projectproject.apply plugin: 'com.google.osdetector'project.extensions.create(PROTOBUF_EXTENSION_NAME, ProtobufExt)//创建命名空间project.protobuf.extensions.create(PROTO_SUB_EXTENSION_NAME, ProtoExt)//在gradle分析之后执行project.afterEvaluate {if (!project.protobuf.protoc.path) {if (!project.protobuf.protoc.artifact) {throw new GradleException("请配置protoc编译器")}//创建依赖配置Configuration config = project.configurations.create("protobufConfig")def (group, name, version) = project.protobuf.protoc.artifact.split(":")def notation = [group: group, name: name, version: version, classifier: project.osdetector.classifier, ext: 'exe']//本地存在则返回工件,否则先下载Dependency dependency = project.dependencies.add(config.name, notation)//获得对应dependency的所有文件File file = config.fileCollection(dependency).singleFileprintln fileif (!file.canExecute() && !file.setExecutable(true)) {throw new GradleException("protoc编译器无法执行")}project.protobuf.protoc.path = file.path}Task task = project.tasks.create("compileProtobuf", CompileProtobufTask)task.inputs.files(project.protobuf.srcDirs)task.outputs.dir("${project.buildDir}/generated/source/proto")//将编译生成的java文件假如到工程源代码文件列表中linkProtoToJavaSource()}}/*** 判断是否为安卓工程* @return*/boolean isAndroidProject() {return project.plugins.hasPlugin(AppPlugin) || project.plugins.hasPlugin(LibraryPlugin)}def getAndroidVariants() {return project.plugins.hasPlugin(AppPlugin) ?project.android.applicationVariants + project.android.testVariants : project.android.libraryVariants + project.android.testVariants}def linkProtoToJavaSource() {if (isAndroidProject()) {androidVariants.each {BaseVariant variant ->//将任务加入构建过程,并将第二个参数的文件注册到编译列表当中variant.registerJavaGeneratingTask(project.tasks.compileProtobuf, project.tasks.compileProtobuf.outputs.files.files)}} else {project.sourceSets.each {SourceSet sourceSet ->def compileName = sourceSet.getCompileTaskName('java')JavaCompile javaCompile = project.tasks.getByName(compileName)javaCompile.dependsOn project.tasks.compileProtobufsourceSet.java.srcDirs(project.tasks.compileProtobuf.outputs.files.files)}}}
}

实现一个DefaultTask子类,主要是通过输入参数拼接出如下的编译所需的命令行

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto

class CompileProtobufTask extends DefaultTask {CompileProtobufTask() {group = 'Protobuf'outputs.upToDateWhen { false } //关闭增量构建,否则输入输出不变时执行增量构建}@TaskActiondef run() {def outDir = outputs.files.singleFileoutDir.deleteDir()outDir.mkdirs()def cmd = [project.protobuf.protoc.path]cmd << "--java_out=$outDir"def source = []def inDirs = inputs.files.filesinDirs.each {cmd << "-I=${it.path}"}getProtoFiles(inDirs, source)cmd.addAll(source)println "执行:$cmd"Process process = cmd.execute()def stdout = new StringBuffer()def stdErr = new StringBuffer()process.waitForProcessOutput(stdout, stdErr)//输出错误日志if (process.exitValue() == 0) {println "编译protobuf文件成功"} else {throw new GradleException("编译protobuf文件失败" + " $stdout" + " $stdErr")}}/*** 将目录下所有.proto文件添加到集合* @param dirs* @param source*/def getProtoFiles(dirs, source) {dirs.each {File file ->if (file.isDirectory()) {getProtoFiles(file.listFiles(), source)} else if (file.name.endsWith(".proto")) {source << file}}}
}

如果觉得有收获,麻烦给个赞吧

链接:https://www.jianshu.com/p/2265f56805fa

Protobuf学习相关推荐

  1. Protobuf学习笔记

    Protobuf学习笔记 Posted by iamxhuon 2012/05/22 Leave a comment (0)Go to comments Protocol buffers是什么? 首先 ...

  2. Protobuf 学习手册——语法篇

    一.Override Protobuf1 是一种语言中立.平台无关.可扩展的序列化数据的格式,可用于通信协议,数据存储等. ProtoBuf 在序列化数据方面,它是灵活的.高效的.相比于 XML 来说 ...

  3. Protobuf学习入门(一)

      笔者最近在学习使用tensorflow/serving,其中有不少涉及Protobuf相关的内容,因此接触学习了Prorobuf,记录于此,希望能对读者有所启发.   本文作为Protobuf入门 ...

  4. Protobuf 学习(二)编译proto文件并测试

    Google 官网上的一个典型例子 (1)定义 proto 文件 // addressbook.protopackage tutorial; // package声明符message Person { ...

  5. 学习 protobuf(一)—— ubuntu 下 protobuf 2.6.1 的安装

    下载地址:https://github.com/google/protobuf/releases/download/v2.6.1/protobuf-2.6.1.tar.gz(如果初次下载失败,不妨多试 ...

  6. 【职业规划和代码库设计】

      主业规划:(工具集 和 代码库) 技术论坛和开源社区 学习网站 设计(素描.PS.UI) 专业英语 绩效考评与自我评价  https://www.unjs.com/fanwenwang/ziwop ...

  7. 学习Protobuf,ZigZag是啥你真的知道么?

    结合上一篇文章<学习Protobuf,Varint是啥你真的知道么?>,我们了解到通过Varint 编码整数,如遇到负数或大整数,就不具备压缩优势了?由于引入了MSB,不但没有好的压缩效果 ...

  8. Unity C# 网络学习(十二)——Protobuf生成协议

    Unity C# 网络学习(十二)--Protobuf生成协议 一.安装 去Protobuf官网下载对应操作系统的protoc,用于将.proto文件生成对应语言的协议语言文件 由于我使用的是C#所以 ...

  9. alin的学习之路:序列化与protobuf

    alin的学习之路:序列化与protobuf 1. 序列化(串行化) 序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程,与之相对应的过程称之为反序列化(Unser ...

最新文章

  1. 替换Android自带apk【转】
  2. prototype.js常用函数及其用法
  3. RAM的一个实例,向下取整
  4. oracle 复制组删除,利用copy在ASM磁盘组之间迁移
  5. 蓝桥杯JAVA省赛2013-----B------5(有理数类)
  6. html中表单元素_HTML中的表单元素
  7. 中文词语概念上下位图谱项目
  8. 从零开始编写深度学习库(五)ConvolutionLayer CPU编写
  9. 建造者模式 设计模式 Java实现 创建型
  10. JDK源码(9)-Double、Float
  11. 嵌入式linux只读保护,如何使用squashfs只读文件系统制作Linux系统文件
  12. Python【第一篇】python安装、pip基本用法、变量、输入输出、流程控制、循环
  13. java修改cookie的值_Java管理Cookie增删改查操作。
  14. 极光IM即时通讯初探
  15. crxmouse在其他页面失效
  16. 心不隔离|愿春早来,花枝春满
  17. 硬盘的结构和介绍,硬盘MBR详细介绍(超详细彩图)
  18. 【AnySDK】接入必读及常见问题
  19. App Store 审核指南 2017-12-11
  20. item_sku - 获取sku详细信息

热门文章

  1. 【生活随笔】读书笔记之《檀香刑》
  2. codeforces 排位赛2
  3. java socket重连_Java客户端Socket在服务端重启后的异常情况处理
  4. Cadence IRUN仿真编译选项
  5. TCP/IP协议简介(三) 之 网络层
  6. Win10 备份与还原驱动
  7. Sentinel系列4--- 实战限流篇
  8. s19文件编辑_19个令人印象深刻的在线图像编辑器
  9. 金九银十!阿里内部首推《SpringCloudAlibaba学习笔记》全方位讲解,细致至极!
  10. java 友元函数_C++友元函数(超详细)