首部校验和是什么?它的计算过程是怎样的?

日期: 2024-07-30 04:03:02|浏览: 469|编号: 58953

友情提醒:信息内容由网友发布,本站并不对内容真实性负责,请自鉴内容真实性。

前言:这篇文章的灵感来源于上课时老师提出的问题,正是因为老师的深入提问,我才写了这篇文章。

目录

什么是报头校验和?

报头校验和是网络通信中常用的一种校验方法,用来验证数据包在传输过程中是否出现错误或者损坏,通常应用于网络层协议(如IP协议)的报头中。

报头校验和计算过程

我引用一段话吧:

IP头的校验和没有采用复杂的CRC校验而是采用如下简单的计算方法:

在发送方,IP 数据报头首先被分成一串 16 位字,并将校验和字段设置为 0。使用逆算法将所有 16 位字相加后,将和的逆写入校验和字段。

接收方收到数据报后,用逆运算将报头中的所有16位字再次相加,再取和的倒数,即为接收方校验和的计算结果。

如果报头没有改变,结果必定为 0,因此数据报被保留。否则,则认为是错误。

你明白了吗?是不是很困惑?

让我解释。

0. 步骤

报头校验和的计算过程包括以下步骤:

将校验和字段的值初始化为0作为校验和累加器。将报头中每一段数据按照一定的单位(一般为16位)分组。依次将这些数据段的值累加到校验和累加器中。如果累加的结果超出了数据段所能表示的最大值,则需要进行进位运算。将最终的累加结果取反,即可得到校验和的值。

还是很困惑,对吧?让我进一步解释一下。

1. IP数据报格式

首先,我们需要了解IP数据报的格式。

如图所示,IP数据报由以上部分组成,另外还需要下面12个字段来计算报头校验和。

是的,这就是固定部分(20字节)的所有内容。

它包含了:

了解了这些,我们就可以计算出头部校验和。

2.示例

使用捕获数据包,得到以下数据:

通过抓包工具/IP数据报结构我们可以知道:

(1)版本 = 4

(2)报头长度 = 5

(3)差异化服务 = 0(0x00)

(4)总长度=60()

(5)身份证号=0973()

(6)标志 = 0

(7)切片偏移 = 0

(8)生存期 = 64(0x40)

(9)协议 = 1

(10)报头校验和 =

(11)源地址 = 192.168.1.24(c0 a8 01 18)

(12)目标地址 = 192.168.1.1(c0 a8 01 01)

我这里写了二进制和十六进制,并且使用十六进制进行计算。

头部校验和 = ffff-(4500+003c+03cd+0000+4001+c0a8+0118+c0a8+0101)=

头部校验和跟图片显示的一致。

是不是很简单呢?

不,接下来还有一些事情需要考虑。

3. 数据段不是单位整数倍的情况如何处理

通常情况下,报头中的数据段分为 16 位或 32 位,但在接收数据时,数据段可能不是单位的整数倍。为了处理这种情况,我们可以采取以下做法:

首先,将所有完整的数据段以单位(如16位或32位)进行累加。接下来,对于剩余的不完整的数据段,将它们视为一个字节的数据。该字节的数据被累加到校验和累加器中。4. 处理进位情况

在计算校验和的过程中,可能会发生进位运算,如果累计和的结果超出了对应数据段能够表示的最大值,就需要进行进位运算。

处理携带的方式是:

将累加和右移一位,并将进位位添加到累加和中。对累加和与进位位进行按位与运算,将进位位移到低位,继续累加。如果还有进位位,则重复上述步骤,直到没有进位位。代码实现 ed1

USHORT checksum(USHORT* buffer, int size)
{
    unsigned long cksum = 0;
    while(size>1)
    {
        cksum += *buffer++;/*
        这里实质上就是
        checkSum+=*pBuf;
        *pBuf++;
        */
        size -= sizeof(USHORT);
    }
    if(size)
    {
        cksum += *(UCHAR*)buffer;
    }
    /*对于接下来两行,可以写个通用的程序,这样如果产生了多于两次(三次及以上)进位时也能用,但是一般来说,进位不会超过2次
    while(ckSum>>16){
        cksum = (cksum>>16) + (cksum&0xffff); 
    }
    */
    cksum = (cksum>>16) + (cksum&0xffff); //这段代码是进行16位校验和的溢出处理
    cksum += (cksum>>16); 
    return (USHORT)(~cksum);
}

ed2

修改上述代码的想法来自于老师提出的一个问题:如果将指针类型改为UCHAR会怎么样?

USHORT checksum(UCHAR* buffer, int size)
{
    unsigned long cksum = 0;
    while(size > 1)
    {
        cksum += *(USHORT*)buffer;
        buffer += sizeof(USHORT);
        size -= sizeof(USHORT);
    }
    if(size)
    {
        cksum += *(UCHAR*)buffer;
    }
    while (cksum >> 16)
    {
        cksum = (cksum & 0xffff) + (cksum >> 16);
    }
    return (USHORT)(~cksum);
}

代码解释 1. if(size) cksum += *(UCHAR*);

这段代码的目的是处理IP头大小不是2的整数倍,且有剩余字节的情况。

if(size)表示如果剩余字节数不为0(即size不为0),则执行相应代码。

*(UCHAR*)的意思是将指针指向的地址强制转换成UCHAR类型的指针,也就是将指针指向的地址当做一个字节的数据。

+= *(UCHAR*)表示将转换后的UCHAR类型字节数据添加到校验和中。

为什么不用*(UCHAR*)呢?因为它是一个用来累加校验和的变量,并不代表具体的数据地址。其实存放的是校验和累加的结果。

这段代码的目的是为了处理IP头剩余少于2个字节的情况,因此需要把指向的具体地址(代表剩余的字节)作为一个字节的数据添加到校验和中。

总结一下,代码*(UCHAR*)的作用就是将指向的地址当做一个字节的数据,加入到校验和中进行处理。

为什么要强制进行类型转换呢?因为如果不做类型转换的话,我们就会多加一些不该加的东西,就会多加一个字节的内容。

2. cksum = (cksum>>16) + (cksum&);

是无符号长整数,可能会有大于16位的计算,需要处理高位溢出,此时需要右移16位,分别计算高16位和低16位,将结果相加。其中>>表示右移操作,也就是会向右移16位。

(&)用于保留低16位,&运算符为按位与运算符(&&为逻辑与),将中的低16位按位与运算,得到低16位。

一旦完成高16位和低16位的加法,如果出现进位,则需要将进位的位数加回到中间,以保证校验的正确性。这里可以用while循环来写。(如代码所示)

因此,本段代码的目的在于保证IP头校验和的正确性,处理16位校验和的溢出,使用了按位运算符>>和&,即右移和按位与。

扩展 CRC

CRC(Check)循环冗余校验是一种数据传输错误检测技术,它通过在数据帧中增加校验位来检测数据传输过程中的错误。CRC校验算法是一种基于二进制多项式除法的校验方法,可以检测出多种数据传输错误,如单比特错误、双比特错误、突发错误等,广泛应用于计算机网络、通讯、存储等领域。

它是一款流行的网络数据包分析工具,可以截取各种网络数据包并显示数据包的详细信息。可用于网络故障排除、网络安全分析、网络协议开发等。它提供了丰富的过滤功能,可以根据协议、端口、主机名、数据包内容等各种条件进行过滤,以便用户快速定位需要分析的数据包。同时,它还提供多种统计和图形分析工具,帮助用户更好地了解网络数据流量和协议行为。如果需要进行网络数据包分析,它是一个很不错的选择。

提醒:请联系我时一定说明是从101箱包皮具网上看到的!