问题

后台服务传包太大时,我们框架可以使用 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. (九)UIScrollView和PageControl的分页

    涉及到内容的滚动与拖拽,使用UIScrllView. 对于滚动的多张图片,由于超出屏幕,应该使用代码添加代码. 添加的细节是:图片的宽高即为滚动视图的宽高,图片的y=0,x=图片的序号乘以图片的宽度. ...

  2. tomcat生产部署关键参数设置

    JVM设置 个节点,每个tomcat预计处理500个链接,那么连接池的长连接数最大设为2000. 全节点复制(DeltaManager)模式集群节点数3-6为宜. 主备复制(BackupMnagage ...

  3. 打造你的开发神器——介绍Android Studio上的几个插件

    这个月因为各种事情在忙,包括赶项目,回老家,还有准备旅游的事,所以应该写不了四篇博客了.今天介绍一下关于Android Studio 的几个好用的插件,都是我在用的,它们或能帮你节省时间,或者让你心情 ...

  4. redis简介(keeper实时报表的基本部分)

    网上有一篇介绍Redis的文章,由浅入深地讲解了Redis:http://blog.mjrusso.com/2010/10/17/redis-from-the-ground-up.html.强烈建议对 ...

  5. OpenCV实现仿射变换

    什么是仿射变换?¶ 一个任意的仿射变换都能表示为 乘以一个矩阵 (线性变换) 接着再 加上一个向量 (平移). 综上所述, 我们能够用仿射变换来表示: 旋转 (线性变换) 平移 (向量加) 缩放操作 ...

  6. C语言获取系统时间的函数

    在标准C编程中,我们如何写程序来获取当前系统的时间呢? 获取系统时间的函数 #include <time.h> time_t  time(time_t  *t) 参数:如果参数不为空,那么 ...

  7. Visual studio2010和Modelsim配置SystemC开发(转)

    本文转自一博文. 一.编译System库 1. 下载SystemC library source code, 到http://www.systemc.org注册会员账号后,即可下载SystemC li ...

  8. WINCE之“系统事件”——System/Events

    1. 简介 Event--事件,相信有线程概念的编程人员都知道,它可以用来同步不同进程.不同线程的通信.在Windows CE 5.0系统中,有一种我称之为"系统事件"的Event ...

  9. 和菜鸟一起学linux之linux性能分析工具oprofile移植

    一.内核编译选项 make menuconfig General setup---> [*] Profiling support <*> OProfile system profil ...

  10. sxoi爆炸祭

    好吧,纯粹是去玩玩的,我这么一个弱省的蒟蒻,进队纯粹是开玩笑.... Day0 去五中试机,感觉电脑手感不错,打了半个线段树的板子才发现试机要在自己的电脑上试,然后我无奈的搬东西(从26号搬到2号), ...