netty传输报文进阶使用LengthFieldBasedFrameDecoder

netty可以传输报文,为了解决数据粘连,自己费力气写了缓存

private void addCmd(byte[] bytes, byte len1, byte len2, List list) {
        log.debug("Jc5000Service Jc5000Decoder decode jcBuffer.limit  memory ={}",jcBuffer.limit());
        Jc5000Response jc5000Response = new Jc5000Response();
        byte cmdCode = (byte) (bytes[CMD_POS] & 0xff);
        if (log.isDebugEnabled()) {
            log.debug("Jc5000Service Jc5000Decoder decode command is cmdCode=" + cmdCode + "," + Jc5000CommandEnum.getJc5000CommandEnumKeyByCode(cmdCode));
        }
        jc5000Response.setCmdCode(cmdCode);
        jc5000Response.setCommand(Jc5000CommandEnum.getJc5000CommandEnumByCode(cmdCode));
        jc5000Response.setLEN(new byte[]{len1, len2});
        jc5000Response.setBCC(bytes[bytes.length - 2]);
        if (log.isDebugEnabled()) {
            log.debug("Jc5000Service Jc5000Decoder success parse addcmd {}", bytes2HexString(bytes));
        }
        jc5000Response.setDATA(bytes);
        byte reponseFlag = bytes[SUCCESS_POS];
        jc5000Response.setReponseFlag(reponseFlag);
        if (reponseFlag == Constants.SUCCESS) {
            jc5000Response.setIS_SUCCEEDED(true);
            log.info("Jc5000Service Jc5000Decoder cmd  execute success");
            TvmRuntimeCoreDeviceStatusManager.getInstance().setJc5000ReaderStatus(DeviceStatusEnum.NORMAL.getCode());
            TvmRuntimeCoreDeviceStatusManager.getInstance().setJc5000ReaderrCode(StringUtils.EMPTY);
        } else if (reponseFlag == Constants.FAIL) {
            byte[] errorCode = Arrays.copyOfRange(bytes, 5, bytes.length);
            jc5000Response.setErrorcode(errorCode);
            if (errorCode != null && errorCode.length >= 2) {
                jc5000Response.setErrCode(String.format("%02dH", Integer.valueOf(errorCode[1])));
                jc5000Response.setErrUniqueCode(bytes2HexString(new byte[]{errorCode[0],errorCode[1]}));
            }
            log.error("Jc5000Service Jc5000Decoder execute failure , {}",bytes2HexString(bytes));
        }else{
            log.error("Jc5000Service Jc5000Decoder execute ,other failure, result={}", reponseFlag);
        }
        list.add(jc5000Response);
    }

    /**
     * 读取到缓存里所有数据
     */
    public byte[] conver(ByteBuffer byteBuffer) {
        byteBuffer.flip();
        byteBuffer.rewind();
        int len = byteBuffer.limit() - byteBuffer.position();
        byte[] bytes = new byte[len];
        if (byteBuffer.isReadOnly()) {
            return null;
        } else {
            byteBuffer.get(bytes);
        }
        return bytes;
    }

    /**
     * 处理一个数据包
     */
    private int dealPacket(byte[] tmp, List list) {
        if (tmp.length >= PACKET_LEN && (tmp[0] == START_DATA[0] && tmp[1] == START_DATA[1] && tmp[2] == START_DATA[2])
                && (tmp[3] == tmp[5] && tmp[4] == tmp[6]) && tmp.length > (HEAD_LEN + bytes2Int(tmp[3], tmp[4]) + 1)
                && tmp[HEAD_LEN + bytes2Int(tmp[3], tmp[4]) + 1] == START_DATA[0]) {
            int i = bytes2Int(tmp[3], tmp[4]);
            byte[] bytes = Arrays.copyOfRange(tmp, HEAD_LEN, HEAD_LEN + i);
            if (ByteArrayUtils.sumBCC(bytes) == tmp[HEAD_LEN + i]) {
                addCmd(bytes, tmp[3], tmp[4], list);
                return HEAD_LEN + bytes2Int(tmp[3], tmp[4]) + 2;
            }
        }
        return 0;
    }

    /**
     * 处理缓存数据
     */
    private void dealbuf(List list) {
        byte[] tmp = conver(jcBuffer);
        jcBuffer.compact();
        if (tmp == null) {
            return;
        }
        byte[] acks = Arrays.copyOfRange(tmp, 0, ACK_LEN);
        if (tmp.length > ACK_LEN) {
            int k = 0;
            //剔除ack 长度大于ack,前面数据等于ack或nack 并且长度不等于长度校验
            if ((Arrays.equals(ACK, acks) || Arrays.equals(NACK, acks)) && (acks[ACK_LEN - 1] != acks[3] || tmp[ACK_LEN] != acks[4])) {
                k = ACK_LEN + dealPacket(Arrays.copyOfRange(tmp, ACK_LEN, tmp.length), list);
            } else {
                k = dealPacket(tmp, list);
            }
            if (k < tmp.length) {
                jcBuffer.put(Arrays.copyOfRange(tmp, k, tmp.length));
            }
            return;
        } else if (tmp.length == ACK_LEN && (Arrays.equals(ACK, acks) || Arrays.equals(NACK, acks))) {
            log.info("Jc5000Service Jc5000Decoder decode ack tmp : {}", tmp);
            //ack或nack 直接丢弃不用特别处理
            return;
        }
        //不完整的数据放回缓冲区
        jcBuffer.put(tmp);
    }

