Russian Qt Forum

Qt => Работа с сетью => Тема начата: Vladimir от Октябрь 18, 2011, 10:56



Название: Завершение серверного потока
Отправлено: Vladimir от Октябрь 18, 2011, 10:56
Доброго дня! Проблема в следующем..
Создаю на каждого подключаемого клиента свой поток
Код:
void FortuneServer::incomingConnection(int socketDescriptor)
 {
     
     QString fortune = fortunes.at(qrand() % fortunes.size());
     FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this);
     connect(thread, SIGNAL(msgToGUI(QString,int)), SIGNAL(MessageS(QString,int)));
     
     connect(thread, SIGNAL(finished()), SLOT(deleteLater()));

     thread->start();
 }

Функция потока:
Код:
void FortuneThread::run()
 {
     tcpSocket = new QTcpSocket(this); // для каждого подключенного клиента свой СОКЕТ

     if(!tcpSocket->setSocketDescriptor(socketDescriptor)) {
        emit error(tcpSocket->error());
        emit MessageClient(tcpSocket, "<b>SERVER:</b>Error to initialization Socket!",2);
}
 
while(true)
{
             if(tcpSocket->state() == QAbstractSocket::UnconnectedState)
                     {
        tcpSocket->disconnectFromHost();
                tcpSocket->waitForDisconnected();
return;                                           // ??????!!!!!!!!!!         
       }

if(tcpSocket->waitForReadyRead(3000))
     {
// .... принимаем данные от клиента

     } // waitForReadyRead()

} // while()
}

Собственно вопрос как завершить правильно поток, если клиент отсоединился??! Ибо сейчас, если клиент отсоединился он больше не подключается (не вызывается incomingConnection) ! Если закоментить return, то клиент подключается, но начинает грузиться CPU!


Название: Re: Завершение серверного потока
Отправлено: serega-5508 от Октябрь 18, 2011, 20:54
написать функцию проверки соединения. если отключён, то завершать поток. простая проверка при помощи таймера делается.


Название: Re: Завершение серверного потока
Отправлено: andrew.k от Октябрь 19, 2011, 02:25
Код
C++ (Qt)
connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
 
Ты удаляешь не поток, а FortuneServer.
Когда ты комментируешь ретурн, поток просто не может завершиться и поэтом сервер продолжает жить и плодить бессметные потоки.
Правильно:
Код
C++ (Qt)
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
 
Это вообще странный кусок. Первые две строки блока не нужны совершенно. Сокет и так отключен уже.
Код
C++ (Qt)
if(tcpSocket->state() == QAbstractSocket::UnconnectedState)
                    {
       tcpSocket->disconnectFromHost();
               tcpSocket->waitForDisconnected();
return;                                           // ??????!!!!!!!!!!          
    }
 

А вообще ты можешь ловить от сокета сигнал disconnected() но тогда надо делать через exec() внутри run()

К тому же возможно после вызова waitForReadyRead, если произойдет разрыв соединения, не факт, что управление вернется сразу, а по таймауту, т.е. через 3 секунды. Кто знает достоверно прокомментируйте.


Название: Re: Завершение серверного потока
Отправлено: Vladimir от Октябрь 19, 2011, 11:27
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

Действительно это решило проблему! Огромное спасибо, andrew.k =)

Вроде все работает как и задумывалось.. Клиенты нормально подключаются в отдельных потоках и передают файлы на сервер, нормально и отключаются..! Теперь стоит вопрос в скорости и надежности, а именно:
1. Обнаруживать, что клиент отключился максимально быстро, даже в момент передачи файла и завершить корректно поток, связанный с этим клиентом! После определенного тестирования возникает такая проблемка (не всегда, но пару раз вылетала программа!!!)..
Если скажем 3 клиента передают одновременно файлы большого размера и в момент передачи закрыть одного из клиентов, то при завершении работы сервера вылитала ошибка в файле QObject.cpp:
Код:
QTread *QObject::thread() const
{
   return d_func()->threadData->thread;  // на этой строке вылетала ошибка !!!
}


Чтобы это значило????

2. Принимать файлы большого размера (около 200 Мб) также как можно быстрее! Если есть идеи как можно оптимизировать этот код, подскажите пожалуйста!

