protobuf初探(上)

一、简介:

Protocol Buffers(简称Protobuf) ,是Google出品的序列化框架,与开发语言无关,和平台无关,具有良好的可扩展性。Protobuf和所有的序列化框架一样,都可以用于数据存储、通讯协议。国内很多大公司用到这个框架,如百度,腾迅,携程等等。

1.Portobuf的序列化的结果体积要比XML、JSON小很多,XML和JSON的描述信息太多了,导致消息要大;此外Portobuf还使用了Varint 编码,减少数据对空间的占用。

2.Portobuf序列化和反序列化速度比XML、JSON快很多,是直接把对象和字节数组做转换,而XML和JSON还需要构建成XML或者JSON对象结构。

二、基本使用:

2.1加载插件“protobuf support”,并重启idea。

2.2使用gRPC官方推荐的使用姿势进行配置。

2.2.1配置pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<properties>
<grpc.version>1.26.0</grpc.version>
<protobuf.version>3.1.0</protobuf.version>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

2.2.2编写proto文件

在protobuf中,结构化数据被称为 Message。

1
2
3
4
5
6
7
8
9
10
syntax = "proto3";
option java_package = "it.protobuf.test";
option java_outer_classname = "PersonProtobuf";

message Person {
int32 id = 1;
string name = 2;
string email = 3;
string phone = 4;
}

2.2.3编译生成Java类

2.2.4序列化调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static void main(String[] args) throws InvalidProtocolBufferException {

//序列化
//创建Person的builder
PersonProtobuf.Person.Builder personBuilder = PersonProtobuf.Person.newBuilder();
//对person进行传值
personBuilder.setEmail("lingwu@gmail.com");
personBuilder.setId(1001);
personBuilder.setName("lingwu");
personBuilder.setPhone("13612341234");
//创建
PersonProtobuf.Person lingwu = personBuilder.build();
// 序列化,byte[]可以被写到磁盘文件,或者通过网络发送出去。
byte[] data = lingwu.toByteArray();
System.out.println("[****]serialization[****]");
System.out.println(data);
System.out.print("byte----->:");
String s = Arrays.toString(data);
System.out.println(s);


// 反序列化,byte[]可以读文件或者读取网络数据构建。
System.out.println("[****]deserialization[****]");
PersonProtobuf.Person personData = PersonProtobuf.Person.parseFrom(data);
int id = personData.getId();
String name = personData.getName();
String email = personData.getEmail();
String phone = personData.getPhone();
System.out.println("id:"+id+",name:"+name+",email:"+email+",phone:"+phone);
}

三、数据流量

3.1 编写c/s通迅

3.1.1 server端接收数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class ProtobufServer implements Runnable {

public static void main(String[] args) {
Thread desktopServerThread = new Thread(new ProtobufServer());
desktopServerThread.start();
}

@Override
public void run() {
try {

ServerSocket serverSocket = new ServerSocket(55555);
System.out.println(" [ * ] --------> protobufServer is starting.....");

while (true) {
System.out.println("等待接收用户连接:");
// 接受客户端请求
Socket client = serverSocket.accept();
System.out.println("客户端:"+client.getInetAddress().getHostAddress()+"已加入服务器。");


DataOutputStream dataOutputStream;
DataInputStream dataInputStream;

try {
InputStream inputstream = client.getInputStream();
OutputStream outputStream = client.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);

byte[] len = new byte[1024];
int count = inputstream.read(len);
byte[] temp = new byte[count];
for (int i = 0; i < count; i++) {
temp[i] = len[i];
}

//解析数据
PersonProtobuf.Person person = PersonProtobuf.Person.parseFrom(temp);
System.out.println("[*]------------->id:" + person.getId());
System.out.println("[*]------------->name" + person.getName());
System.out.println("[*]------------->phone" + person.getPhone());
System.out.println("[*]------------->email" + person.getEmail());

sendProtoBufBack(dataOutputStream);
inputstream.close();

} catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
} finally {
client.close();
System.out.println("close");
}
}

} catch (IOException e) {
System.out.println(e.getMessage());
}
}

private byte[] getProtoBufBack() {

PersonProtobuf.Person person = PersonProtobuf.Person.newBuilder().setId(1).setName("lingwu")
.setPhone("13612341234").setEmail("1361234@gmil.com").build();
return person.toByteArray();
}

private void sendProtoBufBack(DataOutputStream dataOutputStream) {

byte[] backBytes = getProtoBufBack();

try {
dataOutputStream.write(backBytes, 0, backBytes.length);
dataOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}

3.1.2 client 发送数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class protobufClient {

public static void main(String[] args) {
protobufClient pc=new protobufClient();
System.out.println(" [ * ] -------> protobufClient is startting....");
pc.runget();
}

public void runget() {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 55555);

PersonProtobuf.Person.Builder builderPerson= PersonProtobuf.Person.newBuilder();
PersonProtobuf.Person person = builderPerson.setId(2).
setName("lingwu1").setPhone("13612341235").setEmail("13612345@gmil.com").build();


// 向服务器发送信息
System.out.println("[ * ] -------> send message .......");
person.writeTo(socket.getOutputStream());

// 接受服务器的信息
InputStream input = socket.getInputStream();

System.out.println("[ * ] -------> receive message:");
byte[] data = recvMsg(input);
String s = Arrays.toString(data);
System.out.println(s);
PersonProtobuf.Person personRec = PersonProtobuf.Person.parseFrom(data);
System.out.println("[*]------------->id:" + personRec.getId());
System.out.println("[*]------------->name" + personRec.getName());
System.out.println("[*]------------->phone" + personRec.getPhone());
System.out.println("[*]------------->email" + personRec.getEmail());


input.close();
socket.close();
} catch (Exception e) {
System.out.println(e.toString());
}
}


public byte[] recvMsg(InputStream inpustream) {
byte[] temp = null;
try {

byte len[] = new byte[1024];
int count = inpustream.read(len);

temp = new byte[count];
for (int i = 0; i < count; i++) {
temp[i] = len[i];
}
return temp;
} catch (Exception e) {
System.out.println(e.toString());
return temp;
}
}
}

3.1.3 运行程序并抓取流量

服务端运行结果:

客户端运行结果:

tcpdump抓包

1
sudo tcpdump -i lo0 port 55555 -w /tmp/test.cap

可得16进制数据为

1
080212076c696e677775311a11313336313233343540676d696c2e636f6d220b3133363132333431323335

这篇只是了解,具体的等之后的内容。