如何从Java中的字节[]计算internet校验和

如何从Java中的字节[]计算internet校验和,java,checksum,ip-protocol,Java,Checksum,Ip Protocol,我正试图弄明白如何用Java计算互联网校验和,这给我带来了无尽的痛苦。(我对位操作很糟糕。)我在C#中找到了一个版本。然而,我试图将其转换为Java,却没有看到正确的结果。有人能看出我做错了什么吗?我怀疑有数据类型问题 public long getValue() { byte[] buf = { (byte) 0xed, 0x2A, 0x44, 0x10, 0x03, 0x30}; int length = buf.length; int i = 0; lon

我正试图弄明白如何用Java计算互联网校验和,这给我带来了无尽的痛苦。(我对位操作很糟糕。)我在C#中找到了一个版本。然而,我试图将其转换为Java,却没有看到正确的结果。有人能看出我做错了什么吗?我怀疑有数据类型问题

public long getValue() {
    byte[] buf = { (byte) 0xed, 0x2A, 0x44, 0x10, 0x03, 0x30};
    int length = buf.length;
    int i = 0;

    long sum = 0;
    long data = 0;
    while (length > 1) {
        data = 0;
        data = (((buf[i]) << 8) | ((buf[i + 1]) & 0xFF));

        sum += data;
        if ((sum & 0xFFFF0000) > 0) {
            sum = sum & 0xFFFF;
            sum += 1;
        }

        i += 2;
        length -= 2;
    }

    if (length > 0) {
        sum += (buf[i] << 8);
        // sum += buffer[i];
        if ((sum & 0xFFFF0000) > 0) {
            sum = sum & 0xFFFF;
            sum += 1;
        }
    }
    sum = ~sum;
    sum = sum & 0xFFFF;
    return sum;
}
public long getValue(){
字节[]buf={(字节)0xed,0x2A,0x44,0x10,0x03,0x30};
int length=buf.length;
int i=0;
长和=0;
长数据=0;
而(长度>1){
数据=0;
数据=((buf[i])0){
sum=sum&0xFFFF;
总和+=1;
}
i+=2;
长度-=2;
}
如果(长度>0){
总和+=(buf[i]0){
sum=sum&0xFFFF;
总和+=1;
}
}
sum=~sum;
sum=sum&0xFFFF;
回报金额;
}

我认为是类型升级造成了麻烦。让我们看看
数据=((buf[I])编辑后应用@Andy、@EJP、@RD等人的评论,并添加额外的测试用例,以确保这一点

我使用了@Andys-answer(正确识别问题的位置)的组合,并更新了代码,以包括链接答案中提供的单元测试以及附加测试用例

首先是实施

package org.example.checksum;

public class InternetChecksum {

  /**
   * Calculate the Internet Checksum of a buffer (RFC 1071 - http://www.faqs.org/rfcs/rfc1071.html)
   * Algorithm is
   * 1) apply a 16-bit 1's complement sum over all octets (adjacent 8-bit pairs [A,B], final odd length is [A,0])
   * 2) apply 1's complement to this final sum
   *
   * Notes:
   * 1's complement is bitwise NOT of positive value.
   * Ensure that any carry bits are added back to avoid off-by-one errors
   *
   *
   * @param buf The message
   * @return The checksum
   */
  public long calculateChecksum(byte[] buf) {
    int length = buf.length;
    int i = 0;

    long sum = 0;
    long data;

    // Handle all pairs
    while (length > 1) {
      // Corrected to include @Andy's edits and various comments on Stack Overflow
      data = (((buf[i] << 8) & 0xFF00) | ((buf[i + 1]) & 0xFF));
      sum += data;
      // 1's complement carry bit correction in 16-bits (detecting sign extension)
      if ((sum & 0xFFFF0000) > 0) {
        sum = sum & 0xFFFF;
        sum += 1;
      }

      i += 2;
      length -= 2;
    }

    // Handle remaining byte in odd length buffers
    if (length > 0) {
      // Corrected to include @Andy's edits and various comments on Stack Overflow
      sum += (buf[i] << 8 & 0xFF00);
      // 1's complement carry bit correction in 16-bits (detecting sign extension)
      if ((sum & 0xFFFF0000) > 0) {
        sum = sum & 0xFFFF;
        sum += 1;
      }
    }

    // Final 1's complement value correction to 16-bits
    sum = ~sum;
    sum = sum & 0xFFFF;
    return sum;

  }

}

较短的版本如下所示:

long checksum(byte[] buf, int length) {
    int i = 0;
    long sum = 0;
    while (length > 0) {
        sum += (buf[i++]&0xff) << 8;
        if ((--length)==0) break;
        sum += (buf[i++]&0xff);
        --length;
    }

    return (~((sum & 0xFFFF)+(sum >> 16)))&0xFFFF;
}
长校验和(字节[]buf,整数长度){
int i=0;
长和=0;
而(长度>0){
sum+=(buf[i++]&0xff>16))&0xFFFF;
}

感谢额外的单元测试。关于我的代码是否由于一些错误的测试而不正确,我得到了相互矛盾的答案。@chotchki我已经编辑了答案,以包含来自@Andy et al的注释为什么
calculateChecksum
返回长值?UDP、TCP和IPv4校验和需要两个字节。所以int(最多4个字节)应该是足够的代码是根据RFC1071第4.1节中的示例修改的,该示例使用了long作为其和。在编写RFC时,long可能是32位。这在很大程度上与避免Java中的类型升级有关。有关更多详细信息,请查看其他答案(@Andy现在是@Andrey Balaguta)#2不正确。文本0xFF被视为int,而不是字节。在步骤#3中,您将有一个短的| int,其中第一个参数得到提升,而不是第二个参数。因此,问题不在于您的示例中的0x00,0xFF,而在于0xFF、0x00(或任何buf[i]>0x7F)。当第一个参数从short扩展到int时,它是符号扩展的,正如您所解释的。在原始程序中,是0xed,0x2A的情况导致校验和不正确。这篇文章是非常不正确的。#1,#2和#3的结果是int,除非其中任何一个参数是long,在这种情况下它是long。en轮胎表达式类似于一个int,当存储在一个int中时,它会被加宽到一个long。@RD:老实说,我不知道如何解决这个问题。@EJP,@RD,谢谢你的更正,很抱歉误导。我已经相应地更正了帖子。它是符号扩展,不是“符号扩展”。我喜欢你的版本,但

long checksum(byte[] buf, int length) {
    int i = 0;
    long sum = 0;
    while (length > 0) {
        sum += (buf[i++]&0xff) << 8;
        if ((--length)==0) break;
        sum += (buf[i++]&0xff);
        --length;
    }

    return (~((sum & 0xFFFF)+(sum >> 16)))&0xFFFF;
}