【发布时间】:2014-01-10 02:05:22
【问题描述】:
我正在尝试压缩/解压缩 QStrings。
我如何进行此设置:有一个 telnet/socket 模块可以从 telnet 服务器读取信息。然后将这些字符串发送到 Javascript(这是一个 think client,其中 UI 使用 QWebView 等在 HTML 中实现)。这工作正常。
我想添加对 MCCP 的支持(输入/输出的 zlib 压缩)。
如果可能的话,我希望为应用程序提供完整的脚本支持(只有最少的 C++ 代码,其他一切都由插件实现。)
我要实现两个函数,compressString和decompressString:
QString MainWindow::compressString(QString s) {
QByteArray str = s.toLocal8Bit();
int ret,flush;
z_stream strm;
unsigned char out[CHUNK];
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = deflateInit(&strm,Z_DEFAULT_COMPRESSION);
if ( ret != Z_OK ) {
return "ERROR: Failed to init z_stream";
}
// do until end of string
do {
strm.avail_in = str.size();
strm.next_in = (unsigned char*)str.data();
strm.avail_out = CHUNK;
strm.next_out = out;
flush = Z_FINISH;
ret = deflate(&strm,flush);
if ( ret == Z_STREAM_ERROR) {
return "ERROR: Z_STREAM_ERROR when deflating.";
}
} while ( flush != Z_FINISH );
deflateEnd(&strm);
return QString( (char *)out );
}
QString MainWindow::decompressString(QString s) {
QByteArray str = s.toLocal8Bit();
int ret,flush;
z_stream strm;
unsigned char out[CHUNK];
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if ( ret != Z_OK ) {
return "ERROR: Failed to init z_stream for inflate";
}
// do until end of string
do {
strm.avail_in = str.size();
strm.next_in = (unsigned char*)str.data();
strm.avail_out = CHUNK;
strm.next_out = out;
flush = Z_NO_FLUSH;
ret = inflate(&strm,flush);
if ( ret == Z_STREAM_ERROR) {
return "ERROR: Z_STREAM_ERROR when inflating.";
}
switch( ret ) {
case Z_NEED_DICT:
inflateEnd(&strm);
return "Error: Z_NEED_DICT";
break;
case Z_DATA_ERROR:
inflateEnd(&strm);
return "Error: Z_DATA_ERROR";
break;
case Z_MEM_ERROR:
inflateEnd(&strm);
return "Error: Z_MEM_ERROR";
break;
default:
break;
}
} while ( ret != Z_STREAM_END );
inflateEnd(&strm);
return QString( (char *)out );
}
这是我尝试过的第 3 次/第 4 次重写。压缩字符串方法返回:xóHÍÉÉEWÏ/ÊIQ 也许是对的,也许不是,不知道。
decompressString 方法返回:Error: Z_NEED_DICT;
不管我做什么(我已经尝试了一些在网络上找到的其他示例,以及堆栈溢出)。
现在我只是从 JS 中输入字符串,最终我想使用 telopt 86 实际压缩/解压缩从 telnet 服务器发送的字符串。
任何帮助/指针将不胜感激。我现在的目标只是安装一个简单的输入/输出测试。
更新:
在浏览完 zlib 膨胀代码后,我的问题就在这里:
state->mode = hold & 0x200 ? DICTID : TYPE;
Zlib 期望字符串上有某种标题。 hold 是缓冲区,DICTID 会将其发送到请求 Z_DICT_NEEDED。这很可能是因为标题不存在,或者在字符串传递时丢失了。如果我破解它并说state->mode = TYPE;,那么我会得到一个数据错误。
另外,我把解压功能改成这样:
QString MainWindow::decompressString(QString s) {
QByteArray str = s.toLocal8Bit();
unsigned char in[CHUNK];
for ( int i = 0; i < str.size(); i++ ) {
in[i] = (unsigned char)str.at(i);
}
fflush(stdout);
int ret,flush;
z_stream strm;
unsigned char out[CHUNK];
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if ( ret != Z_OK ) {
return "ERROR: Failed to init z_stream for inflate";
}
// do until end of string
do {
strm.avail_in = str.size();
strm.next_in = in;
strm.avail_out = CHUNK;
strm.next_out = out;
flush = Z_NO_FLUSH;
ret = inflate(&strm,flush);
if ( ret == Z_STREAM_ERROR) {
return "ERROR: Z_STREAM_ERROR when inflating.";
}
switch( ret ) {
case Z_NEED_DICT:
inflateEnd(&strm);
return "Error: Z_NEED_DICT";
break;
case Z_DATA_ERROR:
inflateEnd(&strm);
return "Error: Z_DATA_ERROR";
break;
case Z_MEM_ERROR:
inflateEnd(&strm);
return "Error: Z_MEM_ERROR";
break;
default:
break;
}
} while ( ret != Z_STREAM_END );
inflateEnd(&strm);
return QString( (char *)out );
}
然后在 deflate 生成的输出上使用 printf,然后发送到 inflate 的内容和数据(每个字符的十六进制值)似乎是相同的。
【问题讨论】:
-
检查输出缓冲区似乎表明在 deflate 产生的 null 之后有 4 个字节的数据。我可能必须将其实现为带有 toString 方法的 QObject,以便我可以维护一个可以传递的字节缓冲区?不知道如何仅使用 QStrings 来做到这一点。
-
在您的
(de)compressed函数中,您在循环中使用out和in缓冲区而不推进它。如果循环迭代超过一次,这将覆盖任何先前的数据。也许它适用于压缩功能,因此您丢失了字典。 -
那不应该影响输入,这就是字典无论如何都不会出现的地方?缓冲区太大了,字符串太小了,它不应该循环...... CHUNK 是巨大的,就像 16 KB。
-
我的计划是让 JS 层/插件选择做 MCCP,但也许我会把它放在 C 层并在 QByteArray 内容通过网络时解压缩。这可能会更容易。