Russian Qt Forum
Ноябрь 22, 2024, 14:30 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: QTcpSocket и 48 килобайт  (Прочитано 8599 раз)
SLiDER
Гость
« : Январь 10, 2007, 18:16 »

Господа, кто мне может объяснить, это в моей голове баг или в Qt-ешных сокетах? Имеем приложение (тестовое) которое само себе может слать массивы данных через сокеты (QTcpSocket и QtcpServer). Функции приема и отправки данных выглядят следующим образом ->

Код:
void NetTester::sendData(){

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
#define S 110000 //49148
char data[S];
for (int i=0; i<S; ++i)
data[i]=i;
out.writeBytes(data, S);

QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));

clientConnection->write(block);
clientConnection->disconnectFromHost();
}


Код:
void NetTester::getData(){
QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_4_0);

char *block;
quint32 size;

in.readBytes(block, size);
delete [] block;
}


Все замечательно работает, до тех пор, пока размер буфера, передаваемого в функцию  writeBytes, не превышает 48 килобайт, на один байт больше и все ломается. Получаю сигналы о приходе данных, но сами данные прочитать не удается. функция  readBytes возвращает обнуленные указатель и размер, что, по документации, означает ошибку. Причем, сигнал получаю столько раз, сколько нужно, что бы переслать весь массив кусками по 48 кБ.
Небольшое исследование позволило отыскать в файле qnativesocketengine_win.cpp, в функции nativeWrite, следующий коментарий ->

Код:
// don't send more than 49152 per call to WSASendTo to avoid getting a WSAENOBUFS


После чего идет цикл который разбивает на кусочки переданный массив и шлет его кусками. Т.е. все, вроде, должно работать. однако ни фига не пашет. Начинаю подозревать что проблема зарыта в  функции  readBytes, но прежде чем начать копать ее, решил посоветоваться с умными людьми, может не придется изобретать велосипед.
Что скажете  :?:  :?:  :?:

Да, забыл добавить, ось - винда, кьют - 4.2.2, вижуал студио 2005.
Записан
Dendy
Гость
« Ответ #1 : Январь 11, 2007, 06:17 »

Во первьІх. ВьІделять в стеке 110 кб - кошунство. Стек не резиновьІй. Если и делаете тест, пользуйтесь кучей или QByteArray.

Далее - избавляйтесь от привьІчки делать константьІ макросами, там где можно сделать const int S = 110000. Тем более, что я не вижу #undef S.

Подозреваю, что sendData() реагирует на сигнал сервера о появлении подключения. Так?

В свою очередь getData() - слот на QTcpSocket::readyRead(). Так? ВьІ НИКОГДА не можете знать сколько вам пришло бай из отправленньІх. Будь они отправленьІ все вместе или по частям - прийти они могут как целиком, так и частями, причём не теми частями, что бьІло отправлено. Ибо ВьІ никогда не можете бьІть увереньІ в каком месте, на какой платформе и подсистема Qt и сопутствующие библиотеки разобьют данньІе на TCP пакетьІ и протолкнут в сеть.

Следуя из вьІшесказанного: Нельзя создавать QDataStream на девайсе, не зная, что отправленньІе данньІе полностью загруженьІ. QDataStream НЕ ВОЗВРАЩАЕТ ДАННЬІЕ В ПОТОК. Не до конца считанньІе данньІе поставят QDataStream в состояние ошибки и весь считанньІй кусок будет потерян, а оставшийся (что прийдёт немного позже) не будет играть никакой роли, ибо утеряна часть типа, что должен считьІваться атомарно.

Обязательно нужно предусмотреть механизм проверки размера передаваемьІх данньІх. Обязательно пользоваться QIODevice::bytesAvailable() и/или считьІванием в промежуточньІй буфер.
Записан
SLiDER
Гость
« Ответ #2 : Январь 11, 2007, 10:44 »

Цитата: "Dendy"
Во первьІх. ВьІделять в стеке 110 кб - кошунство. Стек не резиновьІй. Если и делаете тест, пользуйтесь кучей или QByteArray.

Далее - избавляйтесь от привьІчки делать константьІ макросами, там где можно сделать const int S = 110000. Тем более, что я не вижу #undef S.