Код:
void FortuneThread::run()
 {
     headl objHead;
     IDThr = (int)currentThreadId();

     tcpSocket = new QTcpSocket(this);

     if(!tcpSocket->setSocketDescriptor(socketDescriptor)) {
        emit error(tcpSocket->error());
        emit MessageClient(tcpSocket, "<b>SERVER:</b>Error to initialization Socket!",2);
}
    
        emit MessageClient(tcpSocket, "<b>SERVER:</b>Client was connected!",2);
   emit msgToGUI(QTime::currentTime().toString()+" <b>Client</b> " + QString().setNum(numCl) + " CONNECTED!",2);

   QFile file("MatrixData_" + QString().setNum(IDThr));
   if(file.open(QIODevice::WriteOnly))
    {
while(true)
{
if(tcpSocket->state() == QAbstractSocket::UnconnectedState) {
 delete tcpSocket;
 emit msgToGUI("<b>Client</b> DISCONNECTED!",2);
 
 return;
}

if(tcpSocket->waitForReadyRead(1000))
    {
 if(!flag) // false ; чтение заголовка
 {  
timer.start();
    retrRead = tcpSocket->read((char*)&objHead,sizeof(objHead)); // читаем переданную стуктуру!                                

if( retrRead == -1)  return;
if( retrRead ==  0)  return;
  }
 
     
      if(tcpSocket->bytesAvailable() != objHead.sizeData)
  {
    flag = true;
    continue;
  }

  file.write(tcpSocket->read(objHead.sizeData));

  emit MessageClient(tcpSocket, "<b>\nSize file: </b>" + QString().setNum(objHead.sizeData) + " Byte", 2);
  emit msgToGUI(QTime::currentTime().toString()+" <b>File is sent - </b>" + QString().setNum(timer.elapsed(),10) + " ms",2);
                          
  flag = false;
  file.close();
 
 
} // waitForReadyRead()
} // while()
       }
        else { // если нельзя прочитать файл
 msgToGUI("<b>Error</b> of the determination of the file!",2);
 return;
      }
}


Название: Re: Завершение серверного потока
Отправлено: andrew.k от Октябрь 19, 2011, 20:20
Сделал бы через eventLoop проще бы было и понятней.

Может местами поменять эти строчки? Не рано ли удалил.
Код
C++ (Qt)
delete tcpSocket;
emit msgToGUI("<b>Client</b> DISCONNECTED!",2);


Название: Re: Завершение серверного потока
Отправлено: Vladimir от Октябрь 20, 2011, 10:34
Сигнал msgToGUI() в принципе не связан с сокетом.. он просто выводит сообщение о дисконекте клиента на форму сервера!Но, если поменять ошибка все равно иногда проскакивает..  :-\ и именно, когда закрываешь приложение сервера, а не вовремя отключения/подключения клиентов или передаче файлов..! ???

// файл QObgect.cpp
QTread *QObject::thread() const
{
   return d_func()->threadData->thread;  // на этой строке вылетала ошибка !!!
}


Название: Re: Завершение серверного потока
Отправлено: andrew.k от Октябрь 20, 2011, 10:42
Неизвестно как у тебя там все организовано. Видимо ты все-таки где-то удаляешь раньше чем нужно.

Кстати
Код
C++ (Qt)
tcpSocket = new QTcpSocket(this);
Убери this. Он не нужен. Вдруг поможет.)


Название: Re: Завершение серверного потока
Отправлено: Vladimir от Октябрь 20, 2011, 11:20
К сожалению не помогло..Даже наоборот, если убрать this, при закрытии приложения процесс остается висеть и грузить дико проц! Кину весь файл, глянь пожалуйста, может заметишь где собака зарыта)

Код:
FortuneServer::FortuneServer() : QTcpServer()
 {
     fortunes << tr("You've been leading a dog's life. Stay off the furniture.")
              << tr("You've got to think about tomorrow.")
              << tr("You will be surprised by a loud noise.")
              << tr("You will feel hungry again in another hour.")
              << tr("You might have mail.")
              << tr("You cannot kill time without injuring eternity.")
              << tr("Computers are not intelligent. They only think they are.");
 
numCl = 0;
 }

bool FortuneServer::InitializeServer(int port)
{
QString nP;
nP.setNum(port);
nPort = port;
if (!listen(QHostAddress::Any, nPort)) {
           QMessageBox::critical(0,"Server Error","Unable to start the server");
           return false;
          }
emit MessageS("TCP-Server is listen <b>Port</b> " + nP,2);
return true;
}


void FortuneServer::incomingConnection(int socketDescriptor)
 {
    
QString fortune = fortunes.at(qrand() % fortunes.size());
     FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this);
connect(thread, SIGNAL(msgToGUI(QString,int)), SIGNAL(MessageS(QString,int)));
thread->numCl = ++numCl;
     connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

     thread->start();
 }

// ================================= Thread() ============================================================
// конструктор !
FortuneThread::FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent)
     : QThread(parent), socketDescriptor(socketDescriptor), text(fortune), tcpSocket(NULL)
{
     connect(this, SIGNAL(MessageClient(QTcpSocket*,QString,int)),SLOT(sendToClient(QTcpSocket*,QString,int)));
flag = false;
numCl = 0;
}

