【Linux】linux下zlib压缩与crc32校验

最近项目里为了减少传输数据量,需要进行数据的压缩和解压。网上搜索和之前项目中看到是使用到了zlib一个比较小巧的库,具体的地址请查看官网。如果仅仅是数据的解压缩,那直接跟从官网的教程就好。该地址简单的给出了压缩和解压的实现。但是想做我们的项目是有一些历史的原因还引用了crc32的校验,所以在解压和压缩之前都需要对数据进行crc生成和校验。经过了的一段时间摸索,最终流程也终于走通了。

crc32

crc32介绍

crc32校验

对于crc校验,一般有两种方式。

  • 单独对crc校验码进行校验。在这种方式中,原始数据和crc校验码是分开存储,分开进行校验的;
  • 对原始数据crc校验码一起进行校验,进行校验的时候是不知道哪一部分是数据,哪一部分是crc校验码的。采用这种方法比较方便在使用起来,下面代码示例也是采用的这一种方式。

zlib

压缩算法介绍

常用的压缩算法基本上是两种,这个在zlib库里也有介绍。
- huffman编码,它主要是采用变长的字符进行压缩编码。出现的概率越高,其编码越短,最终编码长度相应的就越短。
- lz777,这个大家自行搜索吧,主要是通过一个距离标识来说明下一个字符的位置。

zlib与crc32使用

该测试代码主要是包括压缩、解压两个函数。在测试代码中对压缩的数据生成crc校验码,并把校验码追加到压缩数据的末尾,成为最终的压缩的数据。解压函数就对最终的压缩数据(原始压缩数据与crc校验码)进行解压操作。

解压函数

int DeCompress(const unsigned char * src, long src_len, unsigned char * dest, long * dest_len)
{
    z_stream stream;
    int err;
    if (crc32(0, src, src_len) != 0xFFFFFFFF)
    {   
        printf("crc error: %d\n", Z_BUF_ERROR);
        return Z_BUF_ERROR;
    }   

    stream.next_in = (Bytef*)src;
    stream.avail_in = (uInt)src_len;
    /* Check for src > 64K on 16-bit machine: */
    if ((uLong)stream.avail_in != src_len) return Z_BUF_ERROR;

    stream.next_out = dest;
    stream.avail_out = (uInt)*dest_len;
    if ((uLong)stream.avail_out != *dest_len) return Z_BUF_ERROR;

    stream.zalloc = (alloc_func)0;
    stream.zfree = (free_func)0;

    err = inflateInit2(&stream, -MAX_WBITS); 
    //err = inflateInit(&stream);
    if (err != Z_OK) return err;

    err = inflate(&stream, Z_FINISH);
    if (err != Z_STREAM_END) 
    {   
        inflateEnd(&stream);
        if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) 
            return Z_DATA_ERROR;
        return err;
    }   
    *dest_len = stream.total_out;

    err = inflateEnd(&stream);
    return err;
}

压缩函数

int Compress(const unsigned char * src, long src_len, unsigned char * dest, long * dest_len)
{
    z_stream stream;
    int err = 0;

    stream.zalloc = (alloc_func)0;
    stream.zfree = (free_func)0;
    // setup "a" as the input and "b" as the compressed output
    stream.avail_in = (uInt)src_len; // size of input, string + terminator
    stream.next_in = (Bytef *) src; // input char array
    stream.avail_out = (uInt) *dest_len; // size of output
    stream.next_out = (Bytef *)dest; // output char array


    deflateInit2(&stream, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY );
    deflate(&stream, Z_FINISH); // the actual compression work.
    deflateEnd(&stream);

    // This is one way of getting the size of the output
    *dest_len = stream.total_out;
    return err;
}

测试代码

int main()
{
    unsigned char a[] = "123123123123123123123";
    unsigned char b[124];

    size_t size = sizeof(b);
    Compress(a, sizeof(a), b, (long int  *)(&size));
    printf("a: %s, compress size: %lu\n", a, size);


    uLong crc = crc32(0L, Z_NULL, 0); 
    printf("crc: %lu, char: %u\n", crc, b[size + 1]);
    crc = ~crc32(crc, b, size);
    b[8] = (crc & 0xFF);
    b[9] = ((crc >> 8) & 0xFF);
    b[10] = ((crc >> 16) & 0xFF);
    b[11] = ((crc >> 24) & 0xFF);
    b[12] = 0;
    size = size + 4;

    unsigned char c[124];
    long int s = 124;

    DeCompress(b, size, c, (long int  *)(&s));
    printf("a: %s, c: %s\n", a, c); 
    return 1;
}

稍微提一下,在这种当中遇到的问题:
1、crc检查失败,直接返回失败了。
这个当时脑残的是对解压之前的数据做了crc校验码,然后追加到数据末尾。但是解压的时候是对压缩后的数据做crc校验,所以肯定有问题。
然后理清逻辑之后对压缩后的数据生成crc校验码,然后追加到压缩到数据的末尾。但是还是有问题,看到之前项目代码发现,他们对crc校验码做了取反,不知为何。后来加上取反,crc校验通过了。

2、解压的时候,数据一直无法正常解压出来。
这个地方是因为开始我是参考了官网上给定教程,压缩的时候是使用了deflateInit方法,调试了很久也没调试出来。后来发现,解压的时候方法是inflateInit2,那压缩的时候是不是也有deflateInit与deflateInit2方法的区别,查了文档发现果然是有deflateInit2的方法的。查看deflateInit2方法使用,调试,修改,程序通过,正确输出压缩之前的方法。
所以这里需要注意的是对于压缩和解压有两套方法调用的,分别是inflateInit与deflateInit、inflateInit2与deflateInit2,每一套参数传递的参数不同,最后结果也不同。使用其中一个压缩方法压缩数据,那就必须使用另外与其一样的解压方法进行解压数据,否则数据就会错误。

你可能感兴趣的:(linux,压缩,zlib,CRC32)