美文网首页
java不存在无符号类型引发的异常解决过程

java不存在无符号类型引发的异常解决过程

作者: 有时右逝 | 来源:发表于2018-06-12 16:45 被阅读0次

前言

公司项目是一款路灯控制系统,系统分为2大部分。上层服务端接受和下发指令,底层集中器接受和上报指令。服务端是java语言开发,底层集中器是c语言开发。
由于二者语言的数据类型存在差异,导致了一个隐藏较深的bug。

问题

近期项目要上线了,领导让增加一个单灯告警功能。该功能负责监听路灯是否故障。
在开发中,出现一些奇怪的现象。例如添加单灯档案时,如果少量添加,则可以顺利进行。如果批量添加,则集中器掉线断网重启。

排查

  • 由于单灯告警功能在原有的.net服务端可以正常运行,我们选择对比.net服务端和java服务端的差异,在反复重复操作,对比指令的区别,结果是无功而返。二者产生的指令和下发的指令是相同的。

  • 指令不存在问题,那么现在可能存在的问题就是java服务在运行时可能哪里产生了异常。这里不断增加可能出现故障的地方,输出日志。
    最终定位到故障的地方


    image.png
  • 再次增加更多日志输出。检查发送该异常之前的信息。


    image.png
  • 其中-47引起我的注意,是不应该出现的。因为这里输出的是底层发送的二进制数据。不会出现负值。

  • 继续查看这里输出的代码

if (logger.isDebugEnabled()){
            byte[] dst = new byte[in.readableBytes()];
            in.readBytes(dst);
            logger.debug("cached="+Arrays.toString(dst));
            in.readerIndex(beginIndex);
        }

由于java的Byte 是有符号类型的。所以如果byte值超过127 则会变成负数。

  • 这个 -47 的位置,根据集中器通信协议,是属于长度范围。于是查看长度解析的代码
        byte length1 = in.readByte() ;
        byte length2 = in.readByte();
        long length = length2 << 8 | length1;

这里的含义是 长度用2个byte表示,集中器发送长度的时候,会先发低位。再发高位。
因为解析的时候, 需要进行 位操作。

  • 根据异常的超标提示,应该是这里长度出现问题。输出一下解析的长度。是 -47

那么问题找到了,怎么会解析出 -47呢。

原因

这是因为集中器c语言和java语言的基本数据类型差异导致的。

  • 在计算机中,可以区分正负的类型,称为有符类型(signed),无正负的类型(只有正值),称为无符类型。 (unsigned)
  • 使用二制数中的最高位表示正负。
  • 有符号和无符号存在范围不同。

这里以8位的byte为例。

在c语言中,byte可以是有符号,也可以无符号。集中器这里选择的是无符号的byte.
它可表示的数值范围 是 0-256
但是在java语言中,不存在无符号类型。因此 byte的范围是 -127 到 127

因此如果一个数字,在0-127之间。java和c语言的结果是相同的。不会出错。
如果这个数值超过127,例如集中器发送了一个 128 则java这里就会异常。
因为这个数值超出了byte的范围。

解决

既然是数据类型的范围不满足需要。这里选择比byte 大的范围来盛放数据。

  • 修改代码如下:
       //低位
        int length1 = in.readByte() ;
        //高位
        int length2 = in.readByte() ;
        long length = length2 << 8 | length1;

依旧不行。

  • 继续修改
        //低位
        int length1 =in.readByte()  & 0x000000FF;
        //高位
        int length2 =in.readByte()  & 0x000000FF;

        int length = length2 << 8 | length1;

依旧不行。

  • 最终修改
        //低位
        int length1 = (int)dst[2] & 0x000000FF;
        //高位
        int length2 = (int)dst[3] & 0x000000FF;

        int length = length2 << 8 | length1;

解释: 把有符号的 byte 提升成 int 类型,然后对这个 int 进行按位与操作,仅保留最后 8 个比特位。因为 Java 中的 byte 是有符号的,所以当一个 byte 的无符号值大于 127 的时候,表示符号的二进制位将被设置为 1(严格来说,这个不能算是符号位,因为在计算机中数字是按照补码方式编码的),对于 Java 来说,这个就是负数。当将负数数值对应的 byte 提升为 int 类型的时候,0 到 7 比特位将会被保留,8 到 31 比特位会被设置为 1。然后将其与 0x000000FF 进行按位与操作来擦除 8 到 31 比特位的 1。

  • 最终可以简化
        //低位
        int length1 = (int)dst[2] & 0xFF;
        //高位
        int length2 = (int)dst[3] & 0xFF;

        int length = length2 << 8 | length1;

参考资料

相关文章

  • java不存在无符号类型引发的异常解决过程

    前言 公司项目是一款路灯控制系统,系统分为2大部分。上层服务端接受和下发指令,底层集中器接受和上报指令。服务端是j...

  • Java数据类型精讲

    原文链接 Java数据类型精讲 JAVA中数值的特点 Java中的数值都是有符号的,不存在无符号的数,它们的取值范...

  • Java的无符号类型移位

    Java的原始类型里没有无符号类型,如果需要某个宽度的无符号类型,可以用>>>,这个是java的无符号右移操作符,...

  • Guava-原生类型

    无符号和有符号 在计算机中,可以区分正负的类型,称为有符号类型,无正负的类型,称为无符号类型。 概述 Java的原...

  • JNI异常

    JNI异常 JNI允许native方法引发任意Java异常。native代码还可以处理未解决的Java异常。未处理...

  • Class类文件结构

    按照Java虚拟机规范的规定,Class文件结构只有两种数据类型:无符号数和表 无符号数无符号数属于基本的数据类型...

  • (2) 面试

    java基本的数据类型有几种,无符号与有符号整型有什么区别?8种4类:逻辑类型:boolean;字符类型:char...

  • java.lang.IllegalStateException

    状态异常。java.lang.IllegalStateException异常产生的原因及解决办法 错误类型大致为以...

  • 解决:Python将列表保存为txt时数字前始终有字母u

    原因 数字为无符号类型(无符号整型等) 尝试过 encode('utf-8'),不能解决 解决方法 将数据转换为其...

  • Java异常捕获

    异常Exception异常:就是Java程序在运行过程中出现的错误。前面接触过的空指针,数组越界,类型转换错误异常...

网友评论

      本文标题:java不存在无符号类型引发的异常解决过程

      本文链接:https://www.haomeiwen.com/subject/zjaleftx.html