简体   繁体   English

C ++ / zlib / gzip压缩和C#GZipStream解压缩失败

[英]C++/zlib/gzip compression and C# GZipStream decompression fails

I know there's a ton of questions about zlib/gzip etc but none of them quite match what I'm trying to do (or at least I haven't found it). 我知道有很多关于zlib / gzip等的问题,但是没有一个问题与我正在尝试做的完全匹配(或者至少我没有找到它)。 As a quick overview, I have a C# server that decompresses incoming strings using a GZipStream. 作为一个快速概述,我有一个C#服务器使用GZipStream解压缩传入的字符串。 My task is to write a C++ client that will compress a string compatible with GZipStream decompression. 我的任务是编写一个C ++客户端,它将压缩与GZipStream解压缩兼容的字符串。

When I use the code below I get an error that says "The magic number in GZip header is not correct. Make sure you are passing in a GZip stream." 当我使用下面的代码时,我收到一条错误,上面写着“GZip标头中的幻数不正确。请确保传入GZip流。” I understand what the magic number is and everything, I just don't know how to magically set it properly. 我理解神奇的数字和一切,我只是不知道如何神奇地设置它。

Finally, I'm using the C++ zlib nuget package but have also used the source files directly from zlib with the same bad luck. 最后,我正在使用C ++ zlib nuget包,但也直接使用zlib中的源文件,运气相同。

Here's a more in depth view: 这是一个更深入的视图:

The server's function for decompression 服务器的解压缩功能

public static string ReadMessage(NetworkStream stream)
    {
        byte[] buffer = new byte[512];
        StringBuilder messageData = new StringBuilder();
        GZipStream gzStream = new GZipStream(stream, CompressionMode.Decompress, true);
        int bytes = 0;

        while (true)
        {
            try
            {
                bytes = gzStream.Read(buffer, 0, buffer.Length);
            }
            catch (InvalidDataException ex)
            {
                Console.WriteLine($"Busted: {ex.Message}");
                return "";
            }

            // Use Decoder class to convert from bytes to Default
            // in case a character spans two buffers.
            Decoder decoder = Encoding.Default.GetDecoder();
            char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
            decoder.GetChars(buffer, 0, bytes, chars, 0);
            messageData.Append(chars);
            Console.WriteLine(messageData);
            // Check for EOF or an empty message.
            if (messageData.ToString().IndexOf("<EOF>", StringComparison.Ordinal) != -1)
                break;
        }
        int eof = messageData.ToString().IndexOf("<EOF>", StringComparison.Ordinal);
        string message = messageData.ToString().Substring(0, eof).Trim();

        //Returns message without ending EOF
        return message;
    }

To sum it up, it accepts a NetworkStream in, gets the compressed string, decompresses it, adds it to a string, and loops until it finds <EOF> which is removed then returns the final decompressed string. 总而言之,它接受一个NetworkStream,获取压缩字符串,解压缩,将其添加到字符串,然后循环,直到找到<EOF> ,然后返回最终的解压缩字符串。 This is almost a match from the example off of MSDN. 这几乎与MSDN的示例相匹配。

Here's the C++ client side code: 这是C ++客户端代码:

char* CompressString(char* message)
{
    int messageSize = sizeof(message);

    //Compress string
    z_stream zs;
    memset(&zs, 0, sizeof(zs));

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

    int iResult = deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, (MAX_WBITS + 16), 8, Z_DEFAULT_STRATEGY);
    if (iResult != Z_OK) zerr(iResult);

    int ret;
    char* outbuffer = new char[messageSize];
    std::string outstring;

    // retrieve the compressed bytes blockwise
    do {
        zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
        zs.avail_out = sizeof(outbuffer);

        ret = deflate(&zs, Z_FINISH);

        if (outstring.size() < zs.total_out) {
            // append the block to the output string
            outstring.append(outbuffer,
            zs.total_out - outstring.size());
        }
    } while (ret == Z_OK);

    deflateEnd(&zs);

    if (ret != Z_STREAM_END) {          // an error occurred that was not EOF
        std::ostringstream oss;
        oss << "Exception during zlib compression: (" << ret << ") " << zs.msg;
        throw std::runtime_error(oss.str());
    }

    return &outstring[0u];
}

Long story short here, it accepts a string and goes through a pretty standard zlib compression with the WBITS being set to wrap it in a gzip header/footer. 简而言之,它接受一个字符串,并通过一个非常标准的zlib压缩,WBITS被设置为将其包装在一个gzip页眉/页脚中。 It then returns a char* of the compressed input. 然后它返回压缩输入的char *。 This is what is sent to the server above to be decompressed. 这是发送到上面的服务器以进行解压缩的内容。

Thanks for any help you can give me! 感谢你给与我的帮助! Also, let me know if you need any more information. 另外,如果您需要更多信息,请与我们联系。

In your CompressString function you return a char* obtained from the a locally declared std::string . CompressString函数中,返回从本地声明的std::string获取的char* The string will be destroyed when the function returns which will release the memory at the pointer you've returned. 当函数返回时,该字符串将被销毁,这将释放您返回的指针的内存。

It's likely that something is being allocated to the this memory region and writing over your compressed data before it gets sent. 可能会将某些内容分配给此内存区域并在发送之前写入压缩数据。

You need to ensure the memory containing the compressed data remains allocated until it has been sent. 您需要确保包含压缩数据的内存在发送之前保持分配状态。 Perhaps by passing a std::string& into the function and storing it in there. 也许通过将std::string&传递给函数并将其存储在那里。

An unrelated bug: you do char* outbuffer = new char[messageSize]; 一个不相关的错误:你做char* outbuffer = new char[messageSize]; but there is no call to delete[] for that buffer. 但是没有调用delete[]该缓冲区的delete[] This will result in a memory leak. 这将导致内存泄漏。 As you're throwing exceptions from this function too I would recommend using std::unique_ptr<char[]> instead of trying to manually sort this out with your own delete[] calls. 当您从此函数中抛出异常时,我建议使用std::unique_ptr<char[]>而不是尝试使用您自己的delete[]调用手动对其进行排序。 In fact I would always recommend std::unique_ptr instead of explicit calls to delete if possible. 事实上,我总是推荐std::unique_ptr而不是显式调用delete如果可能的话。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM