Russian Qt Forum

Qt => Работа с сетью => Тема начата: voron1980 от Август 24, 2012, 17:43



Название: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Август 24, 2012, 17:43
Хочу реализовать своей простой протокол для отправки данных типа
Отправка
Код:
    _sok = new QTcpSocket();
    int i = 0;
    while(true)
    {
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out << (qint16)0;
        out << (QByteArray) QByteArray::number(i);
        out.device()->seek(0);
        out << (quint16)(block.size() - sizeof(quint16));
        _sok->waitForBytesWritten();
        i++;
        if(i > 1000)
            break;
    }

   
Прием
Код:
void MyThread::readyRead()
{    
    QDataStream in(socket);
    qint16 size;
    in >> size;
    qDebug() << "size" << size;
    QByteArray bytes;
    in >> bytes;
    qDebug() << "bytes" << bytes;
    bytes.clear();
}
После приема данных получается что цикл идет допустим 260 раз а потом останавливается, но когда я не отключаясь отправляю еще данные то цикл продолжается, то есть данные которые небыли отправлены отправляются при следующем клике, при этом добавляются новые данные, получается в очередь на отправку стоит в несколько раз больше данных чем передается.
То есть если кликать то картина в начале и в конце картина будет такая
Цитировать
bytesAvailable 7
size 5
bytes "0"
...
bytesAvailable 581
size 5
bytes "7"
...
bytesAvailable 73163
size 7
bytes "783"
Как бы передать весь цикл за одно нажатие кнопки?
Если использовать _sok->write(QByteArray file) то например файл 300Мб передается нормально, write все делает сам, но так ничего нельзя дописать в пакет.


Название: Re: QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Август 30, 2012, 16:26
Видимо QT сворачивается и скоро станет не актуальным поскольку перешел к другом хозяину, по этому все побежали на новую VS и .Net.
Жаль.


Название: Re: QTcpSocket не успевает принять пакеты отправленнm
Отправлено: mutineer от Август 30, 2012, 16:41
зачем писать размер отправленного пакета, если не проверять его при приеме?
Ты вычитываешь только первую посылку, хотя доступно для чтения (судя по bytesAvailable) больше

Про какие нажатия кнопки ты говоришь я вообще не понял


Название: Re: QTcpSocket не успевает принять пакеты отправленнm
Отправлено: voron1980 от Сентябрь 03, 2012, 14:21
зачем писать размер отправленного пакета, если не проверять его при приеме?
Ты вычитываешь только первую посылку, хотя доступно для чтения (судя по bytesAvailable) больше

Про какие нажатия кнопки ты говоришь я вообще не понял
Но пакеты тем не менее теряются, никак не могу найти нормальный бесплатный sniffer под Windows без WinPcap, что бы посмотреть содержимое пакетов и их количество, может кто подскажет?


Название: Re: QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: mutineer от Сентябрь 03, 2012, 14:41
С чего ты взял что пакеты теряются? судя по выводу они висят себе на принимающей стороне, а ты их не вычитываешь


Название: Re: QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Сентябрь 03, 2012, 16:12
С чего ты взял что пакеты теряются? судя по выводу они висят себе на принимающей стороне, а ты их не вычитываешь
Да они висели, но это удалось понять только после того как я проверил пакеты снифером "SoftPerfect Network Protocol Analyzer" вроде бесплатный есть портайбл версия, хватило с головой, но пришлось сервер запустить на отдельной машине, сниффер не следит за пакетами отправленными с хоста на самого себя, какой бы IP у него не стоял.
Оказывается в пакет кладется не один набор данных in >> size; in >> bytes; а сколько влезет, тогда сразу начал искать как пройтись по socket циклом и нашел
http://qt-project.org/forums/viewthread/3587
Код:
connect(socket, SIGNAL(readyRead()), SLOT(newData()));
....
void newData()
{
     while(socket->bytesAvailable())
    {
         QDataStream in(socket);
         QString str;
         in >> str;
         ....
    }
....
}


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: fuCtor от Сентябрь 03, 2012, 16:19
Не совсем корректная проверка. Из-за фрагментации при запросе может быть не весь набор данных получен => может быть считано все не полнотью, что может привести к дальнейшей некорректной работе. Нужно проверять доступно ли байт столько, сколько потребуется для корректного вычитывания структуры\пакета.


Название: Re: QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: mutineer от Сентябрь 03, 2012, 16:49
Да они висели, но это удалось понять только после того как я проверил пакеты снифером "SoftPerfect Network Protocol Analyzer"

То есть простого факта, что доступно для чтения 73163 байта, а читаешь ты только 7 недостаточно, чтобы понять что оставшееся так и висит в буфере?


