1.什么是gzip

gzip编码,最早由Jean-loup Gailly和Mark Adler创建,用于UNIX系统的文件压缩。我们在Linux中经常会用到后缀为.gz的文件,它们就是GZIP格式的。现今已经成为Internet 上使用非常普遍的一种数据压缩格式,或者说一种文件格式。HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。(摘在百度百科)。

2.zlib用法简单说明----VC6

1. 如何获得zlib

  zlib的主页是:http://www.zlib.net/  (自己上传的地址 http://download.csdn.net/detail/whatday/4305244 zlib-1.2.5.tar.zip)

  2. 用VC++6.0打开

  把下载的源代码解压打开,VC6.0的工程已经建好了,在projectsvisualc6. 双击zlib.dsw, 可以在VC++6.0中看到里面有3个工程: zlib 是库文件(编译设置选中 win32 lib debug / release), 工程example 是如何使用 zlib.lib 的示例, 工程minigzip 是如何用 zlib 提供的函数读写.gz文件的示例(*.gz的文件一般Linux下比较常用).

  3. 如何加入到我的工程

  编译好 zlib.lib 后, 你就得到了调用一个静态库所需要的所有文件了(zlib.lib, zlib.h, zconf.h). 如何调用静态库不用我说了吧.

  4. 用zlib能干什么

  先来看看 zlib 都提供了那些函数, 都在zlib.h中,看到一堆宏不要晕,其实都是为了兼容各种编译器和一些类型定义.死死抓住那些主要的函数的原型声明就不会受到这些东西的影响了.

  关键的函数有那么几个:

      (1)int compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);

compress函数将 source 缓冲区中的内容压缩到 dest 缓冲区。 sourceLen 表示source 缓冲区的大小(以字节计)。注意函数的第二个参数 destLen 是传址调用。当调用函数时,destLen表示 dest 缓冲区的大小,destLen > (sourceLen + 12)*100.1%。当函数退出后,destLen 表示压缩后缓冲区的实际大小。此时 destLen / sourceLen 正好是压缩率。

compress 若成功,则返回 Z_OK;若没有足够内存,则返回 Z_MEM_ERROR;若输出缓冲区不够大,则返回 Z_BUF_ERROR。

      (2)int uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);

uncompress 函数将 source 缓冲区的内容解压缩到 dest 缓冲区。sourceLen 是 source 缓冲区的大小(以字节计)。注意函数的第二个参数 destLen 是传址调用。当调用函数时,destLen 表示 dest 缓冲区的大小, dest 缓冲区要足以容下解压后的数据。在进行解压缩时,需要提前知道被压缩的数据解压出来会有多大。这就要求在进行压缩之前,保存原始数据的大小(也就是解压后的数据的大小)。这不是 zlib 函数库的功能,需要我们做额外的工作。当函数退出后, destLen 是解压出来的数据的实际大小。

uncompress 若成功,则返回 Z_OK ;若没有足够内存,则返回 Z_MEM_ERROR;若输出缓冲区不够大,则返回 Z_BUF_ERROR。若输入数据有误,则返回 Z_DATA_ERROR。

  (3) uLong compressBound (uLong sourceLen);

  计算需要的缓冲区长度. 假设你在压缩之前就想知道你的产度为 sourcelen 的数据压缩后有多大, 可调用这个函数计算一下,这个函数并不能得到精确的结果,但是它可以保证实际输出长度肯定小于它计算出来的长度

  (4) int uncompress (Bytef *dest,  uLongf *destLen,const Bytef *source, uLong sourceLen);

  解压缩(看名字就知道了:)

  (5) deflateInit() + deflate() + deflateEnd()

  3个函数结合使用完成压缩功能,具体用法看 example.c 的 test_deflate()函数. 其实 compress() 函数内部就是用这3个函数实现的(工程 zlib 的 compress.c 文件)

  (6) inflateInit() + inflate() + inflateEnd()

  和(5)类似,完成解压缩功能.

  (7) gz开头的函数. 用来操作*.gz的文件,和文件stdio调用方式类似. 想知道怎么用的话看example.c 的 test_gzio() 函数,很easy.

  (8) 其他诸如获得版本等函数就不说了.

  总结: 其实只要有了compress() 和uncompress() 两个函数,在大多数应用中就足够了.

