[⇐ К полному списку статей / Content] ID: 20141101_1925-qtcompress

Сжатие потока информации PHP-HTTP-Qt

Это второе дополнение к моей предыдущей статье «Шифровка потока информации PHP-HTTP-Qt». Для того что бы разобраться с тем что здесь написано, следует сначала ознакомиться с ней.

Вопрос о возможности их сжатия потока естественно возникает в связи с постоянной передачей больших объёмов разряженных данных (в формате XML) множеству клиентов. Тем более что у конечных пользователей программ-клиантов может быть и не быстрый доступ в Интернет, и сжатие в этом случае может дать существенный выйгрыш в скорости работы.
Я делаю сжатие/разжатие сразу в паре с шифровкой/дешифровкой и так же в связи с шифровкой о нём сейчас рассказываю, тем более что это делается и на стороне сервера и на стороне клиента подобным образом. Но вы, естественно, можете попробовать сделать сжатие/разжатие и без шифровки.
Для сдатия в PHP я использовал всё поточный фильтр zlib.deflate. На стороне клиента я применил класс QtIOCompressor из подзаброшенной ныне коллекции-библиотеки Qt Solutions.
Что бы XML лучше ужался, конечно, надо сжать его до шифровки, а не после.
В моём коде сжатие происходит опционально, в зависимости от значения переменной $compress в PHP и .. в коде клиента C++.

Итак, установка сжимающего фильтра на PHP (естественно, соответствующий фильтр должен быть доступен; проверка: phpinfo() ) выглядит так:
if ($compress=="RawZip") {

  $compress_alg "zlib.deflate"
}
else
{
  
$compress_alg false
}
if (
$compress_alg)
{
  
stream_filter_append($f$compress_algSTREAM_FILTER_WRITE);
}

В исходнике в статье про шифрование место, где фильт вставляется у меня помечено комментарием: ...[*]....

В программе клиенте поток-декомпрессор вставляется так:

    if (use_compression()) // if use decompressor
{ // the input stream is compressed
m_decompressor = new QtIOCompressor(m_decrypter); // QtIOCompressor *m_decompressor;
m_decompressor->setStreamFormat(QtIOCompressor::RawZipFormat);
m_decompressor->open(QIODevice::ReadOnly);

m_xmlsource = new QXmlInputSource(m_decompressor);
}

else

{
// the input stream isn't compressed
m_xmlsource = new QXmlInputSource(m_decrypter);
}
(Соответствующее место в исходнике я помечетил комменарием // [**]).

Таким образом информация проходит всю цепочку из таблицы MySQL через MySQLi — PHP — сжатие — шифровку — сеть (HTTP) — расшифровку — распаковку — SAX-парсер до GUI-виджета таблицы конвеерно!
Слоты для «массажа» района дешифратора и распаковщика в Qt этой цепочки таковы:

void
WorkplaceTab::onReadyRead()
{

if
( m_reader==NULL || m_xmlsource==NULL )
{

qDebug() << "WorkplaceTab::onReadyRead() - warning";
return
;
}


if
(m_first)
{

// выполняется один раз - при приёме превой порции данных
qDebug() << "m_reader->parse(m_xmlsource, true);";
if
(!m_reader->parse(m_xmlsource, true))
to_idle_state(); // прекращение режима приёма
m_first = false;
}


long
watchdog = 100;
while
( m_decrypter!=NULL &&
(
m_decompressor!=NULL?
m_decompressor->bytesAvailable():
m_decrypter->bytesAvailable())>0
)
{

m_xmlsource->fetchData();
if
(!m_reader->parseContinue())
to_idle_state(); // прекращение режима приёма

if
(watchdog--<0)
break
;
}
}


void
WorkplaceTab::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{

logmessage( tr("%1 bytes and %2 rows loaded").arg(bytesReceived).arg(m_row) );

if
(bytesReceived==bytesTotal)
{

// выполняется один раз - после приёма последней порции данных
qDebug() << "WorkplaceTab::onDownloadProgress LAST TIME";
if
(m_reader==NULL || m_xmlsource==NULL || m_reply==NULL)
{

qDebug() << "Warning: m_reader==NULL or m_xmlsource==NULL or m_reply==NULL!";
return
;
}


long
watchdog=1000000;
bool
b=true;
while
( b && m_xmlsource->data().length()>0 && --watchdog )
{

b = m_reader->parseContinue();
}


if
(b)
{

m_xmlsource->next(); // и ещё раз - вхолостую -
m_reader->parseContinue(); // что бы вызвалось QXmlInputSource::EndOfDocument
}
}
}


Согласитесь, это круто. Но, увы:
ложка дёгтя - если с шифрованием-расшифровкой всё идеально (уже прошло многомесячную интенсивную проверку обкатку), то с копрессия-декомпрессия очень редко, но, увы, всё же даёт сбой — в XML-е образуется мусорный участочек, парсер сигнализирует об ошибке... Происходит, правда, это редко, под нагрузкой и никогда — в начале (на маленьких данных не проявляется, обычно где то с 2000 строки), но всё же происходит... Была замечена ситуация, когда сбой был при работе клиента Windows, но не было при работе в Linux, откомпилированного из того-же исходника (правда библиотеки ZLib там были из разных исходников). В то же время сбой имеет тенденцию проявляться в одном и том же месте одного и того же длинного XML.
Так что я эту функцию сжатия в рабочих дистрибутивах сейчас выключаю, но в ряде простых задач с небольшими блоками данных вполне может прокатить. Или можно например в случае ошибок делать перезапрос с отключённым сжатием.
В чём причина не знаю, виноват скорее всего QtIOCompressor — точнее его не совершенство. Например он в принципе не умеет верно оценивать количество байт, которые он готов отдать в текущий момент.
Есть гипотеза, что ошибку можно обойти, например, усовершенствовав «массаж» или вставив между QtIOCompressor и QDecrypter-ом промежуточный буффер, скажем на 1 Kb.

Есть также открытые вопросы: какой стандартной утилитой можно распаковать данные, cгенерированные фильтром zlib.deflate в PHP?
Как этот поток распаковать на Java? (пока не дошёл до этого)?

В общем ещё есть тут над чем поразбираться!

Original: http://lj.rossia.org/users/shestero/140121.html
Tags: c++, it, php, qt, qt solutions, qtiocompressor, zlib.deflate
[⇐ К полному списку статей / Content]


© http://netdat.ru — Bulletin Publishing System, 2011-2016.