Я знаю, что есть множество вопросов о zlib / gzip и т.д., но ни один из них не совсем соответствует тому, что я пытаюсь сделать (или, по крайней мере, я не нашел его) В качестве краткого обзора, у меня есть сервер C #, который распаковывает входящие строки с помощью GZipStream. Моя задача — написать клиент C ++, который будет сжимать строку, совместимую с декомпрессией GZipStream.
Когда я использую приведенный ниже код, я получаю сообщение об ошибке: «Магическое число в заголовке GZip неверно. Убедитесь, что вы передаете поток GZip». Я понимаю, что такое магическое число и все такое, я просто не знаю, как правильно установить его магическим образом.
Наконец, я использую пакет 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>
который удаляется, затем возвращает окончательную распакованную строку. Это почти совпадает с примером из 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. Затем он возвращает символ * сжатого ввода. Это то, что отправляется на сервер выше для распаковки.
Спасибо за любую помощь, вы можете дать мне! Кроме того, дайте мне знать, если вам нужна дополнительная информация.
В вашем CompressString
функция, которую вы возвращаете char*
полученный из местного std::string
, Строка будет уничтожена, когда функция вернется, что освободит память по указателю, который вы вернули.
Вероятно, что-то выделяется для этой области памяти и записывается поверх ваших сжатых данных, прежде чем они будут отправлены.
Необходимо убедиться, что память, содержащая сжатые данные, остается выделенной до тех пор, пока она не будет отправлена. Возможно, передав std::string&
в функцию и хранить его там.
Несвязанная ошибка: вы делаете char* outbuffer = new char[messageSize];
но нет вызова delete[]
для этого буфера. Это приведет к утечке памяти. Поскольку вы выкидываете исключения из этой функции, я бы рекомендовал использовать std::unique_ptr<char[]>
вместо того, чтобы пытаться вручную разобраться с вашим собственным delete[]
звонки. На самом деле я всегда рекомендую std::unique_ptr
вместо явных призывов к delete
если возможно.
Других решений пока нет …