C++ ZLib GZipStream 解压缩 NULL 终止

C++ ZLib GZipStream Decompression NULL terminated

有很多围绕 zlib 和 GZipStreams 的问题,但 none 我找到了这个问题的答案。我正在使用 C# GZipStream 将压缩数据发送到客户端。它完全读取压缩数据,然后尝试解压缩它。但是,每次在循环中调用 inflate() 时,它只会获得以 NULL 结尾的字符串。发送二进制文件时,这是一个非常大的问题。

在向您展示代码之前,我只想说,如果我将接收到的压缩字节写入 .gz 文件并使用 gzFile/gzopen/gzread/gzclose,一切都会完美无缺。这意味着所有数据都正确输入。我想读入压缩后的数据,在内存中解压,然后把内容放在一个变量中。

我认为问题在于 inflate() 正在写入以 NULL 终止的 char*。我只是不知道如何让它成为一个字符串。我确实完全预料到这是一个重大疏忽和一个简单的修复。感谢您的帮助!

解压代码如下:

bool DecompressString(const std::string& message, std::string& dMsg)
{
    int bufferSize = 512;
    int messageSize = message.size() + 1;
    //decompress string
    z_stream zs;
    memset(&zs, 0, sizeof(zs));

    zs.zalloc = Z_NULL;
    zs.zfree = Z_NULL;
    zs.opaque = Z_NULL;
    zs.next_in = (Bytef*)message.data();
    zs.avail_in = messageSize;

    int ret = Z_OK;
    unsigned char* outbuffer = new unsigned char[bufferSize];

    if (inflateInit2(&zs, 16+MAX_WBITS) == Z_OK)
    {
        do {
            zs.next_out = outbuffer;
            zs.avail_out = bufferSize;

            ret = inflate(&zs, Z_NO_FLUSH);

            if (ret < 0) return false;
            std::stringstream tmpString;
            tmpString << outbuffer;
            if (dMsg.size() < zs.total_out) {
                dMsg.append(tmpString.str().substr(0, zs.total_out - dMsg.size()));
            }
        } while (ret == Z_OK);
    }

    inflateEnd(&zs);
    delete[] outbuffer;
    //"\n<EOF>" is appended by sender to signify the end of file. This removes it
    if (dMsg.find("\n<EOF>") != -1)
        dMsg = dMsg.substr(0, dMsg.find("\n<EOF>"));

    return true;
}

解决方案的工作代码:

bool DecompressString(const std::string& message, std::string& dMsg)
{
    int bufferSize = 512;
    int messageSize = message.size() + 1;
    //decompress string
    z_stream zs;
    memset(&zs, 0, sizeof(zs));

    zs.zalloc = Z_NULL;
    zs.zfree = Z_NULL;
    zs.opaque = Z_NULL;
    zs.next_in = (Bytef*)message.data();
    zs.avail_in = messageSize;

    int ret = Z_OK;
    unsigned char* outbuffer = new unsigned char[bufferSize];

    if (inflateInit2(&zs, 16+MAX_WBITS) == Z_OK)
    {
        // get the decompressed bytes blockwise using repeated calls to inflate
        do {
            zs.next_out = outbuffer;
            zs.avail_out = bufferSize;

            ret = inflate(&zs, Z_NO_FLUSH);

            if (ret < 0) return false;
            //Here's the difference
            if (dMsg.size() < zs.total_out)
                dMsg.append(reinterpret_cast<char*>(outbuffer), bufferSize);
            //End
        } while (ret == Z_OK);
    }

    inflateEnd(&zs);
    delete[] outbuffer;

    if (dMsg.find("\n<EOF>") != -1)
        dMsg = dMsg.substr(0, dMsg.find("\n<EOF>"));

    return true;
}

string本身没有问题,可以处理二进制数据。 正是这一行假设 zero-terminated c-string:

tmpString << outbuffer;

替换为

tmpString.append(outbuffer, bufferSize);