##
Netty实战 IM即时通讯系统(九)实现客户端登录 /**
* 实现客户端登录
*
* @author outman
*/
public class Test_10_实现客户端登录 {
public static void main(String[] args) {
// 启动服务端
Test_10_server.start(8000);
// 启动客户端
Test_10_client.start("127.0.0.1", 8000, 5);
}
}
/**
* 客户端
*
* @author outman
*/
class Test_10_client {
/**
* 客户端启动
*
* @param ip
* 连接ip
* @param port
* 服务端端口
* @param maxRetry
* 最大重试次数
*/
public static void start(String ip, int port, int maxRetry) {
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 添加 客户端处理逻辑
}
});
// 连接服务端
connect(bootstrap, ip, port, maxRetry);
}
/**
* @desc 连接服务端
* @param bootstrap
* @param ip
* @param port
* @param maxRetry
* @param retryIndex
* 重试计数
*/
private static void connect(Bootstrap bootstrap, String ip, int port, int maxRetry, int... retryIndex) {
bootstrap.connect(ip, port).addListener(future -> {
int[] finalRetryIndex;
// 初始化 重连计数
if (retryIndex.length == 0) {
finalRetryIndex = new int[] { 0 };
} else {
finalRetryIndex = retryIndex;
}
// 判断连接状态
if (future.isSuccess()) {
System.out.println("客户端:" + new Date() + "连接【" + ip + ":" + port + "】成功");
} else if (maxRetry <= 0) {
System.out.println("客户端:" + new Date() + "连接【" + ip + ":" + port + "】失败,达到重连最大次数放弃重连");
} else {
// 重连使用退避算法
int delay = 1 << finalRetryIndex[0];
System.out.println("客户端:" + new Date() + "连接【" + ip + ":" + port + "】失败," + delay + "秒后执行重试");
bootstrap.config().group().schedule(() -> {
connect(bootstrap, ip, port, maxRetry - 1, finalRetryIndex[0] + 1);
}, delay, TimeUnit.SECONDS);
}
});
}
}
/**
* 服务端
*
* @author outman
*/
class Test_10_server {
/**
* @desc 服务端启动
* @param port
*/
public static void start(int port) {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 添加服务端处理逻辑
}
});
// 绑定端口
bind(serverBootstrap, port);
}
/**
* @desc 自动绑定递增并启动服务端
* @param serverBootstrap
* @param port
*/
private static void bind(ServerBootstrap serverBootstrap, int port) {
serverBootstrap.bind(port).addListener(future -> {
if (future.isSuccess()) {
System.out.println("服务端:" + new Date() + "绑定端口【" + port + "】成功");
} else {
System.out.println("服务端:" + new Date() + "绑定端口【" + port + "】失败,执行递增绑定");
bind(serverBootstrap, port + 1);
}
});
}
}
/**
* 客户端处理逻辑
*
* @author outman
*/
class Test_10_clientHandler extends ChannelInboundHandlerAdapter {
/**
* 连接成功时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
/**
* 有数据可读时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception {
}
}
/**
* 服务端处理逻辑
*
* @author outman
*/
class Test_10_serverHandler extends ChannelInboundHandlerAdapter {
/**
* 连接成功时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
/**
* 有数据可读时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx , Object obj) throws Exception {
}
}
/**
* 数据包抽象类
*
* @author outman
*/
@Data
abstract class Test_10_Packet {
// 协议版本号
private byte version = 1;
// 获取指定标识
public abstract byte getCommand();
// 指令集合
public interface Command {
// 登录指令
public static final byte LOGIN_REQUEST = 1;
// 登陆响应指令
public static final byte LOGIN_RESPONSE = 2;
}
}
/**
* 序列化抽象接口
*
* @author outman
*/
interface Test_10_Serializer {
// 获取序列化算法标识
byte getSerializerAlgorithm();
// 序列化算法标识集合
interface SerializerAlgorithm {
// JSON 序列化算法标识
public static final byte JSONSerializerAlgrothm = 1;
}
// 默认的序列化算法
public Test_10_Serializer DEFAULT = new Test_10_JSONSerializer();
// 序列化
byte[] enSerialize(ByteBuf byteBuf, Test_10_Packet packet);
// 反序列化
T deSerialize(byte[] bs, Class clazz);
}
/**
* 数据包编解码类
*
* @author outman
*/
class Test_10_PacketCodec {
// 魔数
private static final int MAGIC_NUMBER = 0x12345678;
// 单例
public static Test_10_PacketCodec INSTANCE = new Test_10_PacketCodec();
// 注册 序列化类
private Class[] serializerArray = new Class[] { Test_10_JSONSerializer.class };
// 注册抽象数据包类
private Class[] packetArray = new Class[] {
Test_10_LoginRequestPacket.class,
Test_10_LoginResponsePacket.class };
// 序列化算法标识 和对应的序列化类映射
private static Map> serializerMap;
// 指令标识和对应的数据包抽象类映射
private static Map> packetMap;
// 初始化 两个映射
private Test_10_PacketCodec() {
serializerMap = new HashMap<>();
Arrays.asList(serializerArray).forEach(clazz -> {
try {
Method method = clazz.getMethod("getSerializerAlgorithm");
byte serializerAlgorthm = (byte) method.invoke((Test_10_Serializer)clazz.newInstance());
serializerMap.put(serializerAlgorthm, clazz);
} catch (Exception e) {
e.printStackTrace();
}
});
packetMap = new HashMap<>();
Arrays.asList(packetArray).forEach(clazz -> {
try {
Method method = clazz.getMethod("getCommand");
method.setAccessible(true);
byte command = (byte) method.invoke((Test_10_Packet)clazz.newInstance());
packetMap.put(command, clazz);
} catch (Exception e) {
e.printStackTrace();
}
});
}
// 编码
public ByteBuf enCode(ByteBuf byteBuf, Test_10_Packet packet) {
// 序列化数据包
byte[] bs = Test_10_Serializer.DEFAULT.enSerialize(byteBuf, packet);
// 写入魔数
byteBuf.writeInt(MAGIC_NUMBER);
// 写入协议版本号
byteBuf.writeByte(packet.getVersion());
// 写入指令标识
byteBuf.writeByte(packet.getCommand());
// 写入序列化算法标识
byteBuf.writeByte(Test_10_Serializer.DEFAULT.getSerializerAlgorithm());
// 写入数据长度
byteBuf.writeInt(bs.length);
// 写入数据
byteBuf.writeBytes(bs);
return byteBuf;
}
// 解码
public Test_10_Packet deCode(ByteBuf byteBuf) throws Exception {
// 跳过魔数校验
byteBuf.skipBytes(4);
// 跳过版本号校验
byteBuf.skipBytes(1);
// 获取指令标识
byte command = byteBuf.readByte();
// 获取序列化算法标识
byte serializerAlgorthm = byteBuf.readByte();
// 获取数据长度
int len = byteBuf.readInt();
// 获取数据
byte[] bs = new byte[len];
byteBuf.readBytes(bs);
// 获取对应的序列化算法类
Test_10_Serializer serializer = getSerializer(serializerAlgorthm);
// 获取对应的数据包类
Test_10_Packet packet = getPacket(command);
if(serializer != null && packet != null) {
//反序列化数据包
return serializer.deSerialize(bs, packet.getClass());
}else {
throw new RuntimeException("没有找到对应的序列化实现或数据包实现");
}
}
private static Test_10_Packet getPacket(byte command) throws Exception {
return (Test_10_Packet) packetMap.get(command).newInstance();
}
private static Test_10_Serializer getSerializer(byte serializerAlgorthm) throws Exception {
return (Test_10_Serializer) serializerMap.get(serializerAlgorthm).newInstance();
}
}
/**
* 登录请求数据包实体类
*
* @author outman
*/
@Data
class Test_10_LoginRequestPacket extends Test_10_Packet {
private int userId ;
private String userName;
private String password;
@Override
public byte getCommand() {
return Command.LOGIN_REQUEST;
}
}
/**
* 登录响应数据包实体类
*
* @author outman
*/
@Data
class Test_10_LoginResponsePacket extends Test_10_Packet {
private int code;
private String msg;
@Override
public byte getCommand() {
return Command.LOGIN_RESPONSE;
}
/**
* 响应码集合
* */
interface Code{
// 成功的响应码
public static final int SUCCESS= 10000;
// 失败的响应码
public static final int FAIL = 10001;
}
}
/**
* Json序列化实现类
*
* @author outman
*/
class Test_10_JSONSerializer implements Test_10_Serializer {
@Override
public byte getSerializerAlgorithm() {
return SerializerAlgorithm.JSONSerializerAlgrothm;
}
@Override
public byte[] enSerialize(ByteBuf byteBuf, Test_10_Packet packet) {
return JSONObject.toJSONBytes(packet);
}
@Override
public T deSerialize(byte[] bs, Class clazz) {
return JSONObject.parseObject(bs, clazz);
}
}
接下来我们分别实现一下上述四个过程 , 开始之前 , 我们回顾一下客户端与服务端的启动流程 , 客户端启动的时候 , 我们会在引导类BootStrap里配置客户端处理逻辑 , 本小节中我们的客户端业务处理逻辑叫做Test_10_clientHandler
/**
* 客户端处理逻辑
*
* @author outman
*/
class Test_10_clientHandler extends ChannelInboundHandlerAdapter {
/**
* 连接成功时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
/**
* 有数据可读时触发
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
}
我们在客户端启动的时候 , 给客户端引导类配置这个逻辑处理器 , 这样Netty中事件相关的回调就会回调我们的Test_10_clientHandler
bootstrap.group(workerGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 添加 客户端处理逻辑
ch.pipeline().addLast(new Test_10_clientHandler());
}
});
同样 我们给服务端引导类页配置一个逻辑处理器
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 添加服务端处理逻辑
ch.pipeline().addLast(new Test_10_serverHandler());
}
});
接下来我们主要围绕这两个Handler来编写客户端登录相关的处理逻辑
客户端处理登录请求
我们实现在客户端连接上服务端之后 , 立即登录。 在客户端和服务端连接成功时 , Netty 会回调Test_10_clientHandler 的channelActive(ChannelHandlerContext ctx) 方法 , 我们在这里写 请求登录的逻辑(我们事先在 Test_10_LoginRequestPacket 中添加了三个属性 , Test_10_LoginRequestPacket 类上的@Data 注解是lombok 提供的 ,让我们不用写setter/getter)
/**
* 连接成功时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端:"+new Date()+"开始登陆");
// 创建登陆对象
Test_10_LoginRequestPacket loginRequestPacket = new Test_10_LoginRequestPacket();
// 随机取ID 1~999
loginRequestPacket.setUserId((int)(Math.random()*1000)+1);
loginRequestPacket.setUserName("outman");
loginRequestPacket.setPassword("123456");
// 编码
ByteBuf byteBuf = Test_10_PacketCodec.INSTANCE.enCode(ctx.alloc().buffer(), loginRequestPacket);
// 写出数据
ctx.channel().writeAndFlush(byteBuf);
}
写数据的时候 , 我们通过ctx.channel() 获取到当前连接(Netty对连接 的抽象为channel , 后面小节会分析) , 然后调用了writeAndFlush() 方法 就能把二进制数据写到服务端
服务端处理登录请求
/**
* 服务端处理逻辑
*
* @author outman
*/
class Test_10_serverHandler extends ChannelInboundHandlerAdapter {
/**
* 连接成功时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
/**
* 有数据可读时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx , Object obj) throws Exception {
ByteBuf byteBuf = (ByteBuf) obj;
// 解码
Test_10_Packet packet = Test_10_PacketCodec.INSTANCE.deCode(byteBuf);
// 根据指令执行对应的处理逻辑
switch (packet.getCommand() ) {
case Test_10_Packet.Command.LOGIN_REQUEST:
Test_10_LoginRequestPacket loginRequestPacket = (Test_10_LoginRequestPacket) packet;
// 校验成功
System.out.println("服务端:"+new Date()+"【"+loginRequestPacket.getUserName()+"】 登陆成功");
break;
default:
System.out.println("服务端:"+new Date()+"收到未知的指令【"+packet.getCommand()+"】");
break;
}
}
}
服务端发送登录响应
/**
* 服务端处理逻辑
*
* @author outman
*/
class Test_10_serverHandler extends ChannelInboundHandlerAdapter {
/**
* 连接成功时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
/**
* 有数据可读时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx , Object obj) throws Exception {
ByteBuf byteBuf = (ByteBuf) obj;
// 解码
Test_10_Packet packet = Test_10_PacketCodec.INSTANCE.deCode(byteBuf);
// 根据指令执行对应的处理逻辑
switch (packet.getCommand() ) {
case Test_10_Packet.Command.LOGIN_REQUEST:
Test_10_LoginRequestPacket loginRequestPacket = (Test_10_LoginRequestPacket) packet;
// 模拟校验成功
System.out.println("服务端:"+new Date()+"【"+loginRequestPacket.getUserName()+"】 登陆成功");
// 给服务端响应
Test_10_LoginResponsePacket loginResponsePacket = new Test_10_LoginResponsePacket();
loginResponsePacket.setCode(Code.SUCCESS);
loginResponsePacket.setMsg("登陆成功!");
// 编码
byteBuf = Test_10_PacketCodec.INSTANCE.enCode(byteBuf, loginResponsePacket);
//写出数据
ctx.channel().writeAndFlush(byteBuf);
break;
default:
System.out.println("服务端:"+new Date()+"收到未知的指令【"+packet.getCommand()+"】");
break;
}
}
}
客户端处理登录响应
/**
* 有数据可读时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
// 数据包解码
Test_10_Packet packet= Test_10_PacketCodec.INSTANCE.deCode(byteBuf);
//根据不同的指令选择对应的处理逻辑
switch (packet.getCommand()) {
case Test_10_Packet.Command.LOGIN_RESPONSE:
Test_10_LoginResponsePacket loginResponsePacket = (Test_10_LoginResponsePacket) packet;
System.out.println("客户端:"+new Date() +"收到服务端响应【"+loginResponsePacket.getMsg()+"】");
break;
default:
break;
}
}
执行结果
完整代码
/**
* 实现客户端登录
*
* @author outman
*/
public class Test_10_实现客户端登录 {
public static void main(String[] args) {
// 启动服务端
Test_10_server.start(8000);
// 启动客户端
Test_10_client.start("127.0.0.1", 8000, 5);
}
}
/**
* 客户端
*
* @author outman
*/
class Test_10_client {
/**
* 客户端启动
*
* @param ip
* 连接ip
* @param port
* 服务端端口
* @param maxRetry
* 最大重试次数
*/
public static void start(String ip, int port, int maxRetry) {
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 添加 客户端处理逻辑
ch.pipeline().addLast(new Test_10_clientHandler());
}
});
// 连接服务端
connect(bootstrap, ip, port, maxRetry);
}
/**
* @desc 连接服务端
* @param bootstrap
* @param ip
* @param port
* @param maxRetry
* @param retryIndex
* 重试计数
*/
private static void connect(Bootstrap bootstrap, String ip, int port, int maxRetry, int... retryIndex) {
bootstrap.connect(ip, port).addListener(future -> {
int[] finalRetryIndex;
// 初始化 重连计数
if (retryIndex.length == 0) {
finalRetryIndex = new int[] { 0 };
} else {
finalRetryIndex = retryIndex;
}
// 判断连接状态
if (future.isSuccess()) {
System.out.println("客户端:" + new Date() + "连接【" + ip + ":" + port + "】成功");
} else if (maxRetry <= 0) {
System.out.println("客户端:" + new Date() + "连接【" + ip + ":" + port + "】失败,达到重连最大次数放弃重连");
} else {
// 重连使用退避算法
int delay = 1 << finalRetryIndex[0];
System.out.println("客户端:" + new Date() + "连接【" + ip + ":" + port + "】失败," + delay + "秒后执行重试");
bootstrap.config().group().schedule(() -> {
connect(bootstrap, ip, port, maxRetry - 1, finalRetryIndex[0] + 1);
}, delay, TimeUnit.SECONDS);
}
});
}
}
/**
* 服务端
*
* @author outman
*/
class Test_10_server {
/**
* @desc 服务端启动
* @param port
*/
public static void start(int port) {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 添加服务端处理逻辑
ch.pipeline().addLast(new Test_10_serverHandler());
}
});
// 绑定端口
bind(serverBootstrap, port);
}
/**
* @desc 自动绑定递增并启动服务端
* @param serverBootstrap
* @param port
*/
private static void bind(ServerBootstrap serverBootstrap, int port) {
serverBootstrap.bind(port).addListener(future -> {
if (future.isSuccess()) {
System.out.println("服务端:" + new Date() + "绑定端口【" + port + "】成功");
} else {
System.out.println("服务端:" + new Date() + "绑定端口【" + port + "】失败,执行递增绑定");
bind(serverBootstrap, port + 1);
}
});
}
}
/**
* 客户端处理逻辑
*
* @author outman
*/
class Test_10_clientHandler extends ChannelInboundHandlerAdapter {
/**
* 连接成功时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端:"+new Date()+"开始登陆");
// 创建登陆对象
Test_10_LoginRequestPacket loginRequestPacket = new Test_10_LoginRequestPacket();
// 随机取ID 1~999
loginRequestPacket.setUserId((int)(Math.random()*1000)+1);
loginRequestPacket.setUserName("outman");
loginRequestPacket.setPassword("123456");
// 编码
ByteBuf byteBuf = Test_10_PacketCodec.INSTANCE.enCode(ctx.alloc().buffer(), loginRequestPacket);
// 写出数据
ctx.channel().writeAndFlush(byteBuf);
}
/**
* 有数据可读时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
// 数据包解码
Test_10_Packet packet= Test_10_PacketCodec.INSTANCE.deCode(byteBuf);
//根据不同的指令选择对应的处理逻辑
switch (packet.getCommand()) {
case Test_10_Packet.Command.LOGIN_RESPONSE:
Test_10_LoginResponsePacket loginResponsePacket = (Test_10_LoginResponsePacket) packet;
System.out.println("客户端:"+new Date() +"收到服务端响应【"+loginResponsePacket.getMsg()+"】");
break;
default:
break;
}
}
}
/**
* 服务端处理逻辑
*
* @author outman
*/
class Test_10_serverHandler extends ChannelInboundHandlerAdapter {
/**
* 连接成功时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
/**
* 有数据可读时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx , Object obj) throws Exception {
ByteBuf byteBuf = (ByteBuf) obj;
// 解码
Test_10_Packet packet = Test_10_PacketCodec.INSTANCE.deCode(byteBuf);
// 根据指令执行对应的处理逻辑
switch (packet.getCommand() ) {
case Test_10_Packet.Command.LOGIN_REQUEST:
Test_10_LoginRequestPacket loginRequestPacket = (Test_10_LoginRequestPacket) packet;
// 模拟校验成功
System.out.println("服务端:"+new Date()+"【"+loginRequestPacket.getUserName()+"】 登陆成功");
// 给服务端响应
Test_10_LoginResponsePacket loginResponsePacket = new Test_10_LoginResponsePacket();
loginResponsePacket.setCode(Code.SUCCESS);
loginResponsePacket.setMsg("登陆成功!");
// 编码
byteBuf = Test_10_PacketCodec.INSTANCE.enCode(byteBuf, loginResponsePacket);
//写出数据
ctx.channel().writeAndFlush(byteBuf);
break;
default:
System.out.println("服务端:"+new Date()+"收到未知的指令【"+packet.getCommand()+"】");
break;
}
}
}
/**
* 数据包抽象类
*
* @author outman
*/
@Data
abstract class Test_10_Packet {
// 协议版本号
private byte version = 1;
// 获取指定标识
public abstract byte getCommand();
// 指令集合
public interface Command {
// 登录指令
public static final byte LOGIN_REQUEST = 1;
// 登陆响应指令
public static final byte LOGIN_RESPONSE = 2;
}
}
/**
* 序列化抽象接口
*
* @author outman
*/
interface Test_10_Serializer {
// 获取序列化算法标识
byte getSerializerAlgorithm();
// 序列化算法标识集合
interface SerializerAlgorithm {
// JSON 序列化算法标识
public static final byte JSONSerializerAlgrothm = 1;
}
// 默认的序列化算法
public Test_10_Serializer DEFAULT = new Test_10_JSONSerializer();
// 序列化
byte[] enSerialize(ByteBuf byteBuf, Test_10_Packet packet);
// 反序列化
T deSerialize(byte[] bs, Class clazz);
}
/**
* 数据包编解码类
*
* @author outman
*/
class Test_10_PacketCodec {
// 魔数
private static final int MAGIC_NUMBER = 0x12345678;
// 单例
public static Test_10_PacketCodec INSTANCE = new Test_10_PacketCodec();
// 注册 序列化类
private Class[] serializerArray = new Class[] { Test_10_JSONSerializer.class };
// 注册抽象数据包类
private Class[] packetArray = new Class[] {
Test_10_LoginRequestPacket.class,
Test_10_LoginResponsePacket.class };
// 序列化算法标识 和对应的序列化类映射
private static Map> serializerMap;
// 指令标识和对应的数据包抽象类映射
private static Map> packetMap;
// 初始化 两个映射
private Test_10_PacketCodec() {
serializerMap = new HashMap<>();
Arrays.asList(serializerArray).forEach(clazz -> {
try {
Method method = clazz.getMethod("getSerializerAlgorithm");
byte serializerAlgorthm = (byte) method.invoke((Test_10_Serializer)clazz.newInstance());
serializerMap.put(serializerAlgorthm, clazz);
} catch (Exception e) {
e.printStackTrace();
}
});
packetMap = new HashMap<>();
Arrays.asList(packetArray).forEach(clazz -> {
try {
Method method = clazz.getMethod("getCommand");
method.setAccessible(true);
byte command = (byte) method.invoke((Test_10_Packet)clazz.newInstance());
packetMap.put(command, clazz);
} catch (Exception e) {
e.printStackTrace();
}
});
}
// 编码
public ByteBuf enCode(ByteBuf byteBuf, Test_10_Packet packet) {
// 序列化数据包
byte[] bs = Test_10_Serializer.DEFAULT.enSerialize(byteBuf, packet);
// 写入魔数
byteBuf.writeInt(MAGIC_NUMBER);
// 写入协议版本号
byteBuf.writeByte(packet.getVersion());
// 写入指令标识
byteBuf.writeByte(packet.getCommand());
// 写入序列化算法标识
byteBuf.writeByte(Test_10_Serializer.DEFAULT.getSerializerAlgorithm());
// 写入数据长度
byteBuf.writeInt(bs.length);
// 写入数据
byteBuf.writeBytes(bs);
return byteBuf;
}
// 解码
public Test_10_Packet deCode(ByteBuf byteBuf) throws Exception {
// 跳过魔数校验
byteBuf.skipBytes(4);
// 跳过版本号校验
byteBuf.skipBytes(1);
// 获取指令标识
byte command = byteBuf.readByte();
// 获取序列化算法标识
byte serializerAlgorthm = byteBuf.readByte();
// 获取数据长度
int len = byteBuf.readInt();
// 获取数据
byte[] bs = new byte[len];
byteBuf.readBytes(bs);
// 获取对应的序列化算法类
Test_10_Serializer serializer = getSerializer(serializerAlgorthm);
// 获取对应的数据包类
Test_10_Packet packet = getPacket(command);
if(serializer != null && packet != null) {
//反序列化数据包
return serializer.deSerialize(bs, packet.getClass());
}else {
throw new RuntimeException("没有找到对应的序列化实现或数据包实现");
}
}
private static Test_10_Packet getPacket(byte command) throws Exception {
return (Test_10_Packet) packetMap.get(command).newInstance();
}
private static Test_10_Serializer getSerializer(byte serializerAlgorthm) throws Exception {
return (Test_10_Serializer) serializerMap.get(serializerAlgorthm).newInstance();
}
}
/**
* 登录请求数据包实体类
*
* @author outman
*/
@Data
class Test_10_LoginRequestPacket extends Test_10_Packet {
private int userId ;
private String userName;
private String password;
@Override
public byte getCommand() {
return Command.LOGIN_REQUEST;
}
}
/**
* 登录响应数据包实体类
*
* @author outman
*/
@Data
class Test_10_LoginResponsePacket extends Test_10_Packet {
private int code;
private String msg;
@Override
public byte getCommand() {
return Command.LOGIN_RESPONSE;
}
/**
* 响应码集合
* */
interface Code{
// 成功的响应码
public static final int SUCCESS= 10000;
// 失败的响应码
public static final int FAIL = 10001;
}
}
/**
* Json序列化实现类
*
* @author outman
*/
class Test_10_JSONSerializer implements Test_10_Serializer {
@Override
public byte getSerializerAlgorithm() {
return SerializerAlgorithm.JSONSerializerAlgrothm;
}
@Override
public byte[] enSerialize(ByteBuf byteBuf, Test_10_Packet packet) {
return JSONObject.toJSONBytes(packet);
}
@Override
public T deSerialize(byte[] bs, Class clazz) {
return JSONObject.parseObject(bs, clazz);
}
}