【问题标题】:Finding the end of a zlib compressed stream查找 zlib 压缩流的结尾
【发布时间】:2009-10-28 15:17:45
【问题描述】:

我正在使用 Qt 开发 NMDC 客户端(p2p、DC++ 和朋友)。协议本身非常简单:

$command parameters|

compression除外:

"ZPipe 通过向客户端发送命令 $ZOn| 来工作。在 $ZOn 之后,将跟随一个包含命令的 ZLib 压缩流。该流将以 ZLib 定义的 EOF 结束。(压缩流中没有 $ZOff !)”

以下是相关代码:

QTcpSocket *conn;
bool compressed;
QByteArray zbuffer;
QByteArray buffer;

// ...

void NMDCConnection::on_conn_readyRead() {
    // this gets called whenever we get new data from the hub

    if(compressed) {            // gets set when we receive $ZOn
        zbuffer.append(conn->readAll());


        // Magic happens here


        if( stream_is_complete ) {
            buffer.append(uncompressed_stream);
            buffer.append(remainder_of_data);
            compressed = false;
        }
    } else { 
        buffer.append(conn->readAll());
    };
    parse(buffer);
}

那么,如何获取 stream_is_completeuncompressed_streamremainder_of_data 的值?我无法寻找下一个'$',因为流可以包含它。我尝试在 zlib 文档中寻找类似 EOF 的东西,但没有这样的东西,事实上,每个流都以看似随机的字符结尾。

我也玩过 qUncompress(),但它需要一个完整的流,仅此而已。

【问题讨论】:

    标签: c++ c qt networking zlib


    【解决方案1】:

    你是直接用zlib的吗?

    完全未经测试...

    z_stream zstrm;
    QByteArray zout;
    // when you see a $ZOn|, initialize the z_stream struct
    parse() {
        ...
        if (I see a $ZOn|) {
            zstrm.next_in = Z_NULL;
            zstrm.avail_in = 0;
            zstrm.zalloc = Z_NULL;
            zstrm.zfree = Z_NULL;
            zstrm.opaque = 0;
            inflateInit(&zstrm);
            compressed = true;
        }
    }
    void NMDCConnection::on_conn_readyRead() {
        if (compressed) {
            zbuffer.append(conn->readAll());
            int rc;
            do {
                zstrm.next_in = zbuffer.data();
                zstrm.avail_in = zbuffer.size();
                zout.resize(zstrm.total_out + BLOCK_SIZE);
                zstrm.next_out = zout.data() + zstrm.total_out;
                zstrm.avail_out = BLOCK_SIZE;
                rc = inflate(&zstrm, Z_SYNC_FLUSH);
                zbuffer.remove(0, zstrm.next_in - zbuffer.data());
            } while (rc == Z_OK && zstrm->avail_out == 0);
            if (rc == Z_STREAM_END) {
                zout.truncate(zstrm.total_out);
                buffer.append(zout);
                zout.clear();
                buffer.append(zbuffer);
                zbuffer.clear();
                compress = false;
                inflateEnd(&zstrm);
            }
            else if (rc != Z_OK) {
                // ERROR!  look at zstrm.msg
            }
        }
        else // whatever
    }
    

    这会从qbuffer 逐步解压缩(膨胀)到qout,并在inflate 说“不再”时停止。

    也许向QuaZip借用会更好。

    【讨论】:

    • 嗯,一个明显的改进是继续将zout 刷新到buffer 中——这样你就可以在压缩流全部到达之前开始解析。另一方面,在$ZOn| 中看到$ZOn| 将是非常可怕的......
    【解决方案2】:

    文件结尾对于 zlib 来说不是特殊的。我在您的代码中看到的问题是您使用“readAll()”,它实际上没有任何报告“错误”的方法,例如文件结尾。

    您应该尝试在循环中使用“read()”。如果您读取流并返回读取的 0 字节,则可以确定您已到达流的末尾(另一端已关闭连接)。您已读取的缓冲区,加入循环中所有先前“读取”的缓冲区,将为您提供完整的缓冲区。

    【讨论】:

    • 不,不,不。 QTcpSocket 上的“文件结束”意味着我们已断开连接,并且将触发 disconnected() 信号。 readyRead() 特别表示我们有新的可解析数据。我正在寻找的 EOF 是压缩段的结尾,我们恢复正常的未压缩协议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多