Protocol Buffer 入门

最近公司想要做一套自己的IM Server,老大让我学习一下Protocol Buffer。Protocol Buffer是什么东西、做什么用的、有什么优势这里不做解释。


参考:
https://developers.google.com/protocol-buffers/docs/javatutorial
https://developers.google.com/protocol-buffers/docs/proto
http://blog.csdn.net/cchd0001/article/details/50669079


参考官网例子,想要完成此项工作主要分三步:
1.在.proto 中定义消息格式;
2.使用protocol buffer编译程序;
3.使用Java protocol buffer API读写消息;


一、定义消息协议格式(通讯录)

package tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";

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 phone = 4;
}

message AddressBook {
  repeated Person person = 1;
}



喏。上面就是消息格式,格式有点像C和java。把它保存在addressbook.proto文件中。
现在对.proto文件做一个简单说明:
1.“package tutorial”是文件的开始,它是为了防止不同项目之间的命名冲突。如果我们没有声明“java_package”属性,那它就会成为java程序的包名(感兴趣的同学自己试一下),但如果声明了“java_package”是不是就不用“package”了呢,答案是否定的,因为协议文件很可能在其它语言中使用。
2.“java_package”上面介绍过就是生成的.java文件中的包名。
3.“java_outer_classname”表示生成的.java文件名,如果没有声明它那么它会根据.proto文件名生成,例如“my_proto.proto”文件编译后生成的就是“MyProto.java”。
4.消息类型,简单的消息类型包括:bool, int32, float, double,string等等。上面的例子中你还可以看到一个内部类型“PhoneNumber”和一个枚举类型“PhoneType”,其中在“PhoneNumber”中引用了“PhoneType”类型。
5."=1"、"=2"是消息标签,消息中的每一个字段都被定义了一个独一无二的数字标签。这个标签是用来在二进制的消息格式中区分字段的,一旦你的消息开始被使用,这些标签就不应该在被修改了。注意 1 到 15 标签在编码的时候仅占用1 byte ,16 - 2047 占用 2 byte 。因此你应该将 1 - 15 标签保留给最经常被使用的消息元素。另外为未来可能添加的常用元素预留位子。 
你能定义的最小的标签是1, 最大是 2的29次方 -1 , 另外 19000 到 19999 也不能用。他们是protobuf 的编译预留标签。
6.
required 格式正确的消息必须有一个这个字段。
optional 格式正确的消息可以有一个或者零个这样的消息。
repeated 这个字段可以有任意多个。字段值的顺序被保留。


二、编译协议文件

1.下载编译器,去github下载,我下载的是window版 https://github.com/google/protobuf/releases/tag/v3.0.0
2.解压文件,bin文件夹里面有一个protoc.exe执行文件。
3.protoc-3.0.0-win32文件夹下新建两个文件夹java-作为文件生成路径;proto-作为协议路径
4.dos中执行:protoc -I=../proto --java_out=../java ../proto/addressbook.proto
5.查看java文件夹,文件已经生成

三、使用Java protocol buffer API读写消息
1.新建maven java工程,pom信息如下:

<span style="white-space:pre">		</span><dependency>
<span style="white-space:pre">			</span><groupId>com.google.protobuf</groupId>
<span style="white-space:pre">			</span><artifactId>protobuf-java</artifactId>
<span style="white-space:pre">			</span><version>3.0.0</version>
<span style="white-space:pre">		</span></dependency>

2.拷贝生成的java文件到工程中
3.新建一个测试类(写)

package com.example.tutorial;
import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintStream;

class AddPerson {
  // This function fills in a Person message based on user input.
  static Person PromptForAddress(BufferedReader stdin,
                                 PrintStream stdout) throws IOException {
    Person.Builder person = Person.newBuilder();

    stdout.print("Enter person ID: ");
    person.setId(Integer.valueOf(stdin.readLine()));

    stdout.print("Enter name: ");
    person.setName(stdin.readLine());

    stdout.print("Enter email address (blank for none): ");
    String email = stdin.readLine();
    if (email.length() > 0) {
      person.setEmail(email);
    }

    while (true) {
      stdout.print("Enter a phone number (or leave blank to finish): ");
      String number = stdin.readLine();
      if (number.length() == 0) {
        break;
      }

      Person.PhoneNumber.Builder phoneNumber =
        Person.PhoneNumber.newBuilder().setNumber(number);

      stdout.print("Is this a mobile, home, or work phone? ");
      String type = stdin.readLine();
      if (type.equals("mobile")) {
        phoneNumber.setType(Person.PhoneType.MOBILE);
      } else if (type.equals("home")) {
        phoneNumber.setType(Person.PhoneType.HOME);
      } else if (type.equals("work")) {
        phoneNumber.setType(Person.PhoneType.WORK);
      } else {
        stdout.println("Unknown phone type.  Using default.");
      }

      person.addPhone(phoneNumber);
    }

    return person.build();
  }