后来跟底层沟通去掉了不含长度的应答帧ack,那就只有一种格式的信息帧了,改进下用netty的插件

LengthFieldBasedFrameDecoder,这下代码非常精简了
开启时增加开关是否启用这个插件
  public Boolean enableFrameDecoder = false;
    /**
      * 创建连接channel 分两步执行
     * 1.设置bootstrap 信息
     * 2.创建channel
     *
     */
    @Override
    public boolean init() {
        ready = false;
        setBootstrap();
        return connect();
    }
    /**
     * 设置bootstrap
     * @return
     */
    protected boolean connect() {
        createSerialChannel();
        return ready;
    }
    public void setBootstrap() {
        if (bootstrap.config().group() == null) {
            //标识不限制连接数
            int maxChannels = 0;
            //标识不限制连接数
            EventLoopGroup group = new OioEventLoopGroup(maxChannels, new SerialDeviceThreadFactory(getModulePrefix()));
            bootstrap.group(group);
        }
        if (bootstrap.config().channelFactory() == null) {
            bootstrap.channel(PureJavaCommChannel.class)
                    .option(PureJavaCommChannelOption.BAUD_RATE, getSerialDeviceConfig().getBaudRate())
                    .option(PureJavaCommChannelOption.READ_TIMEOUT, 5)
                    .option(PureJavaCommChannelOption.WAIT_TIME, 5);
        }
        bootstrap.handler(new ChannelInitializer<PureJavaCommChannel>() {
            @Override
            public void initChannel(PureJavaCommChannel ch) throws Exception {
                try {
                    ChannelPipeline channelPipeline = ch.pipeline();
                    //channelPipeline.addLast("logging", new LoggingHandler(LogLevel.DEBUG))
                    if(enableFrameDecoder) {
                        channelPipeline.addFirst(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 5, 2, 2, 5));
                    }
                    channelPipeline.addLast(getDecoder());
                    ChannelHandler channelHandler = getExtendChannelHandler();
                    if (channelHandler != null) {
                        channelPipeline.addLast(channelHandler);
                    }
                    channelPipeline.addLast(getEncoder());
                    channelPipeline.addLast(getHandler());
                } catch (Exception e) {
                    ready = false;
                    log.error("Init Channel failed.", e);
                }
            }
        });
    }

    /**
     * 创建串口创建方法
     */
    public boolean createSerialChannel() {
        try {
            //用于修复init失败的问题
            //java.lang.IllegalStateException: group not set
            if (bootstrap.config().group() == null) {
                this.setBootstrap();
            }
            if (channel == null || !channel.isActive()) {
                ChannelFuture f = bootstrap.connect(new PureJavaCommDeviceAddress(getSerialDeviceConfig().getCom()));
                channel = f.channel();
                f.addListener((ChannelFutureListener) future -> {
                    if (!future.isSuccess()) {
                        future.channel().close();
                        ready = false;
                        channel = null;
                        log.error("Serial port {} open failed.", getSerialDeviceConfig().getCom());
                    } else {
                        ready = true;
                        channel = future.channel();
                        log.debug("Serial port {} open success.", getSerialDeviceConfig().getCom());
                    }
                }).sync();
            }
            if (channel != null && channel.isActive()) {
                ready = true;
            } else {
                ready = false;
            }
        } catch (Exception e) {
            ready = false;
            log.error("Connect serial port failed.", e);
        }
        return ready;
    }

    /**
     * @param key
     * @param message
     * @return
     */
    public synchronized SyncFuture<Object> send(String key, Object message) {
        SyncFuture<Object> future = new SyncFuture<>(key);
        if (SyncFutureMap.get(key) != null) {
            log.error("SyncFuture send key={} value is not null ,this.getClass()={}", key, this.getClass());
            return null;
        }
        if (channel == null) {
            log.error("SyncFuture send key={} ,this.getClass()={}  channel is null ", key, this.getClass());
            return null;
        }
        SyncFutureMap.put(key, future);
        channel.writeAndFlush(message);
        return future;
    }
    @Override
    public void close() {
        try {
            if (channel != null) {
                channel.close();
            }
            //检查线程池是否关闭,如果没有关闭则关闭
            EventLoopGroup eventLoopGroup = bootstrap.group();
            if (!eventLoopGroup.isShutdown()) {
                eventLoopGroup.shutdownGracefully();
            }
        } catch (Exception ex) {
        }
    }
    /**
     * 用于特殊情况扩展串口Handler
     */
    protected ChannelHandler getExtendChannelHandler() {
        return null;
    }

