protocol buffer
介紹
protocol buffer簡稱protobuf。
在網絡數據傳輸的時候,我們需要序列化和反序列化。
java自帶的序列化和反序列化只支持java語言。如果client用python寫,server用java寫,這樣就無法完成數據傳輸。
將數據序列化到xml文件中也行,但是很佔空間,並且xml的樹結構沒有java的字段簡單易讀。
所以,我們使用一個google的rpc庫:protobuf。
下載
首先我們要下載編譯器:
在github上找到google的protobuf項目,並且下載適合自己電腦的編譯器:
將編譯器放在系統變量中:
這就算是安裝完了。
下載對應的java庫:
// https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.12.2'
// https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util
compile group: 'com.google.protobuf', name: 'protobuf-java-util', version: '3.12.2'
編寫.proto文件
.proto
文件是你要存儲的數據的描述文件。
在src下建一個文件:protobuf/addressbook.proto(必須要小寫)
idea要安插件才能認識.proto
結尾的文件,你就按着它的提示安裝就行了。
address book.proto:
syntax = "proto2";
package protobuftest;
option java_package = "com.ocean.protobuftest";
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 phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
syntax
是一個版本,提供錯誤檢查。
package
指定生成的class文件在那個包中。java_package
是更加具體的指定包名。
java_outer_classname
指定生成的class文件的名字。也就是所謂的類名。
然後我們定義了兩個message:Person和AddressBook。
message就像內部類一樣。
在Person裏面,還有一個message
:PhoneNumber,以及一個枚舉類:PhoneType。
首先,看一下字段的基本數據類型:bool, int32, float, double, string
。
這和java極其像。
字段描述有required,optional,repeated
。
-
required表示必須賦值。
-
optional可以不賦值。如果不給,就用默認值。比如
optional PhoneType type = 2 [default = HOME];
PhoneType如果不賦值就默認爲HOME。
如果你也沒有默認值,就用系統默認值:
數值型的就是0,字符串就是空串,布爾型就是false。
- repeated就像一個數組。
repeated Person people = 1;
表示Person這個message可以有多個。
至於後面的=1
,=2
之類的,並非賦值,而是一個標記。在二進制編解碼過程中確保字段的唯一性。
0-15的標記比16及以上的標記在編碼的時候少一個字節,所以會用在較爲常用的字段上,或者是repeated字段上。
編譯.proto文件
命令:
-I是.proto
文件的目錄。–java_out是目標目錄。後面還跟着.proto
文件的地址。
最後的結果:
這個類是不能修改的。
向文件中寫入消息
我們將構建Person對象和AddressBook對象。
這裏的構建方法是建造者模式。比如:
import com.ocean.protobuftest.AddressBookProtos.Person;
public class TestBuild {
public static void main(String[] args) {
Person john =
Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("[email protected]")
.addPhones(
Person.PhoneNumber.newBuilder()
.setNumber("555-4321")
.setType(Person.PhoneType.HOME))
.build();
System.out.println(john.getId());
System.out.println(john.getName());
System.out.println(john.getEmail());
Person.PhoneNumber phone = john.getPhones(0);
System.out.println(phone.getNumber() + "\t" + phone.getType());
}
}
這就構建了一個Person實例。
我們在resources目錄下建addressbook.txt
:
我們要把構建的電話本對象寫到這個文件中:
package com.ocean.protobuftest;
import com.ocean.protobuftest.AddressBookProtos.AddressBook;
import com.ocean.protobuftest.AddressBookProtos.Person;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
public class AddPerson {
// This function fills in a Person message based on user input.
static AddressBookProtos.Person PromptForAddress(BufferedReader stdin,
PrintStream stdout) throws IOException {
Person.Builder person = Person.newBuilder();
stdout.println("Enter person ID: ");
person.setId(Integer.valueOf(stdin.readLine()));
stdout.println("Enter name: ");
person.setName(stdin.readLine());
stdout.println("Enter email address (blank for none): ");
String email = stdin.readLine();
if (email.length() > 0) {
person.setEmail(email);
}
while (true) {
stdout.println("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.println("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.addPhones(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 a person.
addressBook.addPeople(
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();
}
}
我們將獲取到控制檯的輸出流,設置Person對象的屬性。
在調用PromptForAddress
方法之前,程序會先去讀文件中原來已經存在的數據。
加入動態參數:
運行:
如此addressbook.txt
就有值了。
現在我們要將其讀出來。
從文件中讀取消息
package com.ocean.protobuftest;
import java.io.FileInputStream;
import com.ocean.protobuftest.AddressBookProtos.Person;
import com.ocean.protobuftest.AddressBookProtos.AddressBook;
public class ListPeople {
// Iterates though all people in the AddressBook and prints info about them.
static void Print(AddressBook addressBook) {
for (Person person: addressBook.getPeopleList()) {
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.getPhonesList()) {
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);
}
}
同樣的,我們要加入動態參數:
運行:
我加了兩個人,所以遍歷出兩個。