// IP首部校验和的计算
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned short checksum(const void *, unsigned int);
#define CHECK_SIZE 24
/*
知识补充:
为了计算一份数据报的IP检验和,首先把检验和字段置为0。然后,对首部中每个16 bit进行二进制反码求和(整个首部看成是由一串16 bit的字组成),
结果存在检验和字段中。当收到一份IP数据报后,同样对首部中每个16 bit进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和,
因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全1。如果结果不是全1(即检验和错误),那么IP就丢弃收到的数据报。
但是不生成差错报文,由上层去发现丢失的数据报并进行重传。
*/
union DataIp
{
char data[CHECK_SIZE];
unsigned short s[CHECK_SIZE/2];
};
void test()
{
unsigned short ck = 0;
union DataIp ip;
strcpy(ip.data, "123456789abcdef");
/*
设计说明:
假定s[3]为校验位
*/
ip.s[3] = 0; // 首先把检验和字段置为0
// 发送方
printf("send data is %s .\n", ip.data);
// 校验和计算
ip.s[3] = checksum(ip.data, CHECK_SIZE);
// 接收方进行校验和计算
ck = checksum(ip.data, CHECK_SIZE);
printf("last check sum is %hu .\n", ~ck);
}
unsigned short checksum(const void *data, unsigned int size)
{
const unsigned char space = sizeof(unsigned short) * 8; // 单位校验长度(单位bit)
unsigned short buffer; // 缓冲区
const unsigned short *p = (const unsigned short *)data; // 偏移指针
unsigned int total = 0;
// 1.参数校验
if (NULL == data || 0 == size)
{
return total;
}
// 2,逐位进行运算
while (size > 1)
{
buffer = *p++;
/*
设计说明:
为什么不是 total += ~buffer;
因为 buffer 的类型是 unsigned short ,当进行+=运算时,编译器会将 buffer 转成unsigned int类型,
此时 ~buffer操作会导致高位全是1,得出的total结果错误
*/
total += (unsigned int)(~buffer)&0xffff;
size -= sizeof(unsigned short);
}
// 3.剩余位填充
if (size)
{
buffer = 0;
memcpy(&buffer, p, size);
total += (unsigned int)(~buffer)&0xffff;
}
// 4.进位处理
/*
知识补充:
校验位是16bit,但是total却是32bit,需要将产生的进位加到低位
*/
while (total >> space)
{
//循环操作,直到没有进位为止
total = (total >> space) + (total & 0xffff);
}
return (unsigned short)total;
}
int main(int argc, char *argv[])
{
test();
getchar();
return 0;
}