3.如何解决http封包中gzip编码的html
      大家都知道,在默认模式下,http协议中发送的网页的Html代码是经过gzip编码后传送的。那么我们怎么才能回复这段编码后的html呢?
      大家可能都知道linux下有一个gzip命令。可以把文件压缩成gzip编码的格式,即*.gz
      而对于文件的压缩和解压缩,可以采用zlib库中提供的各种接口来进行操作。但是这里问题来了。关于gzip编码的那些函数都带有gz开头的标示。而这些接口维护了一个名叫gz_stream的结构体。并且是针对文件FILE*操作的。

      而我们要解决的问题是,我们把截取的封包中的gzip编码的内容拿出来,放到一个buffer中。那么怎么才能针对这个buffer中的数据应用这些接口呢?

      我本来的想法是能不能在这些接口中找到专门对内存中的数据进行解码?

      考虑这个问题,我看了这个库的源代码。后来放弃了。至于放弃的原因,可能是我意志力不够,或者不想看那些源代码了。总之,我看了一天,看的我头疼。

      后来我就想个办法绕开这一思路,走个弯路。问题豁然开朗。

      思路如下:

      1. 把获取到的经过编码的数据保存到一个文件中。注意写文件的时候一定要以二进制方式。否则是不能解码的。

      2. 应用zlib中的接口,gzopen(),gzread(),gzclose()即可完成解码的任务了。


      我把这一过程简单封装起来,在VC2005下经过测试,仅提供参考:

// ~GzipParse.h  
#ifndef __GZIPPARSE_H_  
#define __GZIPPARSE_H_  
/* 
 * 此类用于解码http协议中gzip编码的html数据数据 
 * 也可用于其他类似用途 
*/ 
class GzipParse  
{  
public:  
    //sCompressedData压缩数据的buffer,  
    //nLen sCompressedData的长度  
    GzipParse(char *sCompressedData,int nLen);  
    ~GzipParse();  
public:  
    //调用时,请确保unCompressedData的长度足够长,能够容纳解码后的数据  
    //sUnCompressedData解码数据的buffer  
    //nLen sUnCompressedData的长度  
    char *Decode(char *sUnCompressedData,int nLen);  
private:  
    char *m_sCompressedData;  
    int m_nLen;  
};  
#endif // __GZIPPARSE_H_  
// ~GzipParse.cpp  
#include "GzipParse.h"  
#include "zlib.h"  
#include <fstream>  
#ifdef WIN32  
#pragma comment(lib,"zlib.lib")  
#endif  
using std::ofstream;  
using std::ios_base;  
     
GzipParse::GzipParse(char *sCompressedData,int nLen)  
{  
    this->m_sCompressedData = sCompressedData;  
    this->m_nLen = nLen;  
}  
GzipParse::~GzipParse()  
{  
}  
char * GzipParse::Decode(char *out,int nOutLen)  
{  
    ofstream fout;  
    fout.open("html.tmp",ios_base::out | ios_base::trunc | ios_base::binary );  
    fout.write(m_sCompressedData,m_nLen);  
    fout.close();  
     
    gzFile file = gzopen("html.tmp","rb");  
    int nLength = gzread(file,out,nOutLen);  
    gzclose(file);  
    out[nLength] = '\0';  
     
    return out;  
}  
//~ testGzipParse.cpp  
#include "GzipParse.h"  
#include <iostream>  
using std::cout;  
using std::cin;  
using std::endl;  
int main()  
{  
    char * sUnCompress = new char[100];  
    char sCompress[] = {0x1f,0x8b,0x08,0x08,0x8c,0x9f,0x5E,0x4A,0x00,0x03,0x67,  
        0x7a,0x69,0x70,0x2e,0x74,0x78,0x74,0x00,0x33,0x34,0x32,  
        0x36,0x32,0x31,0x1c,0x25,0x46,0x09,0x9a,0x12,0x5c,0x00,  
        0xb4,0xe9,0x50,0x7F,0x68,0x02,0x00,0x00};  
     
    GzipParse gzip(sCompress,sizeof sCompress);  
    gzip.Decode(sUnCompress);  
    cout << sUnCompress << endl;  
    cin.get();  
    delete []sUnCompress;  
    return 0;  
}