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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблема с TCP  (Прочитано 11325 раз)
Shaman13
Гость
« : Июль 31, 2006, 13:48 »

Добрый день,

имеется такая задача: на машине-сервер запущен gui, который по нажатию кнопки должен отображать живое видео, которое посылается машиной-клиентом. По нажатию другой кнопки передача видео должна прекратиться.

Для передачи видеокартинок от клиента к серверу я использую функцию connectToHost, которая вызывает сигнал newConnection на сервере, где и происходит прием данных и отсылка их в gui. Для передачи команд от сервера клиенту (начать/прекратить отсылку картинок) я отсылаю соответствующие коды с помощью QDataStream (картинки пересылаются аналогично). Команды ловятся сигналом клиента readyRead.

Теперь проблема - когда идет передача видео, и я отсылаю команду, что пора прекращать, сигнал readyRead возникает на клиенте примерно в 30% случаев. Единственное объяснение, которое я могу придумать - клиент непрерывно отсылает картинки и поэтому пропускает посланные ему команды. Пробовал всякие waitForReadyRead, waitForBytesWritten, ничего не помогает.  В чем тут может быть проблема?

Заранее спасибо.
Записан
C.H.
Гость
« Ответ #1 : Июль 31, 2006, 14:13 »

поидее все должно быть наоборот, клиент это там где смотришь, а тот который посылает - это сервер.
Что будешь делать когда захочешь с двух или более машин смотреть картинку???

А по делу - исходники в студию!
Какой Ку?
Потоки еще какие-то крутятся???
Записан
Shaman13
Гость
« Ответ #2 : Июль 31, 2006, 15:33 »

Вообще-то такая задача и стоит, машин будет несколько. Я так понял, одна - сервер, а остальные - клиенты, которые будут коннектиться к серверу и выполнять то, что он им скажет (в частности, передавать  видео). Подразумеваетя, что инициировать передачу данных может как сервер, так и клиент.

Qt 4.0

Кода слишком много, но упрощенно примерно так:

На сервере:

Код:

QDataStream stream;
stream.setVersion(QDataStream::Qt_4_0);
OnNewConnection()
{
  QTcpSocket *socket=tcpServer->nextPendingConnection();
  stream.setDevice(socket);
  stream >> nPacketType // клиент сообщает, что именно он сейчас пришлет
...
  switch (nPacketType){
    case LIVE_VIDEO:
      stream >> nWidth >> nHeight >> nFormat >> data; // получаем картинку
  }
...
}
...
OnBtnStopLiveVideo()
{
...
  stream << STOP_LIVE_VIDEO;
}

На клиенте:
Код:

OnReadyRead()
{
  stream << message; // сервер сообщает, что именно он сейчас пришлет
  switch (message){
    case STOP_LIVE_VIDEO:
     m_bLiveVideo=false;
...
  }
...
}
OnTimer()
{
  if (m_bLiveVideo){
    socket->disconnectFromHost();
    socket->connectToHost(...);
    stream >> LIVE_VIDEO >> nWidth >> nHeight >> nFormat >> data;
  }
}

Проблема в том, что OnReadyRead на клиенте срабатывает через раз при нажатии на BtnStopLiveVideo на сервере...
Записан
C.H.
Гость
« Ответ #3 : Июль 31, 2006, 15:40 »

пример с threadedfortuneserver и клинетом пробовал?
Без таймера, с потоком?
Почитай Книгу "C++ GUI Programming with Qt 4 By Jasmin Blanchette, Mark Summerfield"
Там есть пример аля threadedfortuneserver.
Глава 14. Writing TCP ClientServer Applications
Записан
Alqualos
Гость
« Ответ #4 : Август 01, 2006, 22:56 »

Так, во-первых: поставить последний Qt 4.1.4. С 4.0 столько багов пофиксено, что не перечесть.

Во-вторых, есть всякая разная буферизация. Хорошо бы проверить каким-нибудь tcpdump'ом (или какой-нибудь аналогичной виндовой дрянью), есть ли реальная передача данных в сети по указанному порту вроде:
tcpdump -i eth0 tcp port 9999
Если нет передачи, то может дело в буферизации и имеет смысл использовать какой-нибудь flush(). Были у меня подобные проблемы, так я их и решал, вполне успешно. Правда, то было с QTextStream и там была буферизация в самом классе, а не в QTcpSocket (в QDataStream вроде ничего похожего нет). Остаётся, правда, вопрос, не может ли этот flush внезапно вызвать блокировку (например, если сетевой буфер переполнен), но я его не разбирал, просто забив ^_^

