Trovare la fine di un flusso compresso zlib
-
08-07-2019 - |
Domanda
Sto lavorando su un client NMDC (p2p, DC ++ e amici) con Qt. Il protocollo stesso è piuttosto semplice:
$command parameters|
Tranne compressione :
" ZPipe funziona inviando un comando $ ZOn | al cliente. Dopo $ ZOn seguirà un flusso compresso ZLib contenente comandi. Questo flusso terminerà con un EOF definito da ZLib. (non c'è $ ZOff nello stream compresso!) "
Ecco il codice pertinente:
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);
}
Quindi, come posso ottenere i valori di stream_is_complete
, uncompressed_stream
e remainder_of_data
? Non riesco a cercare i "$" successivi perché il flusso può contenerlo. Ho provato a cercare qualcosa che assomiglia a un EOF nella documentazione di zlib, ma non esiste nulla del genere, infatti, ogni stream termina con un carattere apparentemente casuale.
Ho anche giocato con qUncompress (), ma questo richiede un flusso completo, niente di meno, niente di più.
Soluzione
Stai usando zlib direttamente?
Totalmente non testato ...
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
}
Questo decomprime in modo incrementale (si gonfia) da qbuffer
a qout
e si interrompe quando gonfia
dice " non più " ;.
Forse sarebbe meglio prendere in prestito da QuaZip invece.
Altri suggerimenti
La fine del file non è speciale per zlib. Il problema che vedo con il tuo codice è che usi "quotAll ()", che in realtà non ha alcun mezzo per segnalare "errori", come la fine del file.
Dovresti provare a utilizzare " read () " invece, in un ciclo. Se leggi lo stream e restituisce 0 byte letti, puoi essere sicuro di aver raggiunto la fine dello stream (l'altra estremità ha chiuso la connessione). Il buffer che hai letto, unendo quelli di tutti i precedenti "leggi" nel ciclo, ti darà il buffer completo.