gzip 与 deflate :gzip算法原理深入分析

gzip 与 deflate :gzip算法原理深入分析 -

gzip 与 deflate :gzip算法原理深入分析

[复制链接]

   
verohan
verohan 当前离线
Q豆
22
Q币
0
在线时间
1 小时
积分
35
精华
1
阅读权限
150
注册时间
2011-4-22
最后登录
2011-6-16
帖子
7
窥视卡 雷达卡
 

4

主题

1

听众

35

积分

官方团队

Q豆
22
Q币
0
注册时间
2011-4-22
分享
0
积分
35
UID
198
  • 收听TA
  • 发消息
电梯直达

楼主

发表于 2011-4-22 17:05:15 |只看该作者 |倒序浏览

本帖最后由 verohan 于 2011-4-22 17:05 编辑

deflate与gzip解压的代码几乎相同,应该可以合成一块代码。
区别仅有:

  • deflate使用inflateInit(),而gzip使用inflateInit2()进行初始化,比 inflateInit()多一个参数: -MAX_WBITS,表示处理raw deflate数据。因为gzip数据中的zlib压缩数据块没有zlib header的两个字节。使用inflateInit2时要求zlib库忽略zlib header。在zlib手册中要求windowBits为8..15,但是实际上其它范围的数据有特殊作用,见zlib.h中的注释,如负数表示raw deflate。
  • Apache的deflate变种可能也没有zlib header,需要添加假头后处理。即MS的错误deflate (raw deflate).zlib头第1字节一般是0×78, 第2字节与第一字节合起来的双字节应能被31整除,详见rfc1950。例如Firefox的zlib假头为0×7801,python zlib.compress()结果头部为0x789c。

再去检查 zlib.h 中的注释说明,在 zlib-1.2.3/zlib.h Line 500 的地方发现这样一段话:

The windowBits parameter is the base two logarithm of the window size
(the size of the history buffer). It should be in the range 8..15 for this
version of the library. Larger values of this parameter result in better
compression at the expense of memory usage. The default value is 15 if
deflateInit is used instead.
windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
determines the window size. deflate() will then generate raw deflate data
with no zlib header or trailer, and will not compute an adler32 check value.
windowBits can also be greater than 15 for optional gzip encoding. Add
16 to windowBits to write a simple gzip header and trailer around the
compressed data instead of a zlib wrapper. The gzip header will have no
file name, no extra data, no comment, no modification time (set to zero),
no header crc, and the operating system will be set to 255 (unknown).  If a
gzip stream is being written, strm->adler is a crc32 instead of an adler32.

回过头来看 nginx 和 apache 的实现:
nginx-0.6.34/src/http/modules/ngx_http_gzip_filter_module.c Line 335:

rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
-wbits, memlevel, Z_DEFAULT_STRATEGY);

httpd-2.0.63/modules/filters/mod_deflate.c Line 374:

zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
c->windowSize, c->memlevel,
Z_DEFAULT_STRATEGY);
(Line 153: c->windowSize = i * -1; )

也就是说,nginx 和 apache 在程序里处理的都是 raw deflate data ,windowBits 都是负数,那为什么 Content-Encoding 都写的是 gzip 而不是 deflate 呢?
在 apache 的 mod_deflate.c 里,首先发现了这样一个写入 gzip header 的动作:

/* RFC 1952 Section 2.3 dictates the gzip header:
*
* +—+—+—+—+—+—+—+—+—+—+
* |ID1|ID2|CM |FLG|     MTIME     |XFL|OS |
* +—+—+—+—+—+—+—+—+—+—+
*
* If we wish to populate in MTIME (as hinted in RFC 1952), do:
* putLong(date_array, apr_time_now() / APR_USEC_PER_SEC);
* where date_array is a char[4] and then print date_array in the
* MTIME position.  WARNING: ENDIANNESS ISSUE HERE.
*/
buf = apr_psprintf(r->pool, “%c%c%c%c%c%c%c%c%c%c”, deflate_magic[0],
deflate_magic[1], Z_DEFLATED, 0 /* flags */,
0, 0, 0, 0 /* 4 chars for mtime */,
0 /* xflags */, OS_CODE);

deflate_magic 是这样定义的:

/* magic header */
static char deflate_magic[2] = { ‘\037′, ‘\213′ };

而 OS_CODE 是在 zutil.h 里定义的,AMIGA 是 1,VAXC 是 2,OS2 是 6,WIN32 是 11,默认 unix 是 3(从这个顺序也可以看出操作系统的发展历史了)
数一下,10个字节,再联想到老大说的 18 个字节,仔细找找,终于在 Line 462 里发现这样一个附加 tail 的动作:

buf = apr_palloc(r->pool, 8);
putLong((unsigned char *)&buf[0], ctx->crc);
putLong((unsigned char *)&buf[4], ctx->stream.total_in);
b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(ctx->bb, b);

不多不少,8个字节。10个字节的头加上 8 个字节的尾巴,就是老大说的多出来的 18 个字节。apache 调用 zlib 的接口产生了 raw defalte 的数据,然后手工的添加了 gzip 头和尾。
同样的,在 nginx 的 ngx_http_gzip_filter_module.c 首先在 Line 179看到 Igor Sysoev 同学很不负责任的定义了这样一个 gzip header:

