我们把java对象根据协议封装成二进制数据包的过程称为编码 , 而把从二进制数据包中解析出就java对象的过程称为解码 。 在学习如何使用Netty 进行通信协议编解码之前 , 我们先来定义一下客户端和服务端通信的java 对象 。
java 对象
/**
* 数据包对象
* @author outman
* */
@Data
abstract class Packet{
/**
* 协议版本
* */
private Byte version = 1;
/**
* 获取指令
* */
public abstract Byte getCommand();
/**
* 指令集合内部接口
* */
interface Command{
public static final Byte LOGIN_REQUEST = 1;
}
}
以上是通信过程中 java对象的抽象类 , 可以看到我们定义了一个版本号(默认值为1) 以及一个获取指令的抽象方法 , 所有的指令数据包都必须实现这个方法 , 这样我们就可以知道某种指令的含义
@Data 注解由lombok 提供 , 他会自动帮我们产生getter/setter 方法 , 减少大量的重复代码 , 需要添加依赖
org.projectlombok
lombok
1.16.18
provided
接下来 , 我们一客户登录请求为例 , 定义登录请求数据包 :
@Data
class LoginRequestPacket extends Packet{
private Integer uerId ;
private String userName;
private String password;
@Override
public Byte getCommand() {
return Command.LOGIN_REQUEST;
}
}
java对象定义完成之后 , 接下来我们就要定义一种规则 , 如何把一个java对象转换成二进制数据 , 这个规则叫做java对象的序列化
序列化
我们如下定义序列化接口
/**
* 序列化接口
* @author outman
* */
interface Serializer{
/**
* 序列化算法
* */
byte getSerializerAlgorithm();
/**
* java 对象转换成二进制 (序列化)
* */
byte[] serialize(Object obj);
/**
* 二进制转换为java对象 (反序列化)
* */
T deSerialize(Class clazz , byte[] bytes);
/**
* 序列化算法标识集合接口
* */
interface SerializerAlgorithm{
public static final byte JSON = 1;
}
}
fastjson 依赖
com.alibaba
fastjson
1.2.54
Json序列化实现类
/**
* JSON 序列化实现类
* @author outman
* */
class JSONSerializer implements Serializer{
@Override
public byte getSerializerAlgorithm() {
return SerializerAlgorithm.JSON;
}
@Override
public byte[] serialize(Object obj) {
return JSONObject.toJSONBytes(obj);
}
@Override
public T deSerialize(Class clazz, byte[] bytes) {
return JSONObject.parseObject(bytes, clazz);
}
}
我们设置 Srializer 的默认序列化方式为JSONSerializer
/**
* 序列化接口
* @author outman
* */
interface Serializer{
/**
* 默认的序列化对象
* */
Serializer DEFAULT = new JSONSerializer();
/**
* 序列化算法
* */
byte getSerializerAlgorithm();
/**
* java 对象转换成二进制 (序列化)
* */
byte[] serialize(Object obj);
/**
* 二进制转换为java对象 (反序列化)
* */
T deSerialize(Class clazz , byte[] bytes);
/**
* 序列化算法标识集合接口
* */
interface SerializerAlgorithm{
public static final byte JSON = 1;
}
}
这样我们就是实现了序列化相关的逻辑 , 如果想要实现其他的序列化算法的话 , 只需要实现一下Serializer接口 , 然后定义一下 序列化算法标识 就好啦。
编码: 封装成二进制数据的过程
/**
* 数据包编解码类
* @author outman
* */
class PacketCodec{
// 魔数
private static final int MAGIC_NUMBER = 0x12345678;
public ByteBuf enCode(Packet packet) {
// 1. 创建ByteBuf 对象
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
// 2. 序列化java对象
byte[] bs = Serializer.DEFAULT.serialize(packet);
// 3. 实际编码过程
byteBuf.writeInt(MAGIC_NUMBER); // 写入魔数
byteBuf.writeInt(packet.getVersion()); // 写入协议版本号
byteBuf.writeInt(Serializer.DEFAULT.getSerializerAlgorithm()); // 写入序列化算法
byteBuf.writeByte(packet.getCommand()); // 写入指令
byteBuf.writeInt(bs.length); // 写入数据长度
byteBuf.writeBytes(bs); // 写入数据
return byteBuf;
}
}
解码: 解析java对象的过程
还是刚刚的PacketCodec.java 中添加deCode(buf) 、 getRequestPacket(byte command) 、 getSerializer(byte serializeAlgorithm)方法
/**
* 数据包编解码类
*
* @author outman
*/
class PacketCodec {
// 魔数
private static final int MAGIC_NUMBER = 0x12345678;
// 指令 与 数据包 映射
private final Map> packetTypeMap;
// 序列化算法 与 序列化类 映射
private final Map> serializerMap;
// 单例
public static final PacketCodec INSTANCE = new PacketCodec();
// 注册Packet集合
List packetList = Arrays.asList(new Class[] { LoginRequestPacket.class });
// 注册序列化算法集合
List serializerList = Arrays.asList(new Class[] { JSONSerializer.class });
/**
* 初始化 指令 与 数据包 映射 序列化算法 与 序列化类 映射
*/
private PacketCodec() {
// 初始化 指令 与 数据包 映射
packetTypeMap = new HashMap>();
packetList.forEach(clazz -> {
try {
Method method = clazz.getMethod("getCommand");
Byte command = (Byte) method.invoke(clazz);
packetTypeMap.put(command, clazz);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
// 初始化序列化算法 与 序列化类 映射
serializerMap = new HashMap>();
serializerList.forEach(clazz -> {
try {
Method method = clazz.getMethod("getSerializerAlgorithm");
Byte serializerAlgorithm = (Byte) method.invoke(clazz);
serializerMap.put(serializerAlgorithm, clazz);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
// 编码
public ByteBuf enCode(Packet packet) {
// 1. 创建ByteBuf 对象
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
// 2. 序列化java对象
byte[] bs = Serializer.DEFAULT.serialize(packet);
// 3. 实际编码过程
byteBuf.writeInt(MAGIC_NUMBER); // 写入魔数
byteBuf.writeByte(packet.getVersion()); // 写入协议版本号
byteBuf.writeByte(Serializer.DEFAULT.getSerializerAlgorithm()); // 写入序列化算法
byteBuf.writeByte(packet.getCommand()); // 写入指令
byteBuf.writeInt(bs.length); // 写入数据长度
byteBuf.writeBytes(bs); // 写入数据
return byteBuf;
}
// 解码
public Packet deCode(ByteBuf byteBuf) throws Exception {
// 跳过 魔数 校验
byteBuf.skipBytes(4);
// 跳过版本号
byteBuf.skipBytes(1);
// 序列化算法标识
byte serializeAlgorithm = byteBuf.readByte();
// 指令标识
byte command = byteBuf.readByte();
// 数据包长度
int length = byteBuf.readInt();
// 数据
byte[] bs = new byte[length];
byteBuf.readBytes(bs);
// 通过序列化算法标识获取对应的 序列化对象
Serializer serializer = getSerializer(serializeAlgorithm);
// 通过指令标识 获取对应的 数据包类
Packet packet = getRequestPacket(command);
// 执行解码
if (serializer != null && packet != null) {
return serializer.deSerialize(packet.getClass(), bs);
} else {
System.out.println("没有找到对应的序列化对象或数据包对象");
return null;
}
}
// 通过指令获取对应的 数据包类
private Packet getRequestPacket(byte command) throws Exception {
return (Packet) packetTypeMap.get(command).newInstance();
}
// 通过序列化算法标识 获取对应的序列化类
private Serializer getSerializer(byte serializeAlgorithm) throws Exception {
return (Serializer) serializerMap.get(serializeAlgorithm).newInstance();
}
}
完整代码
package com.tj.NIO_test_maven;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import lombok.Data;
/**
* @author outman
*/
public class Test_09_客戶端与服务端通信协议编解码 {
public static void main(String[] args) {
}
}
/**
* 数据包对象
*
* @author outman
*/
@Data
abstract class Packet {
/**
* 协议版本
*/
private Byte version = 1;
/**
* 获取指令
*/
public abstract Byte getCommand();
/**
* 指令集合内部接口
*/
interface Command {
public static final Byte LOGIN_REQUEST = 1;
}
}
/**
* 登录请求数据包
*
* @author outman
*/
@Data
class LoginRequestPacket extends Packet {
private Integer uerId;
private String userName;
private String password;
@Override
public Byte getCommand() {
return Command.LOGIN_REQUEST;
}
}
/**
* 序列化接口
*
* @author outman
*/
interface Serializer {
/**
* 默认的序列化对对象
*/
Serializer DEFAULT = new JSONSerializer();
/**
* 序列化算法
*/
byte getSerializerAlgorithm();
/**
* java 对象转换成二进制 (序列化)
*/
byte[] serialize(Object obj);
/**
* 二进制转换为java对象 (反序列化)
*/
T deSerialize(Class clazz, byte[] bytes);
/**
* 序列化算法标识集合接口
*/
interface SerializerAlgorithm {
public static final byte JSON = 1;
}
}
/**
* JSON 序列化实现类
*
* @author outman
*/
class JSONSerializer implements Serializer {
@Override
public byte getSerializerAlgorithm() {
return SerializerAlgorithm.JSON;
}
@Override
public byte[] serialize(Object obj) {
return JSONObject.toJSONBytes(obj);
}
@Override
public T deSerialize(Class clazz, byte[] bytes) {
return JSONObject.parseObject(bytes, clazz);
}
}
/**
* 数据包编解码类
*
* @author outman
*/
class PacketCodec {
// 魔数
private static final int MAGIC_NUMBER = 0x12345678;
// 指令 与 数据包 映射
private final Map> packetTypeMap;
// 序列化算法 与 序列化类 映射
private final Map> serializerMap;
// 单例
public static final PacketCodec INSTANCE = new PacketCodec();
// 注册Packet集合
List packetList = Arrays.asList(new Class[] { LoginRequestPacket.class });
// 注册序列化算法集合
List serializerList = Arrays.asList(new Class[] { JSONSerializer.class });
/**
* 初始化 指令 与 数据包 映射 序列化算法 与 序列化类 映射
*/
private PacketCodec() {
// 初始化 指令 与 数据包 映射
packetTypeMap = new HashMap>();
packetList.forEach(clazz -> {
try {
Method method = clazz.getMethod("getCommand");
Byte command = (Byte) method.invoke(clazz);
packetTypeMap.put(command, clazz);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
// 初始化序列化算法 与 序列化类 映射
serializerMap = new HashMap>();
serializerList.forEach(clazz -> {
try {
Method method = clazz.getMethod("getSerializerAlgorithm");
Byte serializerAlgorithm = (Byte) method.invoke(clazz);
serializerMap.put(serializerAlgorithm, clazz);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
// 编码
public ByteBuf enCode(Packet packet) {
// 1. 创建ByteBuf 对象
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
// 2. 序列化java对象
byte[] bs = Serializer.DEFAULT.serialize(packet);
// 3. 实际编码过程
byteBuf.writeInt(MAGIC_NUMBER); // 写入魔数
byteBuf.writeByte(packet.getVersion()); // 写入协议版本号
byteBuf.writeByte(Serializer.DEFAULT.getSerializerAlgorithm()); // 写入序列化算法
byteBuf.writeByte(packet.getCommand()); // 写入指令
byteBuf.writeInt(bs.length); // 写入数据长度
byteBuf.writeBytes(bs); // 写入数据
return byteBuf;
}
// 解码
public Packet deCode(ByteBuf byteBuf) throws Exception {
// 跳过 魔数 校验
byteBuf.skipBytes(4);
// 跳过版本号
byteBuf.skipBytes(1);
// 序列化算法标识
byte serializeAlgorithm = byteBuf.readByte();
// 指令标识
byte command = byteBuf.readByte();
// 数据包长度
int length = byteBuf.readInt();
// 数据
byte[] bs = new byte[length];
byteBuf.readBytes(bs);
// 通过序列化算法标识获取对应的 序列化对象
Serializer serializer = getSerializer(serializeAlgorithm);
// 通过指令标识 获取对应的 数据包类
Packet packet = getRequestPacket(command);
// 执行解码
if (serializer != null && packet != null) {
return serializer.deSerialize(packet.getClass(), bs);
} else {
System.out.println("没有找到对应的序列化对象或数据包对象");
return null;
}
}
// 通过指令获取对应的 数据包类
private Packet getRequestPacket(byte command) throws Exception {
return (Packet) packetTypeMap.get(command).newInstance();
}
// 通过序列化算法标识 获取对应的序列化类
private Serializer getSerializer(byte serializeAlgorithm) throws Exception {
return (Serializer) serializerMap.get(serializeAlgorithm).newInstance();
}
}