crc32校验的c语言实现

最近在做软件升级,需要对升级文件进行crc校验,就学习了crc的实现原理


crc就是一个数值,该数值用于检验数据的正确性,crc校验的原理就是将需要作校验的数据与一个数据模2相除,得到的余数即为校验值。

      模2相除就是在除的过程中用模2加,模2加实际上就是异或运算,就是不进行进位操作,即相同为假,不相同为真。

下面是几种CRC校验的生成多项式:

CRC8 = X8+X5+X4+1

CRC-CCITT = X16+X12+X5+1

CRC16=X16+X15+X2+1

CRC12=X12+X11+X3+X2+1

CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X+1

一个多项式就是一个比特流,也就是由0、1组合起来的一组数

我们需要获取的常数就是上面多项式所对应的反转多项式(反转多项式:在数据通讯时,信息字节先传送或接受低位字节,如重新排列则影响速度)。

 

下面使用查表法实现crc校验,具体步骤如下:

(1)将上次计算出的CRC校验码右移一个字节;
(2)将移出的这个字节与新的要校验的字节进行XOR 运算;
(3)用运算出的值在预先生成码表中进行索引,获取对应的值(称为余式);
(4)用获取的值与第(1)步右移后的值进行XOR 运算;
(5)如果要校验的数据已经处理完,则第(4)步的结果就是最终的CRC校验码。如果还有数据要进行处理,则再转到第(1)步运行。


本例使用crc32校验,使用上面crc32的生成多项式,值为0xEDB88320


首先写一个功能函数,实现生成一个crc表,然后写一个功能函数,用于计算crc值,最后再写一个函数用于比较crc值,下面是全部代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>

uint32_t crc32_table[256];

int make_crc32_table()
{
	uint32_t c;
	int i = 0;
	int bit = 0;
	
	for(i = 0; i < 256; i++)
	{
		c  = (uint32_t)i;
		
		for(bit = 0; bit < 8; bit++)
		{
			if(c&1)
			{
				c = (c >> 1)^(0xEDB88320);
			}
			else
			{
				c =  c >> 1;
			}
			
		}
		crc32_table[i] = c;
	}
	

}

uint32_t make_crc(uint32_t crc, unsigned char *string, uint32_t size)
{

	while(size--)
		crc = (crc >> 8)^(crc32_table[(crc ^ *string++)&0xff]);

	return crc;
}

void compare_crc(char *filename)
{
	FILE *sp = NULL;
	uint32_t srcCrc ;
	uint32_t calcuCrc = 0xffffffff;
	unsigned char buf[1024];
	uint32_t count;
	
	if(filename == NULL)
	{
		printf("filename is null\n");
		exit(1);
	}

	sp = fopen(filename, "rb");
	if(sp == NULL)
	{
		printf("open file fail\n");
		exit(1);
	}

	fread(&srcCrc, 1, 4, sp);
	printf("In %s: src crc is 0x%x\n", __FUNCTION__, srcCrc);	


        if(sp)
        {
                while(!feof(sp))
                {
                        memset(buf, 0, sizeof(buf));
                        count = fread(buf, 1, sizeof(buf), sp);
                        calcuCrc = make_crc(calcuCrc, buf, count);
                }
        }
	printf("In %s: calcuCrc is 0x%x\n", __FUNCTION__, calcuCrc);

	fclose(sp);

	if(srcCrc == calcuCrc)
	{
		printf("In %s: the calculate crc equal the src crc in file \n", __FUNCTION__);

	}
	else
	{
		printf("In %s: the calculate crc not equal the src crc in file \n", __FUNCTION__);
	}
	
}


int main()
{
	int i;
	FILE *sp = NULL;
	FILE *dp = NULL;
	uint32_t count;
	uint32_t crc = 0xFFFFFFFF;
	unsigned char buf[1024];
	

	make_crc32_table();

	sp = fopen("/home/user/work_soft/crc_check/bak/test.txt", "rb");
	if(sp == NULL)
	{
		printf("open file error\n");
		return -1;
	}
	dp = fopen("/home/user/work_soft/crc_check/bak/testcrc.txt", "wb");
	if(dp == NULL)
	{
		printf("open file error\n");
		return -1;
	}

	if(sp)
	{
		while(!feof(sp))
		{
			memset(buf, 0, sizeof(buf));
			count = fread(buf, 1, sizeof(buf), sp);
			crc = make_crc(crc, buf, count);		
		}
	}

	printf("In main: calculate crc is 0x%x\n", crc);
	if(dp)
	{
		fwrite(&crc, 1, 4, dp);//write the crc into the file testcrc.txt
		fseek(sp, 0, SEEK_SET);
		while(!feof(sp))
		{
			memset(buf, 0, sizeof(buf));
                        count = fread(buf, 1, sizeof(buf), sp);
			fwrite(buf, 1, count, dp);
		}
		
		fclose(sp);
		fclose(dp);
	}

	/*compare crc*/
	compare_crc("/home/user/work_soft/crc_check/bak/testcrc.txt");
	

	return 0;

}

上面代码是根据查表法实现的,首先生成crc表,make_crc32_table就是根据crc校验的原理实现的,对0-255进行模2除法生成crc表;

函数make_crc是根据查表法的步骤实现的;而函数compare_crc则用来打开一个在头部嵌入crc值的文件,首先将文件头部的crc值取出(若用于功能实现,可设计一个数据结构用于存放crc等相关值,并存放在文件的头部),然后再读取文件的余下内容进行

crc计算,将计算出的crc值与从文件中读出的crc值进行比较,若相等则说明文件内容没有出错。下面是运行结果:




如果要将crc嵌入到文件头部,比如将计算得出的crc嵌入到升级文件的头部,由于我们生成的crc值的存储方式可能会因为不同的主机而不同(大端或小端)

所以在将crc值嵌入到升级文件的头部时,最好再加一个字节用于说明crc值得存储方式是大端还是小端模式,这样接收方就可以选择同一个存储方式,来确定读取的crc值是正确的

你可能感兴趣的:(c,function,File,table,语言)