Ах да, ещё, опять же из-за буферизации, данные могут приходить одной порцией. То есть на сервер может прийти сразу несколько сообщений пачкой. Согласно документации, readyRead() будет вызван один раз в таком случае. Так что там чтение надо бы в цикле сделать и bytesAvailable() проверять. Пачки опять же можно посмотреть tcpdump'ом.
Записан
Shaman13
Гость
« Ответ #5 : Август 08, 2006, 19:16 »

Сделал через среды, поменял сервер и клиент местами - вроде все работает пока что...
Записан
Joe
Гость
« Ответ #6 : Август 10, 2006, 02:13 »

В продолжение темы. Я почти в отпаде, говоря честно Грустный
Есть не-шибко простая система -
клиент один, рассылает серверам пакеты, сервера - простые guiless, в одном потоке просто ждут, пока придёт чего, хавают данные - и возвращают всё назад и ждут дальше. Идиллия.
Так вот в чём засада. Когда я проектировал и собирал первую версию, была qt4.1.1 - и хорошо было.
Начиная с 4.1.3 - не работет как надо (не проверял 412, может там тоже всё плохо). Засада в том, что или не передаётся от клиента весь запрос в какой-то момент - или не приходит к "серверу" всё. Блин, ну на одной машине когда сидят, как это может не дойти? всякие wautForBytesWritten и flush на клиенте не приводят ни к какому ощутимому результату.

код "сервера" -

#include <iostream>
using namespace std;

#include <QCoreApplication>
#include <QDataStream>
#include<QTcpSocket>

#include <QColor>

#define clamp(x,min,max) (x <= min ? min : (x >= max ? max : x))

typedef float float3[3];

struct bucketDim
{
   int w, h;
   float dx0, dx1, dy0, dy1;
};

struct geobucket : bucketDim
{
   float3* P;
   float3* V;
   geobucket() {};
   geobucket(const bucketDim *src) { *(bucketDim*)this = *src; P = NULL; V = NULL; };
   ~geobucket()  {};
};

struct imgbucket : bucketDim
{
   float3* C;
   float* a;
   imgbucket(const bucketDim *src) { *(bucketDim*)this = *src; C = NULL; a = NULL; };
   ~imgbucket() { if(C != NULL) delete [] C; if(a != NULL) delete [] a; };
};

enum packetType
{
   unknownPacket = 0,
   imagePacket,
   shaderParameter,
   shutdownRenderNode,
   packetTypeForce = 0xffffffff
};

geobucket* readGeo(quint32 blockSize, QDataStream&in);
bool flushUnknown(quint32 blockSize, QDataStream&in);
imgbucket* render(geobucket*);

