import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.nio.ByteBuffer;
import java.util.List;
public class CustomDecoder extends ByteToMessageDecoder {
private static Logger logger = LoggerFactory.getLogger(CustomDecoder.class);
private static final int HEADER_SIZE = 10;
private static final int LENGTH_SIZE = 4;
private static final int PK_TYPE_SIZE = 4;
private static final int CRC_SIZE = 2;
private static final String FRAME_HEADER = "#dlhjtxxy#";
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
// decode1(ctx, in, out);
//decode2(ctx, in, out);
//逐个字节读取帧头,直到匹配帧头
decode3(ctx, in, out);
}
/**
* 解码器1
* 直接读取
* @param ctx
* @param in
* @param out
*/
private void decode1(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() < HEADER_SIZE + LENGTH_SIZE) {
return;
}
in.markReaderIndex(); // 标记当前位置,以便重置
byte[] header = new byte[HEADER_SIZE];
in.readBytes(header);
char[] chars = ByteUtil.byteArrayTocharArray_ascii(header);// 读取帧头(10字节)(#dlhjtxxy#)(ASCII码)
if (!FRAME_HEADER.equals(new String(chars))) {
in.resetReaderIndex(); // 如果帧头不匹配,重置读取位置
return;
}
int length = in.readInt();
int totalLength = HEADER_SIZE + length + CRC_SIZE; // 计算整个帧的长度
if (in.readableBytes() < totalLength - HEADER_SIZE - LENGTH_SIZE) {
in.resetReaderIndex(); // 不够读取主体和CRC,重置读取位置
return;
}
in.resetReaderIndex(); // 重置读取位置到帧头
byte[] allBytes = readAllBytes(in, totalLength);
out.add(allBytes); // 将所有字节添加到out列表中
}
/**
* 解码器2
* 只判断帧头的第一个字节
* @param ctx
* @param in
* @param out
* @throws Exception
*/
protected void decode2(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
while (in.readableBytes() > 0) {
in.markReaderIndex(); // 标记当前位置,以便重置
byte firstByte = in.readByte();
//读取字符串第一个字符
char firstFRameHeader = getFirstFRameHeader();
if (firstByte == firstFRameHeader) { // 帧头的第一个字节
byte[] header = new byte[HEADER_SIZE];
in.readBytes(header);
char[] chars = ByteUtil.byteArrayTocharArray_ascii(header);
String frameHeader = firstFRameHeader + new String(chars);
if (FRAME_HEADER.equals(frameHeader)) {
int length = in.readInt();
int totalLength = HEADER_SIZE + LENGTH_SIZE + length + CRC_SIZE; // 计算整个帧的长度
if (in.readableBytes() < totalLength - HEADER_SIZE - LENGTH_SIZE) {
in.resetReaderIndex(); // 不够读取主体和CRC,重置读取位置
return;
}
byte[] allBytes = readAllBytes(in, totalLength);
out.add(allBytes); // 将所有字节添加到out列表中
return;
}
}
// 如果帧头不匹配,丢弃当前字节并继续
}
}
private static char getFirstFRameHeader() {
char firstFRameHeader = FRAME_HEADER.charAt(0);
return firstFRameHeader;
}
/**
* 解码器3
* 逐个字节读取帧头,直到匹配帧头
* @param ctx
* @param in
* @param out
* @throws Exception
*/
protected void decode3(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
while (in.readableBytes() > 0) {
in.markReaderIndex(); // 标记当前位置,以便重置
byte firstByte = in.readByte();
if (firstByte == getFirstFRameHeader()) { // 帧头的第一个字节
byte[] header = new byte[HEADER_SIZE];
header[0] = firstByte;
StringBuilder frameHeaderBuilder = new StringBuilder(String.valueOf(getFirstFRameHeader()));
for (int i = 0; i < HEADER_SIZE - 1; i++) {
if (!in.isReadable()) {
in.resetReaderIndex(); // 如果没有足够的字节来读取完整的帧头,重置读取位置
return;
}
byte nextByte = in.readByte();
header[i + 1] = nextByte;
char nextChar = ByteUtil.byteToChar_ascii(nextByte);
frameHeaderBuilder.append(nextChar);
if (!FRAME_HEADER.startsWith(frameHeaderBuilder.toString())) {
// logger.warn("帧头不匹配,丢弃当前字节并继续,firstByte:{},frameHeaderBuilder:{},消息来源:{}", firstByte, frameHeaderBuilder.toString(), ctx.channel().remoteAddress());
break; // 如果帧头不匹配,停止读取,丢弃已读取字节
}
}
if (FRAME_HEADER.equals(frameHeaderBuilder.toString())) {
byte[] lengthByte = new byte[4];
in.readBytes(lengthByte);
int length = ByteUtil.bytesToIntBigEnd(lengthByte);
// int length = in.readInt();
int totalLength = HEADER_SIZE + length + CRC_SIZE; // 计算整个帧的长度
if (in.readableBytes() < totalLength - HEADER_SIZE - LENGTH_SIZE) {
in.resetReaderIndex(); // 不够读取主体和CRC,重置读取位置
return;
}
// byte[] allBytes = readAllBytes(in, totalLength);
byte[] allBytes = readAllBytes(in, totalLength, header,lengthByte); // 读取所有字节,包括帧头
// boolean validFrame = isValidFrame(allBytes);
ByteBuf buffer = Unpooled.wrappedBuffer(allBytes);
out.add(buffer); // 将所有字节添加到out列表中
return;
}
}else {
// 如果帧头不匹配,丢弃当前字节并继续
// logger.warn("帧头不匹配,丢弃当前字节并继续,firstByte:{},消息来源:{}", firstByte, ctx.channel().remoteAddress());
}
}
}
/*public Msg decodeToMsg(ByteBuf in) {
int serialNo = in.readInt();
int pkType = in.readInt();
int length = in.readInt() - SERIAL_NO_SIZE - PK_TYPE_SIZE;
byte[] body = new byte[length];
in.readBytes(body);
byte[] crc = new byte[CRC_SIZE];
in.readBytes(crc);
// 可以添加CRC校验
return new Msg(serialNo, pkType, body);
}*/
public Msg decodeToMsg(ByteBuf in) {
int serialNo = in.readInt();
int pkType = in.readInt();
int length = in.readInt();
byte[] body = new byte[length];
in.readBytes(body);
byte[] crc = new byte[CRC_SIZE];
in.readBytes(crc);
// 可以添加CRC校验
return new Msg(serialNo, pkType, body);
}
public byte[] readAllBytes(ByteBuf in, int length) {
byte[] bytes = new byte[length];
in.readBytes(bytes);
return bytes;
}
public byte[] readAllBytes(ByteBuf in, int totalLength, byte[] header) {
byte[] bytes = new byte[totalLength];
System.arraycopy(header, 0, bytes, 0, HEADER_SIZE);
in.readBytes(bytes, HEADER_SIZE, totalLength - HEADER_SIZE);
return bytes;
}
public byte[] readAllBytes(ByteBuf in, int totalLength, byte[] header, byte[] lengthByte) {
byte[] bytes = new byte[totalLength];
System.arraycopy(header, 0, bytes, 0, HEADER_SIZE);
System.arraycopy(lengthByte, 0, bytes, HEADER_SIZE, LENGTH_SIZE);
in.readBytes(bytes, HEADER_SIZE + LENGTH_SIZE, totalLength - HEADER_SIZE - LENGTH_SIZE);
return bytes;
}
/**
* 检查字节流是否是一个完整的帧
* @param bytes
* @return
*/
public boolean isValidFrame(byte[] bytes) {
if (bytes.length < HEADER_SIZE + LENGTH_SIZE) {
return false; // 不够读取帧头和长度
}
char[] chars = ByteUtil.byteArrayTocharArray_ascii(Arrays.copyOfRange(bytes, 0, HEADER_SIZE)); // 读取帧头(10字节)(#dlhjtxxy#)(ASCII码)
if (!FRAME_HEADER.equals(new String(chars))) {
return false; // 帧头不匹配
}
int length = ByteBuffer.wrap(bytes, HEADER_SIZE, LENGTH_SIZE).getInt();
int totalLength = HEADER_SIZE + length + CRC_SIZE; // 计算整个帧的长度
return bytes.length == totalLength; // 检查字节流总长度是否等于解码器需要的解码长度
}
}
netty自定义解码器,帧头校验,异常数据丢弃,同步帧
最新推荐文章于 2024-10-02 17:00:59 发布
这篇文章详细介绍了如何在Netty框架中创建一个自定义解码器,处理包含帧头、长度和CRC校验的字节流,通过三个不同的解码方法逐个字节解析帧结构。
摘要由CSDN通过智能技术生成