Как вычислить контрольную сумму интернета из байта [] в Java
Я пытаюсь выяснить, как рассчитать контрольную сумму в Интернете на Java, и это не причиняет мне никакой боли. (Я ужасен в битовых манипуляциях.) Я нашел версию в C# Рассчитать контрольную сумму в Интернете (он же IP, он же RFC791) в 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;
}
3 ответа
Отредактировано для применения комментариев @Andy, @EJP, @RD и др. И добавления дополнительных контрольных примеров просто для уверенности.
Я использовал комбинацию ответа @Andys (правильно определяющую местонахождение проблемы) и обновил код, включив в него тестовые модули, представленные в связанном ответе, а также проверенный контрольный итог сообщения дополнительного контрольного примера.
Сначала реализация
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;
}
}
Тогда юнит тест в 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]; // should work for any-length array of zeros
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();
// Berkley example http://www.cs.berkeley.edu/~kfall/EE122/lec06/tsld023.htm
// e3 4f 23 96 44 27 99 f3
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();
// RFC1071 example http://www.ietf.org/rfc/rfc1071.txt
// 00 01 f2 03 f4 f5 f6 f7
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);
}
}
Гораздо более короткая версия выглядит следующим образом:
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;
}
Я думаю, что это продвижение типа вызывает проблемы. Давайте посмотрим на data = (((buf[i]) << 8) | ((buf[i + 1]) & 0xFF))
:
((buf[i]) << 8)
будет способствоватьbuf[i]
вint
, вызывая расширение знака(buf[i + 1]) & 0xFF
также будет способствоватьbuf[i + 1]
вint
, вызывая расширение знака. Но маскируя этот аргумент с0xff
это правильно - мы получаем правильный операнд в этом случае.- Целое выражение повышается до
long
(еще раз, знак включен).
Проблема заключается в первом аргументе - он должен быть замаскирован 0xff00
, как это: data = (((buf[i] << 8) & 0xFF00) | ((buf[i + 1]) & 0xFF))
, Но я подозреваю, что для Java реализованы более эффективные алгоритмы, возможно, даже в стандартной библиотеке есть такой. Вы могли бы взглянуть на MessageDigest
, может быть, он есть.