问题

后台服务传包太大时,我们框架可以使用 zlib 库对响应进行压缩;在这次服务调试过程中,使用 zlib compress2Z_BEST_COMPRESSION 模式进行压缩时,报 Z_BUF_ERROR(-5) 错误

分析

bool Codec::ZlibCompress(const std::string& src , std::string& compr_str)
{
int max_size = src.size();
if(max_size == 0)
{
return false;
}
DataBuffer<char> compr(max_size);
memset(compr, 0, max_size);
uLongf compr_len = max_size;
int res = compress2((Bytef*)compr.Get(),&compr_len,(const Bytef*)(src.c_str()),src.size(),9);
if(res != Z_OK)
{
return false;
} compr_str = Base64Encode((const void*)compr,compr_len);
return true;
}

这里 compress2 的输出缓冲区大小设置成为待压缩原串的长度了,在压缩数据太小或随机性很大等情况下,压缩得到的数据可能会比原来的大,导致 Z_BUF_ERROR;

下面的数据是对随机字符的压缩情况,src_size 为原串大小,compr size 为压缩后长度,说明在字符很随机的情况下,压缩完再使用 Base64Encode(Base64 编码数据会增大 1/3),长度一般都会变大。

解决

int compressBound(uLong sourceLen);

zlib 提供了一个 compressBound 方法,用于估算调用一次 compress2()/compress(),用于压缩 sourceLen 长度数据所需输出缓冲区的最大大小。

但是框架压缩后,对压缩串进行 Base64 编码(Base64 编码数据会增大 1/3),最终的输出缓冲区大小应该至少设置为 compressBound(sourceLen) * 4 / 3,因此改后的代码为:

bool Codec::ZlibCompress(const std::string& src , std::string& compr_str)
{
int src_size = src.size(); // 原串则不进行压缩,且压缩输出串置空
if(0 == src_size)
{
compr_str = "";
return true;
} /*
经过测试,在 best_compress(9) 的模式下,由随机字符构成的原串,压缩输出串有可能比原串长度大
由随机字符构成的原串,长度在 0-40k 情况下,output_buffer_size / compressBound() * 100 最大为 133.33(best_compress 和 best_speed 下此值一样)
为保证输出缓冲区足够大,缓冲区大小设置为 compressBound() * 2
*/
int max_size = compressBound(src_size) * 2; // 如果得到的输出缓冲区大小不大于 0,则返回压缩失败
if(max_size <= 0)
{
return false;
} DataBuffer<char> compr(max_size);
uLongf compr_len = max_size;
int res = compress2((Bytef*)compr.Get(),&compr_len,(const Bytef*)(src.c_str()),src_size,9);
if(res != Z_OK)
{
return false;
} compr_str = Base64Encode((const void*)compr,compr_len);
return true;
} bool Codec::ZlibDecompress(const std::string& base64_src , std::string& decmpr_str)
{
int size = base64_src.size(); if(0 == size)
{
decmpr_str = "";
return true;
} DataBuffer<char> src(base64_src.size());
int base64_res = Base64Decode((const char*)(base64_src.c_str()),base64_src.size(),(void*)src,&size);
if(base64_res != 0)
{
return false;
}
int max_size = size*30;
DataBuffer<char> decompr(max_size);
uLongf decompr_len = max_size;
uLong src_size = size;
int res = uncompress2((Bytef*)decompr.Get(),&decompr_len,(const Bytef*)src.Get(),&src_size);
if(res != Z_OK)
{
return false;
}
decmpr_str = std::string(decompr , decompr_len);
return true;
}

参考

compressBound

http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/zlib-compressbound-1.html

In what situation would compressed data be larger than input?

https://stackoverflow.com/questions/16992754/in-what-situation-would-compressed-data-be-larger-than-input

zlib详细基础教程

https://www.0xaa55.com/thread-442-1-1.html

