Netty中数据通信需要自行根据业务场景抽象出客户端与服务器端通讯协议: 通讯过程中需要进行ByteBuf与JAVA对象中的编解码问题:
直接上代码:
=======================解码器:二进制转换为JAVA对象======================
package org.jy.sso.websocket.stomp.push.netty.chat.system.chatedcode;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* 数据包解码器: 二进制装换为JAVA对象
*/
@Slf4j
public class PacketDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
out.add(PacketEncodeDecode.INSTANCE.decode(byteBuf));
}
}
==============================编码器: JAVA对象转换为ByteBuf===========================
package org.jy.sso.websocket.stomp.push.netty.chat.system.chatedcode;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.DataPacket;
/**
* 数据包编码器: JAVA对象转换为二进制装换
*/
public class PacketEncoder extends MessageToByteEncoder<DataPacket> {
@Override
protected void encode(ChannelHandlerContext ctx, DataPacket packet, ByteBuf out) throws Exception {
PacketEncodeDecode.INSTANCE.encode(out,packet);
}
}
通讯协议编解码控制器:
package org.jy.sso.websocket.stomp.push.netty.chat.system.chatedcode;
import cn.hutool.core.util.ObjectUtil;
import io.netty.buffer.ByteBuf;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatcommond.ChatCommand;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.DataPacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatLoginRequestPacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatMessageRequestPacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatHeartBeatResponsePacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatLoginResponsePacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatMessageResponsePacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatserializer.ChatSerializer;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatserializer.ChatSerializerType;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatserializer.impl.ChatIMSerializer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 通讯协议数据包编解码器
*/
public class PacketEncodeDecode {
/**
* 协议魔法值
*/
public static final int MAGIC_NUMBER = 0x12345678;
// 单例
public static final PacketEncodeDecode INSTANCE = new PacketEncodeDecode();
// 存放指令与对应的请求类,这里的value,对泛型的上线的限定
private Map<Byte, Class<? extends DataPacket>> packetTypeMap;
// 系列化接口
private final Map<Byte, ChatSerializer> serializerMap;
// 构造函数中初始化化,项目上线后这里一般都基本上不变
private PacketEncodeDecode() {
packetTypeMap = new HashMap<>();
// 线程安全
packetTypeMap = Collections.synchronizedMap(packetTypeMap);
//心跳检测请求指令
packetTypeMap.put(ChatCommand.HEARTBEAT_REQUEST_COMMAND, ChatHeartBeatRequestPacket.class);
// 心跳检测响应指令
packetTypeMap.put(ChatCommand.HEARTBEAT_RESPONSE_COMMAND, ChatHeartBeatResponsePacket.class);
// 线程安全
serializerMap = new ConcurrentHashMap<>();
ChatSerializer chatMsgSerializer = new ChatIMSerializer();
// 系列化算法key,对应的实现类
serializerMap.put(chatMsgSerializer.getSerializerAlgorithm(), chatMsgSerializer);
}
/**
* @param packet 传递的数据协议包
* @return {@link io.netty.buffer.ByteBuf}}
* @deprecated 编码封装协议的数据的过程
*/
public ByteBuf encode(ByteBuf byteBuf, DataPacket packet) {
// 2.系列化Java对象,java对象要系列化,必须实现Serializer接口,否则返回null
byte[] bytes = ChatSerializerType.DEFAULT_ALGORITHM_Serializer.serializer(packet);
// 3.实际编码过程
byteBuf.writeInt(MAGIC_NUMBER);// 协议魔法值
byteBuf.writeByte(packet.getVersion()); // 版本号
byteBuf.writeByte(ChatSerializerType.DEFAULT_ALGORITHM_Serializer.getSerializerAlgorithm()); // 系列化算法
// 之类实现了该方法
byteBuf.writeByte(packet.getCommand()); // 指令
// 数组的长度,在解码的时候取出来
byteBuf.writeInt(bytes.length);
byteBuf.writeBytes(bytes);
return byteBuf;
}
/**
* @param byteBuf 数据包缓存区
* @return {@link org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.DataPacket}
* @deprecated 解码, 将二进制封装成对象的过程
*/
public DataPacket decode(ByteBuf byteBuf) {
// 跳过魔数
byteBuf.skipBytes(4);
// 跳过版本号
byteBuf.skipBytes(1);
// 获取系列化算法标识
byte serializeAlgorithm = byteBuf.readByte();
// 指令
byte command = byteBuf.readByte();
// 数据包的长度
int packetLength = byteBuf.readInt();
// 读取客户端传递过来的数据包数组的长度
byte[] bytes = new byte[packetLength];
byteBuf.readBytes(bytes);
Class<? extends DataPacket> chatRequest = matchChatRequestType(command);
ChatSerializer serializer = matchMsgSerializer(serializeAlgorithm);
return ObjectUtil.isAllNotEmpty(chatRequest, serializer) ? serializer.deserialize(chatRequest, bytes) : null;
}
/**
* @param serializeAlgorithm 系列化算法标识
* @return {@link ChatSerializer}
* @deprecated 根据系列化标识,获取对应的系列化算法
*/
private ChatSerializer matchMsgSerializer(byte serializeAlgorithm) {
return serializerMap.get(serializeAlgorithm);
}
/**
* 根据指令获取对应的请求数据包实体
*
* @param command 指令
* @return {@link Class<? extends DataPacket>}
*/
private Class<? extends DataPacket> matchChatRequestType(byte command) {
return packetTypeMap.get(command);
}
}
协议指令集:
package org.jy.sso.websocket.stomp.push.netty.chat.system.chatcommond;
/**
* 各种指令常量
*/
public interface ChatCommand {
/**
* 登录请求指令
*/
Byte LOGIN_REQUEST_COMMAND = 1;
/**
* 登录响应指令
*/
Byte LOGIN_RESPONSE_COMMAND = 2;
/**
* 消息请求指令
*/
Byte MESSAGE_REQUEST_COMMAND = 3;
/**
* 消息响应指令
*/
Byte MESSAGE_RESPONSE_COMMAND = 4;
/**
* 心跳检测请求指令
*/
Byte HEARTBEAT_REQUEST_COMMAND = 17;
/**
* 心跳检测响应指令
*/
Byte HEARTBEAT_RESPONSE_COMMAND = 18;
}