Netty5学习笔记(3) ---Protobuf序列化

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

你可能感兴趣的:(Netty,netty,protobuf)