Encontrar o final de um fluxo zlib comprimido
-
08-07-2019 - |
Pergunta
Eu estou trabalhando em um cliente NMDC (p2p, DC ++ e amigos) com Qt. O protocolo em si é bastante simples:
$command parameters|
"ZPipe funciona enviando um comando $ ZON |. Para o cliente Após $ ZON um fluxo comprimido ZLib contendo comandos seguirá Este fluxo vai acabar com um EOF que define ZLib (não existe $ Zoff no fluxo comprimido.. !) "
Aqui está o código relevante:
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);
}
Então, como faço para obter os valores para stream_is_complete
, uncompressed_stream
e remainder_of_data
? Eu não posso olhar para a próxima '$' porque o fluxo pode contê-lo. Eu tentei olhar para algo parecido com um EOF na documentação zlib, mas não existe tal coisa, na verdade, todos os fins de fluxo com um caráter aparentemente aleatório.
Eu também brinquei com qUncompress (), mas que quer um fluxo completo, nada menos, nada mais.
Solução
Você está usando zlib diretamente?
Totalmente testado ...
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
}
Esta forma incremental descomprime (inflar) de qbuffer
para qout
, e pára quando inflate
diz "não mais".
Talvez seria melhor tomar emprestado de QuaZip .
Outras dicas
O fim do arquivo não é especial para zlib. O problema que vejo com o seu código é que você use "readAll ()", que na verdade não tem nenhum meio de relatórios "erros", como o fim do arquivo.
Você deve tentar usar "read ()" em vez disso, em um loop. Se você ler o fluxo e retorna 0 bytes ler, você pode ter certeza que você tenha atingido o fim do fluxo (a outra extremidade fechou a conexão). O buffer que você leu, juntando-as de toda a anterior "lê" no circuito, vai dar-lhe o tampão completo.