Netty用7E作为头尾标识来识别数据帧。

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @describe TCP/IP数据解码器
 */
@Slf4j
@Component
public class MessageDecoderHandler extends ByteToMessageDecoder {

    private final ByteBuf header;
    private final ByteBuf ender;

    public MessageDecoderHandler() {
        this.header = Unpooled.buffer();
        header.writeByte(0x7E);
        this.ender = Unpooled.buffer();
        ender.writeByte(0x7E);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 显示十六进制的接收码
        log.info("收到数据:{}", ByteBufUtil.hexDump(in));
        if (ByteBufUtil.hexDump(in).startsWith("7e") && ByteBufUtil.hexDump(in).endsWith("7e")) {
            // 调用decode方法,识别帧头和帧尾
            ByteBuf childBuf = decode(in);
            // 如果获得有效数据
            if (childBuf != null) {
                // 将有效数据备份加入接收列表
                out.add(childBuf.copy());
                log.info("有效数据:{}", ByteBufUtil.hexDump(childBuf));
            }
        }
    }

    protected ByteBuf decode(ByteBuf buf) {
        // 帧头起始位置
        int sliceStart = 0;
        // 帧尾起始位置
        int sliceEnd = 0;
        // 数据帧
        ByteBuf frame = null;
        // 帧头是存在
        if (header != null) {
            // 获取帧头位置
            int index = ByteBufUtil.indexOf(header, buf);
            // 帧头第一次出现位置找到
            if (index > -1 && index < buf.capacity()) {
                // 舍弃帧头前面的数据
                buf.readerIndex(index + 1);
            }
            // 将帧头位置保存
            sliceStart = index + 1;
        }
        // 帧尾存在
        if (ender != null) {
            // 获取帧尾的起始位置
            int endindex = ByteBufUtil.indexOf(ender, buf);
            // 保存帧尾的位置
            sliceEnd = endindex;
            // 帧尾找到,并且在帧头的后面
            if (endindex > -1 && endindex > sliceStart && endindex < buf.capacity()) {
                // 计算数据帧的长度:帧尾的起始位置-帧头的起始位置-帧头的长度
                assert header != null;
                int length = sliceEnd - sliceStart - header.readableBytes();
                // 获取数据子帧
                frame = buf.slice(sliceStart - 2 + header.readableBytes(), length + 3);
                // 将reader索引设定到帧尾的后面
                buf.skipBytes(sliceEnd - sliceStart + ender.readableBytes());
                // 将数据帧返回
                return frame;
            }
        }
        //去掉剩余的垃圾数据
        if (sliceEnd == -1) {
            //将可读数据设置为0
            buf.skipBytes(buf.readableBytes());
        }
        return null;
    }
}