  // Main function:  Reads the entire address book from a file,
  //   adds one person based on user input, then writes it back out to the same
  //   file.
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("Usage:  AddPerson ADDRESS_BOOK_FILE");
      System.exit(-1);
    }

    AddressBook.Builder addressBook = AddressBook.newBuilder();

    // Read the existing address book.
    try {
      addressBook.mergeFrom(new FileInputStream(args[0]));
    } catch (FileNotFoundException e) {
      System.out.println(args[0] + ": File not found.  Creating a new file.");
    }

    // Add an address.
    addressBook.addPerson(
      PromptForAddress(new BufferedReader(new InputStreamReader(System.in)),
                       System.out));

    // Write the new address book back to disk.
    FileOutputStream output = new FileOutputStream(args[0]);
    addressBook.build().writeTo(output);
    output.close();
  }
}
测试类做的是新建一个文件来保存序列化的AddressBook信息,当然新建AddressBook需要输入保存的文件地址及person内容。
eclipse中 Run->Run Configurations->Arguments 填写C:\Users\Administrator\Desktop\AddressBook
运行...


输入一个person laowang、一个person laozhang,可见桌面上生成了一个AddressBook文件


C:\Users\Administrator\Desktop\AddressBook: File not found.  Creating a new file.
Enter person ID: 1
Enter name: laowang
Enter email address (blank for none): laowang@163.com
Enter a phone number (or leave blank to finish): 
...

4.新建一个测试类(读)

package com.example.tutorial;
import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;

class ListPeople {
  // Iterates though all people in the AddressBook and prints info about them.
  static void Print(AddressBook addressBook) {
    for (Person person: addressBook.getPersonList()) {
      System.out.println("Person ID: " + person.getId());
      System.out.println("  Name: " + person.getName());
      if (person.hasEmail()) {
        System.out.println("  E-mail address: " + person.getEmail());
      }

      for (Person.PhoneNumber phoneNumber : person.getPhoneList()) {
        switch (phoneNumber.getType()) {
          case MOBILE:
            System.out.print("  Mobile phone #: ");
            break;
          case HOME:
            System.out.print("  Home phone #: ");
            break;
          case WORK:
            System.out.print("  Work phone #: ");
            break;
        }
        System.out.println(phoneNumber.getNumber());
      }
    }
  }

  // Main function:  Reads the entire address book from a file and prints all
  //   the information inside.
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("Usage:  ListPeople ADDRESS_BOOK_FILE");
      System.exit(-1);
    }

    // Read the existing address book.
    AddressBook addressBook =
      AddressBook.parseFrom(new FileInputStream(args[0]));

    Print(addressBook);
  }
}
运行时输入参数(文件保存路径),可以看到打印的信息:


Person ID: 1
  Name: laowang
  E-mail address: laowang@163.com
  Mobile phone #: 15811111111
Person ID: 2
  Name: laozhang
  E-mail address: laozhang@163.com
  Mobile phone #: 15822222222


好啦,入门级的测试完成了,现在回头看看给我们生成的AddressBookProtos.java中都提供了哪些函数。

标准方法:
一.每个Message和Builder中包含以下方法
1)isInitialized(): 检查是否所有的必须属性已经被设置.
2)toString(): 你懂得.
3)mergeFrom(Message other): (只在builder中) 覆盖已有的单属性,合并重复属性.
4)clear(): (只在builder中) 清空所有的属性值到空状态.


二.序列化和反序列化(这个看方法名就知道了)
1)byte[] toByteArray();: serializes the message and returns a byte array containing its raw bytes.
2)static Person parseFrom(byte[] data);: parses a message from the given byte array.
3)void writeTo(OutputStream output);: serializes the message and writes it to an OutputStream.
4)static Person parseFrom(InputStream input);: reads and parses a message from an InputStream.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值