int main(int argc, char *argv[])
{
    //QCoreApplication a(argc, argv);

   QTcpSocket socket;

   socket.connectToHost("localhost", 5005);
   if(!socket.waitForConnected())
   {
      cout << "FAILED CONNECTION" << endl;
   };

   // HANDSHAKING
   {
      QDataStream in(&socket);
      in.setVersion(QDataStream::Qt_4_0);

      quint16 blockSize;

      while(socket.bytesAvailable() < (int)sizeof(quint16))
      {
         if (!socket.waitForReadyRead(5000))
         {
            cout << "WAIT TIMEOUT" << endl;
            return -1;
         }
      }

      in >> blockSize;

      while(socket.bytesAvailable() < blockSize)
      {
         if (!socket.waitForReadyRead(5000))
         {
            cout << "WAIT TIMEOUT" << endl;
            return -1;
         }
      }

      QString info;
      in >> info;

      QByteArray ba = info.toAscii();
      cout << "HANDSHAKE: " << ba.constData() << endl;

      // SEND HANDSHAKE

      QByteArray block;
      QDataStream buff(&block, QIODevice::WriteOnly);
      buff.setVersion(QDataStream::Qt_4_0);

      buff << (quint16)0;
      buff << QString("Bla-Bla");

      buff.device()->seek(0);
      buff << (quint16)(block.size() - sizeof(quint16));

      socket.write(block);
      socket.waitForBytesWritten(-1);

      cout << "HANDSHAKING SUCCESSFULL" << endl;
   }

------- тут всё отлично проходит

   // MAIN LOOP

   int bc = 0;

   QDataStream in(&socket);
   in.setVersion(QDataStream::Qt_4_0);

   while(true)
   {
      // READ

      quint64 baa; // Bytes Available

      while((baa = socket.bytesAvailable()) < 2*sizeof(quint32))
      {
         if (!socket.waitForReadyRead(-1)) // WAIT AND ALL
         {
            cout << "WAIT TIMEOUT" << endl;
            break;
         }
      }

      quint32 blockSize;
      in >> blockSize;

      quint32 packet;
      in >> packet;

---------------- а вот здесь приключения
----------------один/два раза весь цикл выполняется полностью и хорошо, всё чо надо приходит, но в какой-то момент здесь bytesAvaialble возвращает недостаточное число байт, и в socket перестают поступать данные

если включить не вечное ожидание, а определить интервал - это не помогает, всё равно bytesAvailable показывает недостаток данных.
      while((baa = socket.bytesAvailable()) < blockSize)
      {
         if(!socket.waitForReadyRead(-1))
         {
            //cout << "WAIT TIMEOUT" << endl;
            //break;
         }
      }

--------самая чушь в том, что если из клиента в socket плюнуть ещё хоть 1 байт, пока "сервер" в таком клинче, то всё сдвинется с мёртвой точки. Но это палиатив, проблема-то появилась в поздних версиях. И в 4.2.tp2 она тоже есть, весьма прискорбно.


      geobucket* bucket = NULL;

      switch(packet)
      {
      case imagePacket:
         bucket = readGeo(blockSize,in);
         break;
      case shutdownRenderNode:
         {
            cout << "TERMINATING EVENT OCCURED!" << endl;
            exit(0);
            return 0;
         }
      default:
         if(!flushUnknown(blockSize,in)) return NULL;
         break;
      };

      cout << "RECEIVED BUCKET: [" << bucket->dx0 << "," << bucket->dx1 << "]:[" << bucket->dy0 << "," << bucket->dy1 << "] (" <<  bucket->w << "x" << bucket->h << ")" << endl;
      cout << "BLOCKZISE: " << blockSize << endl;

      imgbucket* img = render(bucket);

      // WRITE
      QByteArray block;
      QDataStream buff(&block, QIODevice::WriteOnly);
      buff.setVersion(QDataStream::Qt_4_0);

      // PACKETSIZE
      buff << (quint32)0;

      // PACKET TYPE
      buff << (quint32)imagePacket;

      // BUCKET
      {
         buff << img->dx0 << img->dx1 << img->dy0 << img->dy1 << img->w << img->h;
      }

      int cnt = img->w*img->h;
      // C
      {
         for(int i=0;i<cnt;i++)
         {
            buff << img->C
  • ;
            buff << img->C[1];
            buff << img->C[2];
         }
      }
      // a
      {
         for(int i=0;i<cnt;i++) buff << img->a;
      }

      // SETUP PACKET
      buff.device()->seek(0);
      blockSize = block.size() - 2*sizeof(quint32);
      buff << blockSize;

      // SEND
      socket.write(block);
      socket.flush();
      socket.waitForBytesWritten(-1);

      cout << "SENT BUCKET: [" << img->dx0 << "," << img->dx1 << "]:[" << img->dy0 << "," << img->dy1 << "] (" <<  img->w << "x" << img->h << ")" << endl;
      cout << "BLOCKZISE: " << blockSize << endl;
   }

   socket.disconnectFromHost();

   socket.waitForDisconnected(3000);

   return 0;

}

geobucket* readGeo(quint32 blockSize, QDataStream&in)
{
   bucketDim dim;

   in   >> dim.dx0 >> dim.dx1
      >> dim.dy0 >> dim.dy1
      >> dim.w >> dim.h;

   geobucket* bucket = new geobucket(&dim);
   int cnt = bucket->w*bucket->h;

   bucket->P = new float3[cnt];
   for(int i=0;i<cnt;i++)
   {
      in >> bucket->P
  • ;
      in >> bucket->P[1];
      in >> bucket->P[2];
   };
   bucket->V = new float3[cnt];
   for(int i=0;i<cnt;i++)
   {
      in >> bucket->V
  • ;
      in >> bucket->V[1];
      in >> bucket->V[2];
   };

   return bucket;
};

