前段时间我需要一个函数来执行校验和测试数据完整性,同时通过Internet交换数据报。那些天我找到了这个功能,只是复制/粘贴并使用它。
Checksum (void* buf, int bufsize) {
register int sum = 0;
word answer = 0;
register word *w = (word*) buf;
register int nleft = bufsize;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(byte*)(&answer) = *(byte*)w;
sum += answer;
}
sum = (sum>>16) + (sum&0xFFFF);
sum += (sum>>16);
answer = ~sum;
return answer;
}
今天我正在研究另一个需要校验和以确保数据完整性的程序,但这次我想看看它是如何工作的并且有一个疑问。
算法对数据缓冲区的所有字进行求和,然后将进位(高位字,如果有的话)加到和中,最后取消和(一个补码),因此它是校验和值。
我的具体问题是:
它适用于具有校验和支持的简单通信协议,但必须确保它可以独立于其架构而适用于任何平台。
缓冲区中的单词是否应该是小端或大端?
没有特定的协议,你无法知道。协议的endianess有时被称为“网络endianess”。传统上大多数协议使用大端,但不保证这一点。
如果缓冲区大小为奇数,则最后一个字节直接添加到总和中,但它是作为一个值的OR或一个字的低位字节添加(考虑到后面的字节为0 - 小端的高位字节) ?
无论如何,对于错误检测都不会有任何问题 - 对于大多数双位错误,它将失败。我强烈建议使用CRC:标准CRC-16或CRC-32之一,具体取决于数据大小。
- 缓冲区中的单词是否应该是小端或大端?
用于网络连接(使用任何协议通过网络发送或接收的任何数据包的“有效载荷”);缓冲区总是包含八位字节(字节),八位字节总是以它们发送的相同顺序到达(没有“字节顺序”问题)。
在软件使用网络通过网络发送数据之前,它将某些东西(例如,高级语言的结构)转换为八位字节的缓冲区。这称为“序列化”,是避免各种可移植性灾难(不仅仅是“endianess”,而是编译器/实现特定结构填充,浮点格式差异,字符集差异等)所必需的。
在软件从网络接收数据之后,它将八位字节的缓冲区转换成某种形式(例如,高级语言的结构)。这称为“反序列化”。它涉及3个目的:
这些事情(序列化和反序列化)都不是网络本身的一部分 - 它们“在网络之前”和“在网络之后”而不是“在网络中”。
请注意(根据您的问题)您假设缓冲区是高级结构并且无法序列化/反序列化;所以你的代码(包括你的校验和)被打破并且会失败(可能不仅仅是因为“endianess”)。
- 如果缓冲区大小为奇数,则最后一个字节直接添加到总和中,但它是作为一个值的OR或一个字的低位字节添加(考虑到后面的字节为0 - 小端的高位字节) ?
是。
请注意,校验和计算的主要部分(错误地假设“八位字节的缓冲区”是单词)将因“endianess”而中断(如果发送方和接收方具有不同的字节顺序,则校验和检查“将”(请参阅注释)失败所有大于1个八位字节的消息)。避免这种情况的简单方法是使用“字节总和”(而不是“字的总和”)。避免这种情况的更好方法是使用更复杂的校验和(例如TCP使用的CRC-32)。
注意:对于16位校验和,65536中至少有一次机会由于运气而导致校验和意外通过;并且“任何事物的总和”方法容易受到各种故障的影响(例如,如果发送者或接收者得到的消息大小错误,并且最终不存在大量的零,那么“不正确的零”将不会影响校验和)。这意味着当发送方和接收方使用相同的字节顺序时,校验和在不应该通过的情况下会有“差于1 65536”的机会;并且它还意味着当发送方和接收方使用不同的字节顺序并且消息大于1个字节时,校验和将在“应该”时通过的“优于65536”的机会。