zlib 压缩输出缓冲区 overflow 问题的更多相关文章

  1. PHP基础之输出缓冲区基本概念、原理分析

    一.概念 在PHP运行的过程中,可以将会产生输出的函数或操作结果暂时保存在PHP的缓冲区,只有当缓冲区满了.或者PHP运行完毕.或者在必要时候进行输出,才会将数据输出到浏览器,此缓冲数据的区域称为PH ...

  2. 深入理解php的输出缓冲区(output buffer)

    这篇文章是翻译自Julien Pauli的博客文章PHP output buffer in deep,Julien是PHP源码的资深开发和维护人员.这篇文章从多个方面讲解了PHP中的输出缓冲区以及怎么 ...

  3. PHP中zlib扩展实现GZIP压缩输出各种方法总结

    一般情况下我们出现大量数据传输理希望减少服务器的带宽压力,会采取一种方式来压缩文件传输,php中用zlib也可以实现gzip压缩输出,下面我们来看GZIP压缩输出各种方法总结. GZIP(GNU-ZI ...

  4. 饼干是这样压缩的——PHP使用zlib扩展实现页面GZIP压缩输出

    饼干是这样压缩的——PHP使用zlib扩展实现页面GZIP压缩输出 GZIP(GNU-ZIP)是一种压缩技术.经过GZIP压缩后页面大小可以变为原来的30%甚至更小.这样用户浏览的时候就会感觉很爽很愉 ...

  5. PHP的输出缓冲区(转)

    什么是缓冲区?简单而言,缓冲区的作用就是,把输入或者输出的内容先放进内存,而不显示或者读取.至于为什么要有缓冲区,这是一个很广泛的问题,如果有兴趣,可以在网山找下资料.其实缓冲区最本质的作用就是,协调 ...

  6. PHP的输出缓冲区

    什么是缓冲区?简单而言,缓冲区的作用就是,把输入或者输出的内容先放进内存,而不显示或者读取.至于为什么要有缓冲区,这是一个很广泛的问题,如果有兴趣,可以在网山找下资料.其实缓冲区最本质的作用就是,协调 ...

  7. zlib打印bit length overflow

    bit length overflow code bits -> code bits -> zlib库输出此log,此log不代表压缩出现错误,没有什么危害,而且zlib非常稳定,完全可以 ...

  8. Nginx_地址重写(rewrite)_日志管理(log_format)_压缩输出_Nginx设定限速_Nginx设置反向代理及反向代理缓存

    Nginx地址重写 Nginx rewrite rewrite语法规则1).变量名可以使用 "=" 或 "!=" 运算符~ 区分大小写~* 不区分大小写^~ 禁 ...

  9. Delphi Base64编码/解码及ZLib压缩/解压

    最近在写的程序与SOAP相关,所以用到了一些Base64编码/解码及数据压缩/解压方面的知识. 在这里来作一些总结:   一.Base64编码/解码   一般用到的是Delphi自带的单元EncdDe ...

随机推荐

  1. OC第二天—封装

    /.锁定头文件的方法 1. 打开终端 2. 进入到Xcode的目录, 命令:   cd /Applications/Xcode.app 3. 把系统头文件修改为只读, 命令:   sudo chown ...

  2. python“# -*- coding: UTF-8 -*-”

    python跑一趟红 python脚本文件中,python编译器是使用ascii码来解释脚本内容.如果.py源文件中包含中文,会报错(注释也报错).所以文件开头加上"# -*- coding ...

  3. redis简单测试用例(内存不足,可以使用redis)

    Redis本质上是一个Key-Value类型的内存数据库,很像memcached,听说他的性能远高于memcached,所以想自己搞个玩下.看到底有什么好处. 在windows下使用redis首先要 ...

  4. DBUtils学习总结

    这几天闲着无聊,就看了一下DBUtils这个数据库组件.中间有了一些想法,现在记录下来. 文章主要分几部分 1 最简单同时也是最经常使用的一些范例 2 学习源码前的一些知识储备 3 我自己写的mydb ...

  5. 【Android 应用开发】Android - 时间 日期相关组件

    源码下载地址 : -- CSDN :  http://download.csdn.net/detail/han1202012/6856737 -- GitHub : https://github.co ...

  6. Collections.sort()的分析

    首先我们得说明在Collections里面有两个排序方法 public static <T> void sort(List<T> list, Comparator<? s ...

  7. how tomcat works 读书笔记(二)----------一个简单的servlet容器

    app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...

  8. android的Devices窗口中Online显示成Offline

    这种情况几率很低,如果出现,点击Reset adb就好了.

  9. Android服务器——TomCat服务器的搭建

    Android服务器--TomCat服务器的搭建 作为一个开发人员,当然是需要自己调试一些程序的,这个时候本地的服务器就十分方便了,一般都会使用TomCat或者IIS服务器,IIS就比较简单了,其实t ...

  10. 苹果新的编程语言 Swift 语言进阶(十一)--实例的初始化与类的析构

    一 .实例的初始化          实例的初始化是准备一个类.结构或枚举的实例以便使用的过程.初始化包括设置一个实例的每一个存储属性为一个初始值,以及执行任何其它新的实例能够使用之前需要的设置或初始 ...