1.protocol buff是一种协议,是谷歌推出的一种序列化协议;Java序列化协议也是一种协议
2.两者的目的是,将对象序列化成字节数组,或者说是二进制数据
3.Java本身的序列化机制占用内存较多,而且效率低,相对于java本身的序列化机制,protobuf序列化机制更加优秀,可以节省很多内存空间、网络传输带宽和序列化时间。
4.代码示例:
4.1 Java序列代码示例:
/**
* 玩家对象
*/
public class Player implements Serializable{
private static final long serialVersionUID = -5248069984631225347L;
public Player(long playerId, int age, String name) {
this.playerId = playerId;
this.age = age;
this.name = name;
}
private long playerId;
private int age;
private String name;
private List<Integer> skills = new ArrayList<>();
public long getPlayerId() {
return playerId;
}
public void setPlayerId(long playerId) {
this.playerId = playerId;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Integer> getSkills() {
return skills;
}
public void setSkills(List<Integer> skills) {
this.skills = skills;
}
}
/**
* java序列化
*
*/
public class JAVA2Bytes {
public static void main(String[] args) throws Exception {
byte[] bytes = toBytes();
toPlayer(bytes);
}
/**
* 序列化
* @throws IOException
*/
public static byte[] toBytes() throws IOException{
Player player = new Player(101, 20, "peter");
player.getSkills().add(1001);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
//写入对象
objectOutputStream.writeObject(player);
//获取 字节数组
byte[] byteArray = byteArrayOutputStream.toByteArray();
System.out.println(Arrays.toString(byteArray));
return byteArray;
}
/**
* 反序列化
* @param bs
* @throws Exception
*/
public static void toPlayer(byte[] bs) throws Exception{
ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(bs));
Player player = (Player)inputStream.readObject();
//打印
System.out.println("playerId:" + player.getPlayerId());
System.out.println("age:" + player.getAge());
System.out.println("name:" + player.getName());
System.out.println("skills:" + (Arrays.toString(player.getSkills().toArray())));
}
}
以下是java序列化产生的字节数组示例:
[-84, -19, 0, 5, 115, 114, 0, 15, 99, 111, 109, 46, 106, 97, 118, 97, 46, 80, 108, 97, 121, 101, 114, -73, 43, 28, 39,
-119, -86, -125, -3, 2, 0, 4, 73, 0, 3, 97, 103, 101, 74, 0, 8, 112, 108, 97, 121, 101, 114, 73, 100, 76, 0, 4, 110, 97,
109, 101, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 76, 0, 6, 115,
107, 105, 108, 108, 115, 116, 0, 16, 76, 106, 97, 118, 97, 47, 117, 116, 105, 108, 47, 76, 105, 115, 116, 59, 120,
112, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 101, 116, 0, 5, 112, 101, 116, 101, 114, 115, 114, 0, 19, 106, 97, 118, 97, 46,
117, 116, 105, 108, 46, 65, 114, 114, 97, 121, 76, 105, 115, 116, 120, -127, -46, 29, -103, -57, 97, -99, 3, 0, 1,
73, 0, 4, 115, 105, 122, 101, 120, 112, 0, 0, 0, 1, 119, 4, 0, 0, 0, 10, 115, 114, 0, 17, 106, 97, 118, 97, 46, 108,
97, 110, 103, 46, 73, 110, 116, 101, 103, 101, 114, 18, -30, -96, -92, -9, -127, -121, 56, 2, 0, 1, 73, 0, 5, 118,
97, 108, 117, 101, 120, 114, 0, 16, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 78, 117, 109, 98, 101, 114,
-122, -84, -107, 29, 11, -108, -32, -117, 2, 0, 0, 120, 112, 0, 0, 3, -23, 120]
4.2 Protobuf序列代码示例:
首先创建 player.proto 文件,并按照格式书写proto内容:
option java_package = "com.proto";
option java_outer_classname = "PlayerModule";
message PBPlayer{
required int64 playerId = 1;
required int32 age = 2;
required string name = 3;
repeated int32 skills = 4;
}
message PBResource{
required int64 gold = 1;
required int32 energy = 2;
}
然后使用proto指令生成Proto类
protoc ./player.proto --java_out=./
会生成 PlayerModule 类,通过 PlayerModule 可以进行相关序列化操作
/**
* 根据生成的PlayerModule进行序列化和反序列化
*
*/
public class PB2Bytes {
public static void main(String[] args) throws InvalidProtocolBufferException {
byte[] bs = toBytes();
toPlayer(bs);
}
/**
* 序列化
*/
public static byte[] toBytes(){
//获取构造器
Builder builder = PlayerModule.PBPlayer.newBuilder();
//设置数据
builder.setPlayerId(101).setAge(22).setName("小三").addSkills(1001);
//构造对象
PBPlayer player = builder.build();
//序列化成字节数组
byte[] byteArray = player.toByteArray();
System.out.println(Arrays.toString(byteArray));
return byteArray;
}
/**
* 反序列化
* @throws InvalidProtocolBufferException
*/
public static void toPlayer(byte[] bs) throws InvalidProtocolBufferException {
PBPlayer player = PlayerModule.PBPlayer.parseFrom(bs);
System.out.println("playerId:" + player.getPlayerId());
System.out.println("playerAge:" + player.getAge());
System.out.println("playerName:" + player.getName());
System.out.println("skills:" + player.getSkills(0));
}
}
以下是protobuf序列化产生的字节数组示例:
[8, 101, 16, 20, 26, 5, 112, 101, 116, 101, 114, 32, -23, 7]
通过比较两种序列化的输出可以看出使用protobuf序列化机制,相比于java本身的序列化机制表现较为优异。
5.自定义序列化协议
6.自定义数据包协议
give me a coffee give me a tea
give me a coffeegive me a tea 粘包现象
give me a coffeegive me a tea 分包现象
粘包和分包出现的原因是:没有一个稳定数据结构
分割符
give me a coffee|give me a tea|
give me a coffee|
give me a tea|
长度 + 数据
16give me a coffee13give me a tea
16give me a coffee
13give me a tea
数据包格式:
/**
*
*
* 数据包格式
* +——----——+——-----——+——----——+——----——+——-----——+
* | 包头 | 模块号 | 命令号 | 长度 | 数据 |
* +——----——+——-----——+——----——+——----——+——-----——+
*
* 包头4字节
* 模块号2字节short
* 命令号2字节short
* 长度4字节(描述数据部分字节长度)
*
* @author -琴兽-
*
*/
Player 1
1 获取玩家数据
2 注册用户
3 购买金币
Fuben 2
1 查看副本
2 攻打副本
FrameDecoder 这个decoder可以协助我们解决粘包分包问题
ChannelBuffer
0 =< readerIndex =< writerIndex