1 自定义实现客户端与服务器端通信的协议
1.1 通信协议的设计
自定义的通信协议需要哪些内容
1)魔数:第一个字段一般是魔数,一般固定的几个字节。一个PNG图片的编码中有固定数量固定内容的字节,用于表示这是一个PNG图片;Java的Class文件打头有一串魔数用于表示这是一个class文件;同样,我们的通信协议也是这么定义,服务器端或客户端收到数据包之后,会先读取魔数看看是不是我们定义的通信协议,只有是我们的通信协议时才能按照我们定义的规则正确读取数据。
2)版本号:用于一个字节表示,比如Http协议有1.0/1.1/2.0版本,用于标识当前的数据包使用的是哪个版本号
3)序列化算法:我们Netty通信过程中,使用Java对象进行数据传输,就一定会涉及到序列化与反序列化,用一个字节说明采用哪种序列化方式。
4)指令:你发给我这个数据包时要干什么。比如TCP协议中,SYN位置1就代表我是要跟你建立连接、FIN位置1就是代表我要断开连接。在聊天系统中,服务器收到客户端的数据包,指令位用于说明客户端发送这条数据包的目的,是要给某人发送消息呢还是要添加某人为好友呢还是怎么的…
5)数据长度:我发送给你的数据包的数据部分的长度是多少。解决TCP粘包拆包问题
6)数据部分:传输的数据
1.2 通信协议的实现
1.2.1 Java对象
1)定义通信过程中传输的Java对象的抽象类
package maolaoke.top.netty.protocol;
import lombok.Data;
@Data
public abstract class Packet {
//协议版本
private Byte version = 1;
//获取指令
public abstract Byte getCommand();
}
2)~~定义一个枚举类,列出所有的指令。~~定义一个Command接口来模拟枚举,因为枚举默认是Integer类型,而我们要传输的是一个字节,用Byte来表示更合适。
public interface Command {
Byte LOGIN_REQUEST = 1; //表示这是一个登录请求
}
登录请求的Java类定义:
import maolaoke.top.netty.protocol.Command;
public class LoginRequestPacket extends Packet{
private Integer userId;
private String username;
private String password;
@Override
public Byte getCommand() {
return Command.LOGIN_REQUEST;
}
}
1.2.2 序列化
1)定义一个接口来模拟枚举,枚举所有的序列化标识。
public interface SerializerAlgorithm {
byte JSON_SERIALIZER = 1; //JSON序列化
}
2)定义一个序列化接口
public interface Serializer {
//获取序列化方式
byte getSerializerAlgorithm();
//序列化方法
byte[] serialize(Object object);
//反序列化
<T> T deserialize(Class<T> clazz, byte[] bytes);
}
3)实现JSON方式的序列化。
导入fastJson依赖包。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
import com.alibaba.fastjson.JSON;
public class JsonSerializer implements Serializer {
@Override
public byte getSerializerAlgorithm(){
return SerializerAlgorithm.JSON_SERIALIZER;
}
@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);
}
}
1.2.3 编解码
PacketCodeC.java
实现编解码,将发送的Java对象编码成标准的自定义通信协议的格式,将接收到的数据包解码成ByteBuf。
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import maolaoke.top.netty.protocol.Command;
import maolaoke.top.netty.protocol.packet.LoginRequestPacket;
import maolaoke.top.netty.protocol.packet.Packet;
import maolaoke.top.netty.protocol.serializer.JsonSerializer;
import maolaoke.top.netty.protocol.serializer.Serializer;
import java.util.HashMap;
import java.util.Map;
public class PacketCodeC {
private static