void FortuneThread::run()
 {
headl objHead;
     IDThr = (int)currentThreadId();

     tcpSocket = new QTcpSocket(this);

     if(!tcpSocket->setSocketDescriptor(socketDescriptor)) {
        emit error(tcpSocket->error());
        emit MessageClient(tcpSocket, "<b>SERVER:</b>Error to initialization Socket!",2);
}
    
        emit MessageClient(tcpSocket, "<b>SERVER:</b>Client was connected!",2);
   emit msgToGUI(QTime::currentTime().toString()+" <b>Client</b> " + QString().setNum(numCl) + " CONNECTED!",2);


while(!tcpSocket->waitForReadyRead(1))
 {
     if(tcpSocket->state() == QAbstractSocket::UnconnectedState) {
   emit msgToGUI("<b>Client</b> DISCONNECTED!",2);
 delete tcpSocket;
 
 return;
  }
 }

QFile file("MatrixData_" + QString().setNum(IDThr));
if(file.open(QIODevice::WriteOnly))
 {
   while(true)
    {
 
  if(tcpSocket->state() == QAbstractSocket::UnconnectedState) {
     if(file.isOpen()) file.close();
 emit msgToGUI("<b>Client</b> DISCONNECTED!",2);
 delete tcpSocket;
   
 return;
  }

  if(tcpSocket->waitForReadyRead(1000))
    {
   if(!flag) // false
    {  
timer.start();
    retrRead = tcpSocket->read((char*)&objHead,sizeof(objHead)); // читаем переданную стуктуру!                                

if( retrRead == -1)  return;
if( retrRead ==  0)  return;
    }

   if(tcpSocket->bytesAvailable() != objHead.sizeData)
{
  flag = true;
      continue;
    }

   file.write(tcpSocket->read(objHead.sizeData));

   emit MessageClient(tcpSocket, "<b>\nSize file: </b>" + QString().setNum(objHead.sizeData) + " Byte", 2);
emit msgToGUI(QTime::currentTime().toString()+" <b>File is sent - </b>" +  QString().setNum(timer.elapsed(),10) + " ms",2);
                          
flag = false;
file.close();
 
  } // waitForReadyRead()
   } // while()
     } // if() file.open()
else
{   // если нельзя открыть файл
    msgToGUI("<b>Error</b> of the determination of the file!",2);
    return;
}
} // run()

void FortuneThread::sendToClient(QTcpSocket* pSocket, QString str, int num) // отправить Клиенту
{
    QByteArray  arrBlock;

headl objHeadSendCl;
objHeadSendCl.sizeData = str.size();

arrBlock.append((const char*)&objHeadSendCl, sizeof(objHeadSendCl));
arrBlock.append(str);
  
    pSocket->write(arrBlock);
}

Главная форма:

Код:
FormServ::FormServ(QWidget *parent) : QDialog(parent)
{
   setupUi(this); // инициализация формы

   editServ->setFocus();
   QRegExp regExp("[0-9]{1,5}"); // ограниченный ввод данных
   editServ->setValidator(new QRegExpValidator(regExp,this));
  
   connect(butServ, SIGNAL(clicked()), SLOT(lineEd_textPORT()));
  
   m_Serv = new FortuneServer;
  
   connect(m_Serv, SIGNAL(MessageS(QString,int)), SLOT(slotShowMessage(QString,int)));
};

void FormServ::lineEd_textPORT()
{
 bool ok;
      m_Serv->InitializeServer(editServ->text().toInt(&ok,10));
}

void FormServ::slotShowMessage(QString mess,int id)
{
if(id == 2) textEditServ->append(mess);
    
}


Название: Re: Завершение серверного потока
Отправлено: andrew.k от Октябрь 20, 2011, 11:35
Слушай, это же пример из ассистанта?
Как ты умудрился его сломать?)
Threaded Fortune Server (http://doc.qt.nokia.com/stable/network-threadedfortuneserver.html)
Сравни с оригиналом. Найди ошибку.


Название: Re: Завершение серверного потока
Отправлено: Vladimir от Октябрь 20, 2011, 11:43
Так да оттуда, не хочется изобретать велосипед!) то что там описано: подключение клиентов в отдельном потоке реализовано и у меня, и это работает! Я же ничего не менял глобально.. а вот отключение клиентов и завершение работы этих потоков там не приводится, писал сам! Может в этом проблема.. Что вообще эта ошибка означает??


Название: Re: Завершение серверного потока
Отправлено: andrew.k от Октябрь 20, 2011, 11:45
Дико грузит проц думаю из-за этого.

Код
C++ (Qt)
while(!tcpSocket->waitForReadyRead(1))


Название: Re: Завершение серверного потока
Отправлено: Vladimir от Октябрь 20, 2011, 11:50
Дико грузит проц думаю из-за этого.

Код
C++ (Qt)
while(!tcpSocket->waitForReadyRead(1))

Да вроде вообще не грузит (1%).. Согласен может и не самое удачное решение (другого не нашел).. иначе бы сразу создавался файл при подключении, даже без передачи, а это тоже не есть правильно! Или имеешь ввиду после закрытия приложения, но предполагается, что все потоки должны быть к этому времени завершены..


Название: Re: Завершение серверного потока
Отправлено: masherbober от Январь 27, 2013, 00:20
Бодрого времени суток! Запускаю проект threaded fortune server и fortune client, клиент находит IP, который выдает сервер, но при подключении к порту выдает ошибку - Connection timed out - что нужно настроить, подскажите пожалуйста!