校验和计算:
1、校验数据以16bit为单位进行累加求和,校验数据需为偶数字节,奇数字节末尾填充0变为偶数字节。
2、如果累加和超过16bit,产生了进位,需将高16bit和低16bit累加求和。
3、循环步骤2,直至未产生进位为止。
4、累加和取反得到校验和。
校验和验证
1、校验数据16bit为单位进行累加求和,校验数据需为偶数字节,奇数字节末尾填充0变为偶数字节。
2、如果累加和超过16bit,产生了进位,需将高16bit和低16bit累加求和。
3、循环步骤2,直至未产生进位为止。
4、累加和和校验和相加得到0xffff,校验成功,否则失败。
一个简单的例子
对于上图的例子,发送数据7、11、12、0、6,五位数据。
发送时的校验和计算(左边)
1)上面是最简单的例子,7、11、12、0、6为奇数位,所以末尾补充0。求和得到了36=100100
2)100100 超过了16,需要把高位10和低位0100进行累加,即得到了0110
3)已无进位,无需循环,执行4
4)0110取反为1001 十进制为9,即发送的数据为7、11
12、0、6、9
接收时的校验和验证(右边)
1)对接收到的数据7、11
12、0、6、9,为偶数位无需补0,直接求和为45=101101
2)101101 超过了16,需要把高位10和低位1101 进行累加,即得到了1111
3)已无进位,无需循环,执行4
4)1111取反为0000 为0,即校验成功,数据为正确数据
另一个例子
如计算下面一段数据的checksum,数据为16进制;
45 00 00 3c 00 00 00 00 40 11 c0 a8 2b c3 08 08 08 08 11
红色的6d 36为checksum字段,先把checksum设0,数据分组,补0,整理完后数据如下,中间checksum设置为0,最后补1byte 0;
4500 003c 0000 0000 4011 c0a8 2bc3 0808 0808 1100
计算:4500+003c+0000+0000+4011++c0a8+2bc3+0808+0808+1100 = 192C8
高低16bit相加: 1 + 92C8 = 92C9
取反: ~92C9 = 6D36
最后所得数据为:45 00 00 3c 00 00 00 00 40 11 c0 a8 2b c3 08 08 08 08 11
对应代码如下:
static u16_t chksum(void *dataptr, u16_t len)//只演示checksum累加的过程
{
u32_t acc;
u16_t src;
u8_t *octetptr;
acc = 0;
octetptr = (u8_t*)dataptr;
while (len > 1) {
src = (*octetptr) << 8;
octetptr++;
src |= (*octetptr); //补足两个字节长度
octetptr++;
acc += src;
len -= 2;
}
if (len > 0) {
src = (*octetptr) << 8;
acc += src;
}
acc = (acc >> 16) + (acc & 0x0000ffffUL);
if ((acc & 0xffff0000UL) != 0) {
acc = (acc >> 16) + (acc & 0x0000ffffUL);
}
src = (u16_t)acc;
return ~src;//取反
}
UDP校验和原理
UDP校验数据范围
范围:UDP伪首部(12字节) + UDP首部(8字节) + UDP负载数据
详情参考网络编程(六)UDP详解
接收方和发送方都遵守该约定。
发送方UDP校验和计算
1、填充伪首部。
2、UDP首部校验和清零。
3、校验数据按照校验和原理计算出校验和。
4、填充校验和至UDP首部校验和字段。
接收方UDP校验和验证
1、接收方接收UDP数据报文(含UDP头部)。
2、填充伪首部。
3、校验数据按照校验和原理校验,满足累加和为0xffff,校验成功。
思考:为什么发送方校验数据(UDP首部校验和)为0,而接收方校验数据(UDP首部校验和)已填充校验和,双方校验数据不一样,不会出错吗?
其实接收方是把计算累加和和校验和验证两个步骤合成了一个步骤,可以理解为接收方先计算校验数据(UDP首部校验和为0)的累加和,再把累加和UDP校验和相加。
如上图,发送时UDP校验和计算
1)填充伪首部,1byte为8bit,即2byte为16bit,按照规则,伪首部12byte每行为4byte,即每行继续拆分为两部分,拆分为153.19,即153和19表示为16进制为10011001 00010011 。以此类推,可以推导出上图所示数据
2)校验和为0无需清除
3)求和为10010110 11101101 刚好是16bit,无需循环,直接求反,即校验和为01101001 00010010