【问题标题】:Checksum Algorithm Producing Unpredictable Results产生不可预测结果的校验和算法
【发布时间】:2026-01-20 16:20:05
【问题描述】:

我正在研究校验和算法,但遇到了一些问题。更重要的是,当我手工制作一个比我收到的“真实”数据小得多的“假”消息时,我得到了一个正确的校验和。但是,针对真实数据 - 校验和无法正常工作。

以下是有关传入数据/环境的一些信息:

  • 这是一个 groovy 项目(见下面的代码)
  • 出于校验和计算的目的,所有字节都将被视为无符号整数
    • 您会注意到一些短裤和长裤的搭配,以使其发挥作用。
  • 实际数据大小为491字节。
  • 我的示例数据的大小(看起来添加正确)为 26 个字节
  • 据我所知,我的十六进制到十进制转换都没有产生负数
  • 文件中的某些字节未添加到校验和中。我已经验证了这些开关是否正常工作,并且应该在什么时候正常工作 - 所以这不是问题。
  • 我计算出来的校验和,和真实传输打包的校验和总是相差相同的量。
  • 我已经手动验证了与真实数据打包的校验和是正确的。

代码如下:

// add bytes to checksum
public void addToChecksum( byte[] bytes) {
    //if the checksum isn't enabled, don't add
    if(!checksumEnabled) {
        return;
    }

    long previouschecksum =  this.checksum;

    for(int i = 0; i < bytes.length; i++) {
        byte[] tmpBytes = new byte[2];
        tmpBytes[0] = 0x00;
        tmpBytes[1] = bytes[i];

        ByteBuffer tmpBuf = ByteBuffer.wrap(tmpBytes);

        long computedBytes = tmpBuf.getShort();
        logger.info(getHex(bytes[i]) + " = " + computedBytes);
        this.checksum += computedBytes;
    }

    if(this.checksum < previouschecksum) {
        logger.error("Checksum DECREASED: " + this.checksum);
    }
    //logger.info("Checksum: " + this.checksum);
}

如果有人能在此算法中找到任何可能导致偏离预期结果的内容,我将非常感谢您帮助追踪此问题。

【问题讨论】:

  • 快速提问... 为什么不使用 md5、sha 或其他一些经过验证的散列算法?即:groovyconsole.appspot.com/script/256001
  • 在不深入了解您的实现细节的情况下,您为什么不使用任何知名、成熟且足够好的校验和算法,例如 CRC、MD5 或 SHA(从微不足道到接近理想)?跨度>
  • 好吧,首先让我声明我无法确定使用哪种校验和算法。所以,这是一个有争议的问题。我必须遵守协议 - 没有选择。其次,这样做是为了节省带宽。随着我们移动的数据量 - 使用这种方法转换为真正的美元符号。

标签: groovy checksum unsigned-integer


【解决方案1】:

我在您的代码中没有看到您重置 this.checksum 的行。

这样,您应该总是得到this.checksum &gt; previouschecksum,对吧?这是故意的吗?

否则我无法在您的上述代码中找到缺陷。也许您的“this.checksum”类型错误(例如缩写)。这可能会翻转,因此您会得到负值。

这是这种行为的一个例子

import java.nio.ByteBuffer
short checksum = 0
byte[] bytes = new byte[491]
def count = 260
for (def i=0;i<count;i++) {
    bytes[i]=255
}
bytes.each { b ->
    byte[] tmpBytes = new byte[2];
    tmpBytes[0] = 0x00;
    tmpBytes[1] = b;
    ByteBuffer tmpBuf = ByteBuffer.wrap(tmpBytes);
    long computedBytes = tmpBuf.getShort();
    checksum += computedBytes
    println "${b} : ${computedBytes}"
}
println checksum +"!=" + 255*count

只需使用与您输入的长度相对应的“count”变量的值。

【讨论】:

  • 这个方法只是更大类的一部分。是的,this.checksum 应该总是大于previouschecksum。我有那条线来检测我产生负数的可能性。这严格来说是一种调试策略。另外,this.checksum 很长。
  • 491 字节不算太多。你已经printlned 每次迭代的结果了吗?
  • 嗯,491 字节可能听起来不多,但它相当于大约 80 个命令。我很乐意分享让其他人检查我的数学,但这是专有的东西。 :-\
【解决方案2】:

您的校验和将不断增加,直到它翻转为负数(因为它是一个有符号长整数)

您还可以将方法缩短为:

public void addToChecksum( byte[] bytes) {
  //if the checksum isn't enabled, don't add
  if(!checksumEnabled) {
    return;
  }

  long previouschecksum =  this.checksum;
  this.checksum += bytes.inject( 0L ) { tot, it -> tot += it & 0xFF }

  if(this.checksum < previouschecksum) {
    logger.error("Checksum DECREASED: " + this.checksum);
  }
  //logger.info("Checksum: " + this.checksum);
}

但这不会阻止它转为消极。为了为每个要生成哈希的项目节省 12 个字节,我仍然建议像 MD5 这样已知可以工作的东西可能比自己滚动更好......但是我知道有时你必须有一些疯狂的要求坚持...

【讨论】:

  • 是的,幸运的是我没有遇到总和翻滚的情况。当我做我的添加时,它只是比它应该的要少,这非常令人沮丧。
  • @Matt 你有导致你看到的错误的示例字节数组吗?
最近更新 更多