netty自定义解码器,帧头校验,异常数据丢弃,同步帧

这篇文章详细介绍了如何在Netty框架中创建一个自定义解码器,处理包含帧头、长度和CRC校验的字节流,通过三个不同的解码方法逐个字节解析帧结构。
摘要由CSDN通过智能技术生成

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; // 检查字节流总长度是否等于解码器需要的解码长度
    }




}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值