static u_char  gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };

仔细看最后一位!居然直接写了一个 3 !这会不会导致 windows 上编译的 nginx 在输出 gzip 压缩过的页面的时候,客户端解压不正常?回头有空再去看看 zlib 里关于解压的算法代码中,对于这个 OS_CODE 是怎么处理的吧。
继续寻找,在 Line 351 的地方,作者还写了一段注释(虽然我越看越不明白他在试图表达什么意思):

b->memory = 1;
b->pos = gzheader;
b->last = b->pos + 10;
out.buf = b;
out.next = NULL;
/*
* We pass the gzheader to the next filter now to avoid its linking
* to the ctx->busy chain.  zlib does not usually output the compressed
* data in the initial iterations, so the gzheader that was linked
* to the ctx->busy chain would be flushed by ngx_http_write_filter().
*/

大致是说把 gzheader 传给下一个 filter 去处理,这个 filter 只出来 raw deflate 数据,以及附加的 tail 吧。在 Line 605 的地方:

#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
trailer->crc32 = ctx->crc32;
trailer->zlen = ctx->zin;
#else
trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
trailer->crc32[1] = (u_char) ((ctx->crc32 >>  & 0xff);
trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
trailer->zlen[1] = (u_char) ((ctx->zin >>  & 0xff);
trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
#endif

幸亏有 IBM MOTOROLA  们造了 Big Endian 机器,这样一来,这段代码的意思再明白不过了。
连上网搜资料加读代码,一共花了大约3个小时,到现在,大约清楚了这么几个问题:

  • deflate 是最基础的算法,在 zlib 里面有实现
  • gzip 在 deflate 的 raw data 前增加了 10 个字节的 gzheader,尾部添加了 8 个字节的校验字节(可选 crc32 和 adler32) 和长度标识字节,gzip 的 magic number 是 0x1f, 0x8b
  • zlib 自己也有 header 和尾部校验的数据,如果使用 deflateInit 而不是 deflateInit2,或者 windowBits 设置为正数8~15的话
  • zlib windowBits 设置为 16 第4位设置为1(即在原来值的基础上加16,感谢 antonio 同学的更正) 的时候,zlib 自己会产生一个 gzip 的头和尾,这种情况下 OS_CODE 被设置为 255(unknown),尾部校验使用 crc32 。问题是,既然 zlib 本身就提供了这种功能,为什么 apache 和 nginx 不用,反而都选择手工添加呢?
  • 为 nginx 添加 deflate 支持,只需要把输出中的头,尾去掉,并把 Content-Encoding 改为 deflate 即可。18 个字节,就这样省下来了。
  • 继续为 nginx 增加 deflate 压缩支持。公司居然用的是 nginx 0.7.33 的最新开发版本,虽然不时的有 502 bad gateway 出现,但老高不以为然。打开0.7.33 代码一看,比 0.6 版本的整洁了许多,gzip 添加头,尾的动作都被封装到了单独的函数中了,再也不是一个大函数从头写到尾了,有进步。
    起初是想为 deflate 压缩单独写一个与 gzip 平行的模块,拿原先 gzip 模块的 c 文件(src/http/modules/ngx_http_gzip_filter_module.c)一通 “搜索”-“替换”,编译通过了,但新模块死活没有被调用。想想也是,http 请求头的处理不在这个 c 文件里面,只改这个文件,当然不会有效果了。
    接下来就把原来的 gzip c 文件一通猛改,添加头的函数直接 return,添加尾的函数也去掉具体添加的动作,最后再把 Content-Encoding 改过来,一测试,呵呵,还真的省下来 18 个字节!
    但这样以来 gzip 就不支持了。更严重的是,如果(虽然可能性比较小),一个客户端只支持 gzip,不支持 deflate,那么它就无法解析请求的结果。在查看 src/http/ngx_http_core_module.c 里的 ngx_http_gzip_ok 函数的时候,终于发现了对于客户端提交的 header 里面的 accept encoding 的判断处理:

    ngx_strcasestrn(r->headers_in.accept_encoding->value.data,
    “gzip”, 4 – 1) == NULL

    而 ngx_http_request_t *r 几乎在每一个函数里都是可用的。为了尽量减少改动的文件数量和代码处,也是为了尽量保险起见,我选择了在 src/http/modules/ngx_http_gzip_filter_module.c 文件里每一个改动的地方都做一次这样的判断:客户端是否支持 deflate,如果支持,则按 deflate 的做法进行修改,如果不支持,则保持原来 gzip 的样子。
    编译,测试,ubuntu 8.10 + firefox 3.0.4 ,httpfox 下测试通过。
    patch: http://code.google.com/p/fulin/s ... .7.33.deflate.patch
    效果:
    浏览器 Accept-Encoding:
    gzip:使用 gzip
    deflate:使用 deflate
    gzip,deflate:使用 deflate
    无:不压缩
    nginx 版本:nginx-0.7.33
    patch 使用方法:将 patch 文件放置于与 nginx-0.7.33 目录同级的地方,使用命令:
    patch -p0 < nginx.deflate.patch
    然后按正常流程 configure,make,make install

posted on 2013-03-31 13:41 lexus 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lexus/archive/2013/03/31/2991683.html

gzip 与 deflate :gzip算法原理深入分析相关推荐

  1. GBDT算法原理深入分析

    GBDT算法原理深入分析1 https://www.zybuluo.com/yxd/note/611571 GBDT算法原理深入分析2 https://www.wandouip.com/t5i1874 ...

  2. Web服务器处理HTTP压缩之gzip、deflate压缩

    一.什么是gzip ​ gzip是一种数据格式,默认且目前仅使用deflate算法压缩data部分: ​ Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Linux平台.当应用Gzip压 ...

  3. gzip和deflate的区别

    我们在配置网站GZip压缩的时候,会发现有两个模块可以设置的,一个是GZip模块的参数配置,另一个是Deflate模块的参数配置,他们的设置方法是一样的.刚开始时我不太明白,这两地方有什么不同?网站开 ...

  4. Spring MVC参数处理--gzip和deflate的几点区别

    今天在获取HTTP报文头的Accept-Encoding时,在控制台蹦出个gzip和deflate,有些陌生,只是知道这是两种压缩算法.那么它们到底有什么不同呢?这里转载一位技术人员的文章,做一下详解 ...

  5. gzip and deflate

    Gzip/deflate 解码   一般的 http 网页在传输时为了减少流量加快传输速度,都会对数据进行压缩,压缩方式一般有 gzip 和 deflate 两种方式. gzip 的解压方式如下: i ...

  6. httpClient的get请求 请求头gzip和deflate的乱码解决方案

    利用httpClient下载页面,先利用浏览器查询页面源代码得知页面编码为utf-8,但生成字符串后总是为乱码,经过查询得知,在设置httpGet参数时,设置了接受压缩类型为Gzip,但却没有对其进行 ...

  7. winhttp 请求头加上Accept-Encoding: deflate, gzip后该如何解压,不加会出现不可识别的乱码

    hRequest = OpenRequest(hConnect, L"GET" ..... //自动判断gzip,如果压缩了,就会自动解压     DWORD dwOption = ...

  8. 搜狐新闻推荐算法原理 | “呈现给你的,都是你所关心的”

    导读 在当前这个移动互联网时代,各种信息内容爆炸,面对海量数据,用户希望在有限的时间和空间内,找到自己感兴趣的内容,这就是推荐需要解决的问题.接下来主要讲解新闻推荐的算法原理. 01.新闻推荐算法架构 ...

  9. 十二之续、快速排序算法的深入分析

    十二之续.快速排序算法的深入分析 作者:July   二零一一年二月二十七日 -------------------------- 前言 一.快速排序最初的版本 二.Hoare版本的具体分析 三.Ho ...

最新文章

  1. 带你进入String类的易错点和底层本质分析!
  2. js获取本周、本月、本季、本年的第一天
  3. 小牛485通讯原理_plc和变频器通讯接线图详解
  4. C程序设计语言现代方法18:声明
  5. html移动端即时翻译插件,React框架实现移动页面翻译是一种什么体验?
  6. openpyxl安装_Openpyxl玩转Excel(一)——新建、读取、复制
  7. ad中装配图如何导出_AD的PDF文件如何进行输出,你都掌握了?
  8. 前端框架中的大熊猫Ember
  9. 用自动控制理论分析电力电子中的基本斩波电路
  10. oracle优化器analyzed,Oracle 学习之 性能优化(十三) 索引
  11. 最后一课——没有拥抱,已成永远
  12. 剖析2015四大合并案:滴滴快的、58赶集、美团大众点评、携程去哪儿 1+12?
  13. jvm jni 及 pvm pybind11 大批量数据传输及优化
  14. 穆利堂(推荐)WxOA房地产协同办公整体解决方案-河南郑州房地产OA协同办公系统软件 穆穆-movno1
  15. 服务间歇性停顿问题优化|得物技术
  16. Linux服务器CPU100%问题
  17. 为什么强烈推荐 Java 程序员使用 Google Guava 编程!
  18. java startw_java-Jetty(9.4.12):上下文启动失败o.e.j.w.WebAp...
  19. CSS3动画帧数科学计算法http://tid.tenpay.com/?p=5983
  20. 常见的产品生命周期模型

热门文章

  1. iOS UIColor RGB 颜色对照表
  2. Python宿舍管理系统 毕业设计-附源码231642
  3. [附源码]java毕业设计宠物商店管理系统
  4. PMO和项目经理必须掌握的战略制定与执行的思路和方法
  5. 什么是AIM短信?AIM短信是怎么发送的?
  6. STC89C52R单片机以及CF85911的AD转换
  7. Soraの第一篇博客
  8. 新的樱桃键盘 好想要啊
  9. 【JavaWeb学习】Vue核心
  10. 数学分析教程(科大)——1.1笔记+习题