// 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 . ", ip.data); // 校验和计算 ip.s[3] = checksum(ip.data, CHECK_SIZE); // 接收方进行校验和计算 ck = checksum(ip.data, CHECK_SIZE); printf("last check sum is %hu . ", ~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; }