bool flushUnknown(quint32 blockSize, QDataStream&in)
{
   unsigned char b;
   for(int i=0;i<blockSize;i++) in >> b;
   return true;
};

Что посоветуете? Может я где лох картонный?
Записан
Alqualos
Гость
« Ответ #7 : Август 10, 2006, 08:33 »

Так, а можно это в более приличном виде на laerel at yandex dot ru вместе с файлами, необходимыми для запуска этой штуковины, буде таковые найдутся? А то копипастить кучу текста из форума, комментируя в ней "комментарии", как-то не хочется, а проблема заинтересовала.

Система какая? Винда? Я смогу это проверить только в FreeBSD. Если тут баг не проявится, значит баг в винде, если винда. Что-то такое было в какой-то версии Qt, потом они его обошли, может в 4 это опять всплыло. Если так, надо будет писать им багрепорт.
Записан
Joe
Гость
« Ответ #8 : Август 10, 2006, 11:34 »

Спасибо, но у меня система - винда, xpsp2.eng, мне затеваться с другими конфессиями боюсь что недосуг, под *nix/*nux это тоже портирую, но пока мне нужен макет, который будет чо-то делать и который я могу везде предьявить. Кроме того, чтобы это всё в целости попробовать - нужно ещё кучу софта доставлять -  Maya, renderman, сомневаюсь, что оне для bsd существуют. Хотя - ыtandalone вариант клиента для bug-show я могу сделать, не большая проблема, даж сорцы дать, пректом для "сервера" тоже могу поделиться, там ничего военного тоже. Но если не будет ошибки в bsd - не факт, что её нет в depended части qt.
Кстати, компиляется разными компилятрами -  пофиг мороз, всё тожсамое.
Записан
Alqualos
Гость
« Ответ #9 : Август 10, 2006, 13:06 »

Лучше всего бы сделать две маленькие программки, только демонстрирующие этот баг. Ага, bug-show.

Кстати, если баг в Qt, есть шанс, что он пропадёт, если выключить буферизацию сокета. После чего ошибку можно будет обойти, сделав собственную буферизацию (если она вообще нужна).
Записан
Joe
Гость
« Ответ #10 : Август 10, 2006, 20:05 »

да, наверное я, при оказии, такой фич сделаю, т.к. это критичная ерунда. Неуверен, что прям щас - я решил проблему, не оч. элегантно, но охота другим заниматься.
Буферизация мне нужна, как мне кажется, и я не очень понял, что мне делать в таком случае - это будет похоже на работу с UDP что-ли? мне это нафиг не надо в таком виде
Записан
C.H.
Гость
« Ответ #11 : Август 11, 2006, 06:47 »

Цитата: "Alqualos"
Кстати, если баг в Qt, есть шанс, что он пропадёт, если выключить буферизацию сокета.


Как буферезицию в соерте выключить???
Записан
Alqualos
Гость
« Ответ #12 : Август 11, 2006, 08:33 »

connectToHost(host, port, QIODevice::ReadWrite | QIODevice::Unbuffered)

Сетевой буфер ОС при этом никуда не пропадёт, исчезнет только внутренняя буферизация Qt, а значит и её баги, если таковые имеются. То есть bytesAvailable(), например, будет возвращать количество байт, доступных в дескрипторе сокета напрямую, а не размер своего буфера.

Кстати, я вспомнил, где там был баг в винде, вот:

http://doc.trolltech.com/3.3/qsocketdevice.html#bytesAvailable

Не знаю, может это та же самая проблема в новом воплощении, а может совсем другая.
Записан
Joe
Гость
« Ответ #13 : Август 11, 2006, 12:13 »

...да, проблема похожа. Но почему она вновь тогда появилась, ведь версия 4.1.1 однозначно хорошо себя ведет, 4.1.3 - уже плохо. Значит, обойти это всё же можно, но trolltech похоже наняли нового программера, и он им всё улучшил назад Улыбающийся выглядит именно так Улыбающийся
Записан
Alqualos
Гость
« Ответ #14 : Август 11, 2006, 12:17 »

Я сравнивал код в 4.1.1 и 4.1.4 - они там, похоже, доделали какие-то фичи в сетевом модуле, которые были не до конца реализованы. Может, попутно напортачили. Правда, я смотрел редакцию X11, может в виндовой версии больше изменений. В любом случае, неплохо было бы, чтобы кто-нибудь написал им багрепорт с прилагающимся примером, демонстрирующим оный баг.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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