解码时就可以直接解码

 @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception {
        int length = byteBuf.readableBytes();
        if (length > 0) {
            byte[] bytesTmp = new byte[length];
            byteBuf.readBytes(bytesTmp);
              log.debug("jc5000 decode bytesTmp : {}", bytes2HexString(bytesTmp));
            byte[] bytes = Arrays.copyOfRange(bytesTmp, 2, bytesTmp.length-2);
            addCmd( bytes,  bytesTmp[0],  bytesTmp[1],  list);
           // jcBuffer.put(bytesTmp);
          //  dealbuf(list);
        }
    }
  private void addCmd(byte[] bytes, byte len1, byte len2, List list) {
        log.debug("Jc5000Service Jc5000Decoder decode jcBuffer.limit  memory ={}",jcBuffer.limit());
        Jc5000Response jc5000Response = new Jc5000Response();
        byte cmdCode = (byte) (bytes[CMD_POS] & 0xff);
        if (log.isDebugEnabled()) {
            log.debug("Jc5000Service Jc5000Decoder decode command is cmdCode=" + cmdCode + "," + Jc5000CommandEnum.getJc5000CommandEnumKeyByCode(cmdCode));
        }
        jc5000Response.setCmdCode(cmdCode);
        jc5000Response.setCommand(Jc5000CommandEnum.getJc5000CommandEnumByCode(cmdCode));
        jc5000Response.setLEN(new byte[]{len1, len2});
        jc5000Response.setBCC(bytes[bytes.length - 2]);
        if (log.isDebugEnabled()) {
            log.debug("Jc5000Service Jc5000Decoder success parse addcmd {}", bytes2HexString(bytes));
        }
        jc5000Response.setDATA(bytes);
        byte reponseFlag = bytes[SUCCESS_POS];
        jc5000Response.setReponseFlag(reponseFlag);
        if (reponseFlag == Constants.SUCCESS) {
            jc5000Response.setIS_SUCCEEDED(true);
            log.info("Jc5000Service Jc5000Decoder cmd  execute success");
            
        } else if (reponseFlag == Constants.FAIL) {
            byte[] errorCode = Arrays.copyOfRange(bytes, 5, bytes.length);
            jc5000Response.setErrorcode(errorCode);
            if (errorCode != null && errorCode.length >= 2) {
                jc5000Response.setErrCode(String.format("%02dH", Integer.valueOf(errorCode[1])));
                jc5000Response.setErrUniqueCode(bytes2HexString(new byte[]{errorCode[0],errorCode[1]}));
            }
            log.error("Jc5000Service Jc5000Decoder execute failure , {}",bytes2HexString(bytes));
        }else{
            log.error("Jc5000Service Jc5000Decoder execute ,other failure, result={}", reponseFlag);
        }
        list.add(jc5000Response);
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值