Не надо так эмоционально реагировать, я же написал что програмка тестовая, и большинство кода (в том числе и #define S 110000 //49148) написано только что бы разобраться с проблемой 48 кБ.

Цитата: "Dendy"

Подозреваю, что sendData() реагирует на сигнал сервера о появлении подключения. Так?

В свою очередь getData() - слот на QTcpSocket::readyRead(). Так? ВьІ НИКОГДА не можете знать сколько вам пришло бай из отправленньІх. Будь они отправленьІ все вместе или по частям - прийти они могут как целиком, так и частями, причём не теми частями, что бьІло отправлено. Ибо ВьІ никогда не можете бьІть увереньІ в каком месте, на какой платформе и подсистема Qt и сопутствующие библиотеки разобьют данньІе на TCP пакетьІ и протолкнут в сеть.

Следуя из вьІшесказанного: Нельзя создавать QDataStream на девайсе, не зная, что отправленньІе данньІе полностью загруженьІ. QDataStream НЕ ВОЗВРАЩАЕТ ДАННЬІЕ В ПОТОК. Не до конца считанньІе данньІе поставят QDataStream в состояние ошибки и весь считанньІй кусок будет потерян, а оставшийся (что прийдёт немного позже) не будет играть никакой роли, ибо утеряна часть типа, что должен считьІваться атомарно.

Обязательно нужно предусмотреть механизм проверки размера передаваемьІх данньІх. Обязательно пользоваться QIODevice::bytesAvailable() и/или считьІванием в промежуточньІй буфер.


А вот это уже кое-что, спасибо за мысль по поводу bytesAvailable(), этот мехаинзм я как то упустил из виду, виноват. В данном случае QDataStream как раз и играет роль промежуточного буфера. Меня немного запутала способность функций readBytes и writeBytes самостоятельно укладывать размер записанных в поток данных и читать его прозрачно для пользователя, а то что данный механизм ни как не связан с механизмом сокетов как-то выпало из сознания. С другой стороны как-то думалось, что если механизм передачи позволяет разом отправить массив данных произвольного размера, то почему бы ему не уметь принимать его по кускам самостоятельно, а пользователю сообщать о готовности только после приема массива целиком. Вот собственно какие соображения ввели меня в заблуждение.  Улыбающийся
Еще раз спасибо за подсказку.

З.Ы. Идея использования QDataStream, в качестве промежуточного буфера, взята из примеров fortuneclient и fortuneserver.
Записан
Вудруф
Гость
« Ответ #3 : Январь 11, 2007, 13:55 »

Дело в том, что QDataStream не сможет определить количество отправленных данных. Для этого в поток придётся помещать дополнительную информацию, но ведь класс-то должен работать с любым источником информации, а не обязательно только с аналогичным классом.
Записан
SLiDER
Гость
« Ответ #4 : Январь 11, 2007, 14:33 »

Цитата: "Вудруф"
Дело в том, что QDataStream не сможет определить количество отправленных данных. Для этого в поток придётся помещать дополнительную информацию, но ведь класс-то должен работать с любым источником информации, а не обязательно только с аналогичным классом.


Я не это имел ввиду (QDataStream здесь, как выяснилось, вообще не причем), мне кажется что если уж QAbstractSocket позволяет вызвать функцию передачи данных произвольного размера, самостоятельно разбивая массив на блоки, то логично было бы что бы Qt и на приеме позаботилась о том, что бы собрать все в один массив и только один раз сгенерировать сигнал готовности данных. Вроде так: "Сказал А, говори и Б".
Записан
Dendy
Гость
« Ответ #5 : Январь 11, 2007, 16:50 »

Ан вот и нет. Ибо ето уже будет назьІваться ПРОТОКОЛ. Функционал QDataStream и QIODevice не предполагает создание собственньІх протоколов, ибо ето унивесальньІе низкоуровневьІе классьІ.

QDataStream - всего-лишь очень тонкая прослойка для межплатформенной конвертации типов в поток байтов. Никакой информации о размере. Всё что он делает - ретранслирует операции включения в поток и исключения в соответствующие типьІ. Никто не знает куда вьІ пишите, в файл или Sequental сокет. НеравномерньІй приход байтов из потока обязан разруливаться протоколом, которьІй разработчик напишет сам, используя функционал того же низкоуровневого QDataStream.

Код:
A::readBytes()
{
  QDataStream ds( socket_ );
  if ( packet_size_ == 0 )
  {
    if ( socket_->bytesAvailable() < PacketSizeBytes )
      return;
    ds >> packet_size_;
  }
  if ( socket_->bytesAvailable() < packet_size_ )
    return;
  Packet packet;
  ds >> packet;
  if ( ds.status() != QDataStream::Ok )
  {
    qWarning( "Packet corrupted!" );
    socket_->deleteLater();
    return;
  }
  packet_size_ = 0;
  process_packet( packet );
}


QDataStream::writeBytes() и QDataStream::readBytes() вводят в заблуждение. Фактически ето аналог QByteArray.
Записан
SLiDER
Гость
« Ответ #6 : Январь 11, 2007, 22:57 »

Цитата: "Dendy"
Ан вот и нет....


Вечером ехал домой ... много думал ... много отдыхать вредно, а праздники вообще зло. Как же меня переклинило.  Грустный Надеюсь это пройдет.  :oops: Спасибо за понимание.  Крутой
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.104 секунд. Запросов: 20.