Название: Завершение серверного потока
Отправлено: 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 - что нужно настроить, подскажите пожалуйста!
|