Netty初学五 客户端与服务端通信协议编解码
一、客户端与服务端的通信协议
1.客户端与服务端的通信是基于tcp通信的数据包格式均为二进制,协议指的是客户端和服务端事先商量好的,每一个二进制包中每一段字节分别代表什么含义。
简单的登录指令:
2.客户端与服务端的通信:
客户端把一个java对象按照通信协议转换为二进制数据包
通过网络把这段二进制数据包发送到服务端,在传送的过程中是由tcp/ip协议负责数据的传输
服务端接收到数据之后,按照协议取出二进制数据包中的相应字段包装成java对象
服务端处理完之后,如果需要生成响应会按照同样的流程进行
3.通信协议的设计:
第一个字段魔数,通常情况下是固定的几个字节,该字段的作用是服务端可以根据这个字段的信息进行比对,可以判断出这个数据包并非遵循自定义协议的,即无效的数据包
第三个字段的序列化算法表示如何将java对象转换成二进制数据以及如何将二进制数据转换成java对象
第五个字段表示的数据长度,占4字节
4.通信协议的实现:
编码:将java对象根据协议封装成二进制数据包,解码反之
java对象:
@Data
public abstract class Packet {
/**
* 协议版本
*/
private Byte version = 1;
//指令
public abstract Byte getCommand();
}
所有的指令数据包都必须实现这个方法
定义登录请求数据包:
public interface Command {
Byte LOGIN_REQUEST = 1;
}
@Data
public class LoginRequestPacket extends Packet {
private Integer userId;
private String username;
private String password;
@Override
public Byte getCommand() {
return LOGIN_REQUEST;
}
}
上述代码示例定义了3个字段,分别是用户id 用户名和密码并且覆盖了getCommand方法
以上过程为java对象的定义,可以根据一种规则把一个java对象转换成二进制数据,即java对象的序列化
序列化:
public interface Serializer {
/**
* 序列化算法
*/
byte getSerializerAlgorithm();
/**
* Java 对象转换成二进制数据
*/
byte[] serialize(Object object);
/**
* 二进制数据转换成Java对象
*/
<T> T deserialize(Class<T> clazz, byte[] bytes);
}
serialize()方法将java对象转换成字节数组,deserialize()方法将字节数组转换成java对象
json序列化方式:
public interface SerializerAlgorithm {
/**
* JSON序列化标识
*/
byte JSON = 1;
}
public class JSONSerializer implements Serializer {
@Override
public byte getSerializerAlgorithm() {
return SerializerAlgorithm.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);
}
}
5.封装成二进制数据的过程:
private static final int MAGIC_NUMBER = 0x12345678;
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.getSerializerAlgorithm());
byteBuf.writeByte(packet.getCommand());
byteBuf.writeInt(bytes.length);
byteBuf.writeBytes(bytes);
return byteBuf;
}
6.解析java对象的过程:
public Packet decode(ByteBuf byteBuf) {
// 跳过魔数
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;
}