查找 zlib 压缩流的末尾

发布于 2024-08-09 08:57:24 字数 1205 浏览 9 评论 0原文

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

$command parameters|

除了压缩

“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(),但这需要一个完整的流,仅此而已。

I'm working on an NMDC client (p2p, DC++ and friends) with Qt. The protocol itself is pretty straightforward:

$command parameters|

Except for compression:

"ZPipe works by sending a command $ZOn| to the client. After $ZOn a ZLib compressed stream containing commands will follow. This stream will end with an EOF that ZLib defines. (there is no $ZOff in the compressed stream!)"

Here's the relevant code:

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);
}

So, how do I get the values for stream_is_complete, uncompressed_stream, and remainder_of_data? I can't look for the next '$' because the stream can contain it. I tried looking for something resembling an EOF in the zlib documentation, but there is no such thing, in fact, every stream ends with a seemingly random character.

I also played around with qUncompress(), but that wants a complete stream, nothing less, nothing more.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

勿挽旧人 2024-08-16 08:57:24

你直接用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 会更好。

Are you using zlib directly?

Totally untested...

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
}

This incrementally decompresses (inflates) from qbuffer to qout, and stops when inflate says "no more".

Maybe it would be better to borrow from QuaZip instead.

清晰传感 2024-08-16 08:57:24

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

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

The end of file is not special for zlib. The problem I see with your code is that you use "readAll()", which actually does not have any means of reporting "errors", such as end of file.

You should try to use "read()" instead, in a loop. If you read the stream and it returns 0 bytes read, you can be sure you've reached the end of stream (the other end has closed the connection). The buffer you've read, joining those of all the previous "reads" in the loop, will give you the complete buffer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文