Название: Re: QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: fuCtor от Сентябрь 03, 2012, 18:18
Да они висели, но это удалось понять только после того как я проверил пакеты снифером "SoftPerfect Network Protocol Analyzer"

То есть простого факта, что доступно для чтения 73163 байта, а читаешь ты только 7 недостаточно, чтобы понять что оставшееся так и висит в буфере?
Мы хотим сделать программу которая тупо вичитывает данные из буфера или программу, которая выполняет корректно заложенную логику?

В определенный момент времени там может оказаться допустим 3 байта, вероятность такой ситуации не такая уж маленькая. Тогда тело цикла (допустим чтение int32) отработает в лучшем случае некорректно и не вызовет падения, да буфер опустошен, но придет новый пакет и в нем будет один лишний байт, как результат как читать и обрабатывать данные?

Правильней проверить как уже писал доступность именно того объема который нужен для обработки, а не просто доступность, и только потом его вычитывать, иначе подождать нового пакета, либо сохранить что есть во внутренний буфер.
Если я не прав, то поправьте меня.


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Сентябрь 03, 2012, 22:51
Не совсем корректная проверка. Из-за фрагментации при запросе может быть не весь набор данных получен => может быть считано все не полнотью, что может привести к дальнейшей некорректной работе. Нужно проверять доступно ли байт столько, сколько потребуется для корректного вычитывания структуры\пакета.
Проверки никакой нет, просто данные считываются в том порядке в котором пришли, это подтверждают разные статьи, QDataStream контролирует порядок отправки и приема.
Но похоже если файл больше 2 мегабайт начинаются проблемы с целостностью, 9Мб уже битый.
Если склеивать куски файла в цикле нарезки(эти же куски пишутся в сокет) то файл склеивается нормально, а вот к серверу доходят не все данные, притом чем больше больше данных ушло тем ниже скорость, на 1,5Гб скорость по локальному интнрфейсу всего 200кб в секунду что очень мало, возможно виноват вывод через qDebug() <<.
Поскольку TCP протокол сам контролирует целостность то это похоже баг.
Еще раз, проверка сколько байт пришло ничего не даст, все проверяется на уровне QDataStream именно по этому я и заинтересовался темой, склеивать байты в ручную это очень трудоемкая работа.


Название: Re: QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: mutineer от Сентябрь 04, 2012, 10:10
Если я не прав, то поправьте меня.


Какое я имею отношение к правильности/неправильности считывания? Я указал на то, что местоположение "потерянных" пакетов видно без сниффера, просто по выводу проги


Название: Re: QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Сентябрь 04, 2012, 11:28
Если я не прав, то поправьте меня.


Какое я имею отношение к правильности/неправильности считывания? Я указал на то, что местоположение "потерянных" пакетов видно без сниффера, просто по выводу проги
Ну вы же написали в теме, может вы ошиблись?


Название: Re: QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: mutineer от Сентябрь 04, 2012, 11:29
Если я не прав, то поправьте меня.


Какое я имею отношение к правильности/неправильности считывания? Я указал на то, что местоположение "потерянных" пакетов видно без сниффера, просто по выводу проги
Ну вы же написали в теме, может вы ошиблись?

Что?


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Сентябрь 04, 2012, 13:17
Нет идей почему данные иногда искажаются?


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: lighting от Сентябрь 04, 2012, 14:39
Нет идей почему данные иногда искажаются?
Видимо не все считываете. А вообще код выложить не помешало-бы, или он у вас с первого поста не менялся?


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Сентябрь 04, 2012, 15:31
Нет идей почему данные иногда искажаются?
Видимо не все считываете. А вообще код выложить не помешало-бы, или он у вас с первого поста не менялся?
Поменялся(файлы прикрепил), пробую даже писать не на диск, временно кидаю в память и при отключении от сокета сразу скидываю на диск.
Вот пример нужно только в переменные wFName и rFName прописать пути файлов которые будут считываться и записываться, wFName1 нужен для проверки, что бы сравнивать каким должен был быть файл и wFName как его перекосило отправкой. После отключения wFName1 стирается на диске.
При передаче по сети 9Мб файл не всегда битый, а вот 78Мб например всегда, и если я кидаю в память то данных теряется меньше, а если писать сразу на диск то из 78Мб теряется ~1Мб если через буфер то теряется примерно ~200Кб.


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: fuCtor от Сентябрь 04, 2012, 18:32
Посмотрел код, не знаю может так и можно, но учитывая что сокеты используются асинхронные, то в таком же стиле нужно и читать/отправлять данные (так будет надежней), возможно где-то недочитывали данные, либо недосылали.

