1. 背景
经历了两家做长连接的公司,在第二家公司我终于理解了长连接开发的精髓,学会设计通信协议并基于通信协议编码解码
2. 含义
2.1 通信协议
无论是使用 Netty 还是原始的 Socket 编程,基于 TCP 通信的数据包格式均为二进制,协议指的就是客户端与服务端事先商量好的,每一个二进制数据包中每一段字节分别代表什么含义的规则。
2.2 编码解码
Java 对象根据协议封装成二进制数据包的过程成为编码,而把从二进制数据包中解析出 Java 对象的过程成为解码
2.3 我设计的协议
3. 代码demo
3.1 序列化相关的接口
/**
* 序列化接口
*/
public interface Serializer {
Serializer DEFAULT = new JSONSerializer();
/**
* 序列化算法
* @return
*/
byte getSerializerAlogrithm();
/**
* java 对象转换成二进制
*/
byte[] serialize(Object object);
/**
* 二进制转换成 java 对象
*/
<T> T deserialize(Class<T> clazz, byte[] bytes);
}
/**
* JSONSerializer 序列化方式
*/
public class JSONSerializer implements Serializer {
@Override
public byte getSerializerAlogrithm() {
return SerializerAlogrithm.JSON;
}
@Override
public byte[] serialize(Object object) {
return JSON.toJSONBytes(object);
}
@Override
public <T> T deserialize(Class<T> clazz, byte[] bytes) {
return JSON.parseObject(bytes, clazz);
}
}
public interface SerializerAlogrithm {
/**
* json 序列化
*/
byte JSON = 1;
}
3.2 客户端与服务端通信的 Java 对象接口
/**
* 客户端与服务端通信的 Java 对象接口
*/
@Data
public abstract class Packet {
/**
* 协议版本
*/
@JSONField(deserialize = false, serialize = false)
private Byte version = 1;
@JSONField(serialize = false)
public abstract Byte getCommand();
}
/**
* 登录请求的包
*/
@Data
public class LoginRequestPacket extends Packet {
private Integer userId;
private String username;
private String password;
@Override
public Byte getCommand() {
return Command.LOGIN_REQUEST;
}
}
/**
* 数据包的类型
*/
public interface Command {
Byte LOGIN_REQUEST = 1;
}
/**
* 数据包编解码成ByteBuf, 这样的没考虑粘包、拆包的过程
*/
public class PacketCodeC {
private static final int MAGIC_NUMBER = 0x12345678;
private static final Map<Byte, Class<? extends Packet>> packetTypeMap;
private static final Map<Byte, Serializer> serializerMap;
static {
packetTypeMap = new ConcurrentHashMap<>();
packetTypeMap.put(LOGIN_REQUEST, LoginRequestPacket.class);
serializerMap = new ConcurrentHashMap<>();
Serializer serializer = new JSONSerializer();
serializerMap.put(serializer.getSerializerAlogrithm(), serializer);
}
public ByteBuf encode(Packet packet) {
// 1. 创建 ByteBuf 对象
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
// 2. 序列化 java 对象
byte[] bytes = Serializer.DEFAULT.serialize(packet);
// 3. 实际编码过程
byteBuf.writeInt(MAGIC_NUMBER);
byteBuf.writeByte(packet.getVersion());
byteBuf.writeByte(Serializer.DEFAULT.getSerializerAlogrithm());
byteBuf.writeByte(packet.getCommand());
byteBuf.writeInt(bytes.length);
byteBuf.writeBytes(bytes);
return byteBuf;
}
public Packet decode(ByteBuf byteBuf) {
// 跳过 magic number
byteBuf.skipBytes(4);
// 跳过版本号
byteBuf.skipBytes(1);
// 序列化算法
byte serializeAlgorithm = byteBuf.readByte();
// 指令
byte command = byteBuf.readByte();
// 数据包长度
int length = byteBuf.readInt();
byte[] bytes = new byte[length];
byteBuf.readBytes(bytes);
Class<? extends Packet> requestType = getRequestType(command);
Serializer serializer = getSerializer(serializeAlgorithm);
if (requestType != null && serializer != null) {
return serializer.deserialize(requestType, bytes);
}
return null;
}
private Serializer getSerializer(byte serializeAlgorithm) {
return serializerMap.get(serializeAlgorithm);
}
private Class<? extends Packet> getRequestType(byte command) {
return packetTypeMap.get(command);
}
}