C++/zlib/gzip 压缩和 C# GZipStream 解压失败
C++/zlib/gzip compression and C# GZipStream decompression fails
我知道有很多关于 zlib/gzip 等的问题,但其中 none 非常符合我正在尝试做的事情(或者至少我还没有找到)。作为快速概述,我有一个使用 GZipStream 解压缩传入字符串的 C# 服务器。我的任务是编写一个 C++ 客户端,它将压缩与 GZipStream 解压缩兼容的字符串。
当我使用下面的代码时,我收到一条错误消息,上面写着 "The magic number in GZip header is not correct. Make sure you are passing in a GZip stream." 我知道幻数是什么以及所有内容,只是不知道如何神奇地正确设置它。
最后,我使用了 C++ zlib nuget 包,但也同样不幸地使用了直接来自 zlib 的源文件。
这里有一个更深入的视图:
服务器解压功能
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;
}
总而言之,它接受一个NetworkStream,获取压缩后的字符串,解压,添加到一个字符串,循环直到找到<EOF>
,然后returns最终解压后的字符串。这几乎与 MSDN 的示例相匹配。
这是 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];
}
长话短说,它接受一个字符串并通过非常标准的 zlib 压缩,并将 WBITS 设置为将其包装在 gzip header/footer 中。然后它 returns 压缩输入的一个 char*。这是上面发给服务器解压的。
感谢您能给我的任何帮助!另外,如果您需要更多信息,请告诉我。
在您的 CompressString
函数中,您 return 从本地声明的 std::string
获得了一个 char*
。当函数 returns 将释放您 returned.
指针处的内存时,字符串将被销毁
可能有东西被分配到这个内存区域并在发送之前覆盖您的压缩数据。
您需要确保包含压缩数据的内存在发送前保持分配状态。也许通过将 std::string&
传递给函数并将其存储在那里。
一个不相关的错误:你做了 char* outbuffer = new char[messageSize];
但没有为该缓冲区调用 delete[]
。这将导致内存泄漏。由于您也从该函数中抛出异常,因此我建议您使用 std::unique_ptr<char[]>
而不是尝试使用您自己的 delete[]
调用手动解决此问题。事实上,如果可能的话,我总是建议 std::unique_ptr
而不是显式调用 delete
。
我知道有很多关于 zlib/gzip 等的问题,但其中 none 非常符合我正在尝试做的事情(或者至少我还没有找到)。作为快速概述,我有一个使用 GZipStream 解压缩传入字符串的 C# 服务器。我的任务是编写一个 C++ 客户端,它将压缩与 GZipStream 解压缩兼容的字符串。
当我使用下面的代码时,我收到一条错误消息,上面写着 "The magic number in GZip header is not correct. Make sure you are passing in a GZip stream." 我知道幻数是什么以及所有内容,只是不知道如何神奇地正确设置它。
最后,我使用了 C++ zlib nuget 包,但也同样不幸地使用了直接来自 zlib 的源文件。
这里有一个更深入的视图:
服务器解压功能
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;
}
总而言之,它接受一个NetworkStream,获取压缩后的字符串,解压,添加到一个字符串,循环直到找到<EOF>
,然后returns最终解压后的字符串。这几乎与 MSDN 的示例相匹配。
这是 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];
}
长话短说,它接受一个字符串并通过非常标准的 zlib 压缩,并将 WBITS 设置为将其包装在 gzip header/footer 中。然后它 returns 压缩输入的一个 char*。这是上面发给服务器解压的。
感谢您能给我的任何帮助!另外,如果您需要更多信息,请告诉我。
在您的 CompressString
函数中,您 return 从本地声明的 std::string
获得了一个 char*
。当函数 returns 将释放您 returned.
可能有东西被分配到这个内存区域并在发送之前覆盖您的压缩数据。
您需要确保包含压缩数据的内存在发送前保持分配状态。也许通过将 std::string&
传递给函数并将其存储在那里。
一个不相关的错误:你做了 char* outbuffer = new char[messageSize];
但没有为该缓冲区调用 delete[]
。这将导致内存泄漏。由于您也从该函数中抛出异常,因此我建议您使用 std::unique_ptr<char[]>
而不是尝试使用您自己的 delete[]
调用手动解决此问题。事实上,如果可能的话,我总是建议 std::unique_ptr
而不是显式调用 delete
。