根据@Andy、@EJP、@RD等人的评论进行了编辑,并添加了额外的测试用例以确保正确性。
我使用了@Andy的解答(正确地确定了问题的位置),并更新了代码以包含链接答案中提供的单元测试,同时还添加了一个经过验证的消息校验和的附加测试用例。
首先是实现部分。
package org.example.checksum;
public class InternetChecksum {
public long calculateChecksum(byte[] buf) {
int length = buf.length;
int i = 0;
long sum = 0;
long data;
while (length > 1) {
data = (((buf[i] << 8) & 0xFF00) | ((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 & 0xFF00);
if ((sum & 0xFFFF0000) > 0) {
sum = sum & 0xFFFF;
sum += 1;
}
}
sum = ~sum;
sum = sum & 0xFFFF;
return sum;
}
}
然后在JUnit4中进行单元测试
package org.example.checksum;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
public class InternetChecksumTest {
@Test
public void simplestValidValue() {
InternetChecksum testObject = new InternetChecksum();
byte[] buf = new byte[1];
long expected = 0xFFFF;
long actual = testObject.calculateChecksum(buf);
assertEquals(expected, actual);
}
@Test
public void validSingleByteExtreme() {
InternetChecksum testObject = new InternetChecksum();
byte[] buf = new byte[]{(byte) 0xFF};
long expected = 0xFF;
long actual = testObject.calculateChecksum(buf);
assertEquals(expected, actual);
}
@Test
public void validMultiByteExtrema() {
InternetChecksum testObject = new InternetChecksum();
byte[] buf = new byte[]{0x00, (byte) 0xFF};
long expected = 0xFF00;
long actual = testObject.calculateChecksum(buf);
assertEquals(expected, actual);
}
@Test
public void validExampleMessage() {
InternetChecksum testObject = new InternetChecksum();
byte[] buf = {(byte) 0xe3, 0x4f, 0x23, (byte) 0x96, 0x44, 0x27, (byte) 0x99, (byte) 0xf3};
long expected = 0x1aff;
long actual = testObject.calculateChecksum(buf);
assertEquals(expected, actual);
}
@Test
public void validExampleEvenMessageWithCarryFromRFC1071() {
InternetChecksum testObject = new InternetChecksum();
byte[] buf = {(byte) 0x00, 0x01, (byte) 0xf2, (byte) 0x03, (byte) 0xf4, (byte) 0xf5, (byte) 0xf6, (byte) 0xf7};
long expected = 0x220d;
long actual = testObject.calculateChecksum(buf);
assertEquals(expected, actual);
}
}
calculateChecksum
返回 long 值?UDP、TCP 和 IPv4 校验和只需要两个字节。因此,int(最多 4 个字节)应该足够了。 - Maksim Dmitrievbuf[i]
应用0xFF00
,对buf[i+1]
应用0xFF
?例如,两个字节{ 64, 17 }
是成对出现的。它们的二进制值如下:01000000 00010001
。因此,如果我将左移运算符应用于 64 八次,我将得到01000000 0000000
。使用0xFF00
进行 AND 运算后,结果不会改变。对于 17 和0xFF
,同样适用。为什么要使用|
而不是+
? - Maksim Dmitriev