Переписал в клиенте и сервере эти части, сделал полностью асинхронными. Проверил на файле в 88Мб, после приема сверил контрольные суммы, все сошлось.

Клиент:
Код
C++ (Qt)
void MainWindow::on_pbSendTest1_clicked()
{
 
   QPointer<QFile> sendFile( new QFile(rFName));
 
if(sendFile->open(QFile::ReadOnly))
{
_sok->setProperty("file", QVariant::fromValue(sendFile));
QDataStream out(_sok);
 
out << quint16(0xCFCF);
out << qint64(sendFile->size());
 
_sok->write(sendFile->read(1300));
}
 
}
 
void MainWindow::OnbytesWritten(qint64 bytes)
{
   QTcpSocket * socket_ = qobject_cast<QTcpSocket*>(sender());
if(!socket_)
return;
 
QPointer<QFile> sendFile = socket_->property("file").value< QPointer<QFile> >();
if(sendFile.isNull())
return;
 
if(sendFile->atEnd())
{
sendFile->close();
delete sendFile.data();
_sok->setProperty("file", QVariant());
qDebug("File sended.");
} else {
socket_->write(sendFile->read(bytes));
}
}
 


Сервер:
Код
C++ (Qt)
void MyThread::readyRead()
{
QTcpSocket * socket_ = socket;
 
if(!socket_)
return;
 
QPointer<QFile> receiveFile = socket_->property("file").value< QPointer<QFile> >();
if(receiveFile.isNull())
{
if(socket_->bytesAvailable() >= (sizeof(quint16) + sizeof(qint64)) )
{
QDataStream in(socket_);
quint16 marker_ = 0;
qint64 fileSize_ = 0;
in >> marker_;
in >> fileSize_;
 
if(marker_ == 0xCFCF)
{
receiveFile = new QFile(wFName);
receiveFile->open(QFile::WriteOnly);
 
 
socket_->setProperty("file", QVariant::fromValue(receiveFile));
 
socket_->setProperty("remaining_size", QVariant::fromValue(fileSize_));
 
qDebug() << "Start file receiving. Size = " << fileSize_;
}
} else {
 
return;
}
}
 
 
if(receiveFile)
{
 
qint64 remainingSize = socket_->property("remaining_size").value<qint64>();
while(!socket_->atEnd() && remainingSize > 0)
{
if(remainingSize > socket_->bytesAvailable())
{
remainingSize -= receiveFile->write(socket_->readAll());
qDebug() << "Remaining size = " << remainingSize;
} else {
remainingSize -= receiveFile->write(socket_->read(remainingSize));
qDebug() << "File received.";
receiveFile->close();
delete receiveFile.data();
socket_->setProperty("file", QVariant());
break;
}
}
 
socket_->setProperty("remaining_size", QVariant::fromValue(remainingSize));
}  
}
 

Исправленную версию с комментариями прикладываю.


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Сентябрь 04, 2012, 23:53
Спасибо попробую!
-----

Протестировал работает! Думал использовать QDataStream для передачи приемки, в общем завтра на свежую голову попробую разобраться.
Главное есть маркер и можно понять что за пакет если я не ошибаюсь.
P.S.
К стати если кто будет смотреть пример нужно в конфигурации проекта удалить конфигурацию там где пути к среде компилятора и добавить званого, то что предложит IDE  по умолчанию, а затем добавить вариант сборки, иначе проект не запускается, это наверное так всегда если обмениваться проектами, в общем в первой закладке проекта все решается.


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: voron1980 от Сентябрь 05, 2012, 17:59
То что надо, отличная заготовка для обмена данными без лишних наворотов.

Код:
QObject::setProperty()
QObject::property()
Это для меня было в первые, но довольно удобно хранить там данные для открытого сокета, как я понял.

Еще раз спасибо.


Название: Re: [РЕШЕНО]QTcpSocket не успевает принять пакеты отправленные в теле цикла while.
Отправлено: fuCtor от Сентябрь 06, 2012, 15:51
То что надо, отличная заготовка для обмена данными без лишних наворотов.

Код:
QObject::setProperty()
QObject::property()
Это для меня было в первые, но довольно удобно хранить там данные для открытого сокета, как я понял.

Еще раз спасибо.

Да, если объектов (например сокетов) открытых более одного, то как вариант можно хранить связанные указатели в свойствах, что-бы не использовать массивы/таблицы. Единственное, для большей надежности, после создания экземпляра QFile, лучше ему еще присвоить родителем сокет, что-бы при удалении сокета (где-нибудь раньше времени) удалились и все связанные объекты во избежание утечек.