You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

zlib inflate()解压缓冲区时部分挂起问题求助

问题:zlib跨版本压缩解压不稳定问题

我在MicroChip/XC32平台使用zlib 1.3.1(通过Harmony导入,编译正常),目标是通过串口接收数据,未压缩数据传输已正常工作。为节省传输时间,我测试对4k、8k及16k大小的二进制数据块压缩后循环传输,在单片机端调用inflate()解压。

测试阶段使用Python 3.6的zlib 1.2.10进行压缩,代码如下:

# 创建自定义配置的压缩器
compressor = zlib.compressobj( level=zlib.Z_BEST_COMPRESSION, wbits=zlib.MAX_WBITS )

data = compressor.compress(datablock) + compressor.flush() # datablock大小为16k

# 发送数据...

单片机接收数据后,在rcvbuf[]中存储数据,处理代码如下:

#include "zlib.h"

uint8_t             rcv_buf[LEN_RCV_BUF];              // LEN_RCV_BUF = 17000
//uint8_t             decompress_buf[LEN_RCV_BUF];
uint8_t             decompress_buf[40000];             // 测试时已扩容
uint8_t             my_adr[40000];                     // 测试时已扩容


/*
* 调用此函数时,rcv_buf已填充完成
*/
void cmd_write_to_myadr(void) {


volatile z_stream   out_stream;   // zlib结构体
uint16_t            have;
int                 ret;    
uint16_t            dat_len = ( rcv_buf[5] << 8 ) + rcv_buf[6];


/* 初始化解压状态 */
out_stream.zalloc     = Z_NULL;
out_stream.zfree      = Z_NULL;
out_stream.opaque     = Z_NULL;
out_stream.avail_in   = 0;
out_stream.next_in    = Z_NULL;

ret = inflateInit( &out_stream ); // HEAP-Size

if( ret != Z_OK ) {
  
//                      return ret;                 
  
  // neg_response(); // AAA
  return; 
}


// rcv_buf[7] != 0 表示数据已压缩

out_stream.avail_in    =  dat_len;
out_stream.next_in     = &rcv_buf[8];
out_stream.avail_out   =  sizeof( decompress_buf );
out_stream.next_out    = &decompress_buf[0];


// 执行解压
ret = inflate( &out_stream,  Z_NO_FLUSH );

assert(ret != Z_STREAM_ERROR);  /* 确保状态未被破坏 */

switch( ret ) {
  
  case Z_NEED_DICT  :
    ret = Z_DATA_ERROR;     /* 继续执行 */
  case Z_DATA_ERROR :
  case Z_MEM_ERROR  :
    inflateEnd( &out_stream );
//                            return ret;
//              negativ_response();
  return;                             // 断点触发位置!!!
  case Z_STREAM_END :
    break;
  default:
    return;                           // 断点触发位置!!!
}

have = sizeof( decompress_buf ) - out_stream.avail_out;
memWrite_sram_buffer( my_adr, &decompress_buf[0], have );
my_adr += have;


inflateEnd( &out_stream );

}

我传输的是图像和代码等二进制数据,问题在于:根据压缩数据块内容不同,解压有时正常有时失败。尝试过不同选项和窗口大小,但多次调用inflate()后总会出现问题,断点显示函数在内部挂起。目前Python仅用于测试,后续会替换为C++代码。请问是否是压缩和解压使用不同版本zlib导致的?


解答

zlib的版本兼容性设计得很好,1.2.10和1.3.1之间不会出现兼容性问题——zlib的压缩格式是向后兼容的,新版本的inflate完全可以处理旧版本compress生成的数据,反之亦然。你的问题大概率不是版本差异导致的,建议从以下几个方向排查:

  • 串口传输的完整性问题
    二进制数据在串口传输时容易出现丢字节、错序的情况,尤其是循环传输多块数据时。可以在每块压缩数据前加上校验码(比如CRC32),单片机接收后先校验数据完整性,再进行解压。
    另外要检查dat_len的计算是否正确:(rcv_buf[5] << 8) + rcv_buf[6]是否和Python端发送的压缩数据长度完全一致?如果长度计算错误,inflate会读到不完整或多余的数据,直接导致解压失败。

  • 解压逻辑的缺陷
    你当前只调用了一次inflate,但inflate不一定能一次处理完所有输入数据。正确的做法应该是循环调用inflate,直到返回Z_STREAM_END或者错误:

    do {
        ret = inflate(&out_stream, Z_NO_FLUSH);
        assert(ret != Z_STREAM_ERROR);
        switch (ret) {
            case Z_NEED_DICT:
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
                inflateEnd(&out_stream);
                return;
        }
        have = sizeof(decompress_buf) - out_stream.avail_out;
        // 处理已解压的数据
        memWrite_sram_buffer(my_adr, decompress_buf, have);
        my_adr += have;
        // 重置输出缓冲区,准备接收下一批解压数据
        out_stream.next_out = decompress_buf;
        out_stream.avail_out = sizeof(decompress_buf);
    } while (out_stream.avail_in > 0);
    

    另外,volatile修饰z_stream可能会干扰zlib内部的状态更新,建议去掉volatile——zlib的操作是单线程的,不需要这个修饰符。

  • 内存问题
    单片机的堆空间可能不足,inflateInit需要分配一定的内存。要确认inflateInit的返回值确实是Z_OK,有时候堆空间不足会导致初始化失败,但你的代码只是直接返回,没有任何报错提示,容易忽略这个问题。
    还要检查decompress_buf的内存是否足够,虽然你已经扩容到40k,但如果某些数据块的压缩比极低(比如本身就是高压缩率的二进制数据),解压后的大小可能超过40k,导致缓冲区溢出,破坏zlib的状态结构。

  • Python压缩的细节
    使用compressor.flush()默认会生成Z_FINISH的结束标记,这没问题,但如果是循环传输多个数据块,要确保每个数据块都是独立的压缩流——不要复用同一个compressobj处理多个数据块,每个数据块都创建新的compressobj,避免流状态互相干扰。


内容的提问来源于stack exchange,提问作者Craggan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 07:19:59