【问题标题】:zLib with Qt C++ MCCP/ Telnet Compress2 option带有 Qt C++ MCCP/ Telnet Compress2 选项的 zLib
【发布时间】: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 函数中,您在循环中使用outin 缓冲区而不推进它。如果循环迭代超过一次,这将覆盖任何先前的数据。也许它适用于压缩功能,因此您丢失了字典。
  • 那不应该影响输入,这就是字典无论如何都不会出现的地方?缓冲区太大了,字符串太小了,它不应该循环...... CHUNK 是巨大的,就像 16 KB。
  • 我的计划是让 JS 层/插件选择做 MCCP,但也许我会把它放在 C 层并在 QByteArray 内容通过网络时解压缩。这可能会更容易。

标签: c++ qt telnet zlib


【解决方案1】:

基本上,你不能。

QStrings “丢失”了一些数据。为了使用 zlib 进行压缩/解压缩,您需要使用 QByteArray,事实上,如果您只使用 STL 字符串,并且仅在需要时转换为 QByteArrays,它会更容易。我发现可行的解决方案来自 pantehma.net http://panthema.net/2007/0328-ZLibString.html

因为我正在从外部源读取压缩字符串,所以我只是不将它们转换为 QStrings 并在 C 层处理压缩/解压缩,并且不费心将其传递给 javascript。只是不值得努力让它工作。

对这个问题投反对票的人圣诞快乐。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多