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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: [4.3.1] Консольный обработчик входящих соединений - в чём проблема?  (Прочитано 12988 раз)
SubV
Гость
« : Сентябрь 05, 2007, 17:35 »

Определены массивы соединений:
Код:
struct conn 
{
QTcpSocket* connection;
QScsConnectionHandler* handler;
};

QTcpServer scs_server;
QVector<conn> connArray;
conn connCurrent;

В главном цикле крутится опрос по заданному порту:
Код:
forever
{
QTcpServer scs_server;
scs_server.listen(QHostAddress::Any, 7001);
scs_server.waitForNewConnection(500, &timedOut);

while(scs_server.hasPendingConnections())
{
connCurrent.connection = scs_server.nextPendingConnection();
connCurrent.handler = new QScsConnectionHandler(connCurrent.connection);
connArray.append(connCurrent);
connCurrent.handler->start();
}

for (int i0=0;i0<connArray.size();i0++)
{
if(!connArray.at(i0).handler->new_connection)
{
delete connArray.at(i0).handler;
connArray.remove(i0);
}
}

if(checkForExit()) break;
}

Есть заготовка класса, который будет эти соединения обрабатывать:
Код:
class QScsConnectionHandler: public QThread
{
public:
QScsConnectionHandler(QTcpSocket* n_conn)
{
new_connection = n_conn;
}
~QScsConnectionHandler()
{
}
virtual void run();
QTcpSocket* new_connection;
protected:
private:
QString tmpStr, headerStr, currentID, currentIDn;
QByteArray b0a;

};

void QScsConnectionHandler::run()
{
forever
{
// process incoming connection
std::cout << "start\n";

tmpStr = "200 OK\r\n";
b0a.clear();
b0a.append(tmpStr);

new_connection->write(b0a);
new_connection->flush();
std::cout << "stop\n";

new_connection->close();
break;
}
new_connection = NULL;
}

При любой попытке вызова функций new_connection, т.е. write(), flush() или close программа либо падает, либо выводит сообщение вида "QObject: Cannot create children for a parent that is in a different thread. (Parent is QNativeSocketEngine(0086C628), parent's thread is QThread(00869B78), current thread is QThread(0086CAF8)".

В чём проблема и как решить? Архитектуру "сигнал-слот" в данном случае использовать нежелательно по ряду причин.
Записан
WW
Гость
« Ответ #1 : Сентябрь 05, 2007, 18:52 »

Попробуй QObject::moveToThread().
Хотя может и не сработать.
Записан
denka
Гость
« Ответ #2 : Сентябрь 05, 2007, 21:07 »

А почему ты не воспользуешся ф-цией incomingConnection ( int socketDescriptor )  класса QTcpServer, как это сделанно в примере hreadedfortuneserver? Создавал бы сам QTcpSocket в run на основе полученого дескриптора и не было бы проблемы с парентом в другом потоке...
Записан
SubV
Гость
« Ответ #3 : Сентябрь 06, 2007, 15:51 »

Попробовал использовать incomingConnection и дескрипторы.
Код:
class SmSrv : public QTcpServer
{
Q_OBJECT

public:
SmSrv(QObject *parent = 0);
~SmSrv();

QVector<SmConnHandler*> cns;
void checkForInactiveConnections();

private:
protected:
void incomingConnection(int handle);
};
Код:
#include "smsrv.h"

SmSrv::SmSrv(QObject *parent)
: QTcpServer(parent)
{
listen(QHostAddress::Any, 7001);
}

SmSrv::~SmSrv()
{
}

void SmSrv::checkForInactiveConnections()
{
for (int i0=0;i0<cns.size();i0++)
if(cns.at(i0)->isFinished())
{
delete cns.at(i0);
cns.remove(i0);
}
}

void SmSrv::incomingConnection(int handle)
{
SmConnHandler* cn = new SmConnHandler(handle, this);
cns.append(cn);
cn->start();
}

Теперь проблема в том, что вызов переопределенной incomingConnection() вообще не происходит. У кого-нибудь есть мысли по этому поводу?
« Последнее редактирование: Сентябрь 06, 2007, 18:22 от SubV » Записан
BD
Гость
« Ответ #4 : Сентябрь 07, 2007, 11:33 »

Не совсем понял зачем тебе нужна структура
Код:
struct conn 
{
Когда у тебя в handler также есть ссылка на connection.
Кроме того, я так пониманию, после обработки соединения, у тебя будет снова создаваться сервер на том же порту, потому что я не вижу где ты его удаляешь // Я про первый вариант
В общем, попробуй что-то типа
Код:
	QTcpServer scs_server;
QVector<QScsConnectionHandler *> connArray;

Код:
QTcpServer scs_server;
scs_server.listen(QHostAddress::Any, 7001);
forever
    scs_server.waitForNewConnection(500, &timedOut);
    while(scs_server.hasPendingConnections())
{
                     conHandler * connCurrent  = new QScsConnectionHandler( scs_server.nextPendingConnection() );
                     connHandler -> start();
                     connArray.append( conHandler);                     
}
if(checkForExit()) break;
}

Код:
class QScsConnectionHandler: public QThread
{
public:
QScsConnectionHandler(QTcpSocket* n_conn)
{
new_connection = n_conn;
}
~QScsConnectionHandler()
{
}
virtual void run();
QTcpSocket* new_connection;
protected:
private:
QString tmpStr, headerStr, currentID, currentIDn;
QByteArray b0a;

};

void QScsConnectionHandler::run()
{
       while ( new_connection -> state!= QAbstractSocket::UnconnectedState ) {
// process incoming connection
                только тогда здесь перед close желательно использовать сначала disconnect
}
        deleteLater ();
}
Записан
SubV
Гость
« Ответ #5 : Сентябрь 07, 2007, 16:32 »

Вроде бы разобрался... причина, судя по всему - странная нелюбовь QT к созданию объектов в потоке по указателю. Привожу рабочий скелет кода.
main.cpp
Код:
#include <QtCore/QCoreApplication>
#include <QDatetime>
#include <QStringList>
#include <QVariant>
#include <iostream>
#include "qscsmisc.h"

#include "smsrv.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
SmSrv srv;
while(srv.isListening())
{
a.processEvents();
srv.checkForInactiveConnections();
if(checkForExit()) break;
}

return 0; /*a.exec();*/
}

smsrv.h
Код:
#ifndef SMSRV_H
#define SMSRV_H

#include <QObject>
#include <QtNetwork>
#include <QtSql>
#include <QThread>
#include <QTcpSocket>
#include "smconnhandler.h"

class SmSrv : public QTcpServer
{
Q_OBJECT

public:
SmSrv(QObject *parent = 0);
~SmSrv();

QVector<SmConnHandler*> cns;
void checkForInactiveConnections();

private:
protected:
void incomingConnection(int handle);
};

#endif // SMSRV_H

smsrv.cpp
Код:
#include "smsrv.h"

SmSrv::SmSrv(QObject *parent)
: QTcpServer(parent)
{
listen(QHostAddress::Any, 7001);
}

SmSrv::~SmSrv()
{
}

void SmSrv::checkForInactiveConnections()
{
for (int i0=0;i0<cns.size();i0++)
if(cns.at(i0)->isFinished())
{
delete cns.at(i0);
cns.remove(i0);
}
}

void SmSrv::incomingConnection(int handle)
{
std::cout << "+1\n";
SmConnHandler* cn = new SmConnHandler(handle, this);
cns.append(cn);
cn->start();
}

smсonnhandler.h
Код:
#ifndef SMCONNHANDLER_H
#define SMCONNHANDLER_H

#include <QThread>
#include <QTcpSocket>
#include <iostream>
#include <stdlib.h>

class SmConnHandler : public QThread
{
Q_OBJECT

public:
SmConnHandler(int socketDescriptor, QObject *parent);
~SmConnHandler();
void run();

QTcpSocket* new_connection;
int sDescr;
private:

};

#endif // SMCONNHANDLER_H

smсonnhandler.cpp
Код:
#include "smconnhandler.h"

SmConnHandler::SmConnHandler(int socketDescriptor, QObject *parent)
: QThread(parent)
{
sDescr = socketDescriptor;
}

SmConnHandler::~SmConnHandler()
{
}

void SmConnHandler::run()
{
QByteArray b0a;
QString tmpStr, headerStr, currentID, currentIDn;
//new_connection = new QTcpSocket; -- хотя и работает, но выдает ошибку, см. ниже.
QTcpSocket new_connection;
new_connection.setSocketDescriptor(sDescr);
forever
{
std::cout << "start\n";
tmpStr = "200 OK\r\n";

b0a.clear();
b0a.append(tmpStr);

//new_connection->write(b0a);
                //если обратиться к write() по указателю, будет ошибка
                //QObject: Cannot create children for a parent that is in a different thread. (Parent is
                //QNativeSocketEngine(0086C4A0), parent's thread is QThread(00865828), current thread is
                //SmConnHandler(0086B990)

new_connection.write(b0a);
new_connection.flush();

std::cout << "stop\n";

new_connection.close();
break;
}
}
« Последнее редактирование: Сентябрь 07, 2007, 16:36 от SubV » Записан
denka
Гость
« Ответ #6 : Сентябрь 07, 2007, 17:23 »

Проблема не в том что Qt не любит указателию Скорей всего ты создавал новый объект так:
Код:
QTcpSocket * new_connection = new QTcpSocket(this);
А так нельзя так как экземпляр SmConnHandler создавался в другом потоке... у тебя в начале была похожая ошибка...
Относительно твоего первого примера:
Цитировать
QObject is reentrant. Most of its non-GUI subclasses, such as QTimer, QTcpSocket, QUdpSocket, QHttp, QFtp, and QProcess, are also reentrant, making it possible to use these classes from multiple threads simultaneously. Note that these classes are designed to be created and used from within a single thread; creating an object in one thread and calling its functions from another thread is not guaranteed to work.
Относительно последнего:
Цитировать
The child of a QObject must always be created in the thread where the parent was created. This implies, among other things, that you should never pass the QThread object (this) as the parent of an object created in the thread (since the QThread object itself was created in another thread).
Т.е. зделаеш так:
Код:
QTcpSocket new_connection(this);
получиш те же яйца тока без указателя... Так что указатели тут не причем Улыбающийся
« Последнее редактирование: Сентябрь 07, 2007, 17:25 от den'ka » Записан
SubV
Гость
« Ответ #7 : Сентябрь 07, 2007, 18:24 »

den'ka, я в курсе насчёт многопоточности наследников QObject. Всё дело в том, что работают оба варианта - как с созданием QTcpSocket через указатель, так и напрямую. Фишка в том, что второй вариант не ругается при выполнении.

Предупреждение "Cannot create children for a parent that is in a different thread. (Parent is QNativeSocketEngine..." выводится в консоль, поэтому вероятно, многие, кто пишет GUI просто его не наблюдали.
Записан
denka
Гость
« Ответ #8 : Сентябрь 08, 2007, 08:46 »

Да?  Улыбающийся Или я тебя не правильно понял или ты говориш что есть разница между тем как создаеться объект в стеке или в куче... Тогда объясни вот что... Код:
Код:
QTcpSocket new_connection(this);
Вызывает это самое сообщение о ктором ты говориш, а код:
Код:
QTcpSocket * new_connection = new QTcpSocket;
Не вызывает... Еще раз повторюсь дело не в том как создаеться объект а в том что ты указываеш парента который создан в другом потоке.
Записан
SubV
Гость
« Ответ #9 : Сентябрь 10, 2007, 16:16 »

...или ты говориш что есть разница между тем как создаеться объект в стеке или в куче... Тогда объясни вот что... Код:
Код:
QTcpSocket new_connection; // без (this);
Вызывает это самое сообщение о ктором ты говориш, а код:
Код:
QTcpSocket * new_connection = new QTcpSocket;
Не вызывает... Еще раз повторюсь дело не в том как создаеться объект а в том что ты указываеш парента который создан в другом потоке.

Как раз первый вариант НЕ ругается. А второй, т.е. при выделении памяти в хипе, отрабатывает нормально, НО выводит в консоль сообщение об ошибке, приведенное выше. Никакого парента, созданного в другом потоке, я не указываю. Очевидно, это одна из "фич" QT, с которой нужно просто смириться.

Еще раз скажу, что оба варианта имеют одинаковую функциональность, т.е. сокет содается, приём/передача данных работают нормально.
Записан
denka
Гость
« Ответ #10 : Сентябрь 10, 2007, 16:46 »

Тогда объясни мне почему у меня не пишет этого сообщения? Какая может быть фича? По твоему троли каким-то образом проверяют как создаеться объект? Невериш мне так хотя бы поверь своим глазам и размышляй логически. Тебе в консоль что выводит? Что дочерный объект в разных потоках с парентом... Это как могло произойти? Только когда создаеш объект и указываеш парента из другого потока или когда сам явно устанавливаеш
Записан
SubV
Гость
« Ответ #11 : Сентябрь 10, 2007, 19:12 »

Вот, у человека была та же проблема. См. его отладочный лог.

http://www.qtcentre.org/forum/f-qt-programming-2/t-threaded-sever-problem-4384.html

Внятного объяснения так и не нашлось.
Записан
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #12 : Сентябрь 10, 2007, 22:27 »

Решил отписаться, но забераю свои слова назад. Добавлю лишь одно: способ размещения объекта (стек или хип) точно не причём. Сделал тестовый примерчик (в точности как у тебя), никаких сообщений не получил в консоль. Все работает как надо. имхо, проблема кроеться в другом месте. В тесте выполнял подключение к серверу 100 раз в цикле. Qt 4.2.3/4.3.0 VS2005+SP1. Сегодня качну 4.3.1 проверю на ней, но думаю результат останется прежним.

Попробуйте пройтись дебагером в самой Qt и посмотреть что к чему.
« Последнее редактирование: Сентябрь 11, 2007, 12:22 от pastor » Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
SubV
Гость
« Ответ #13 : Сентябрь 11, 2007, 14:50 »

У меня QT 4.2.3/4.3.0/4.3.1, VS2003 + SP1.

Неужели дело в компиляторе? Впрочем, создание сокета в стеке на данный момент меня устраивает. Так что проехали..
Записан
denka
Гость
« Ответ #14 : Сентябрь 11, 2007, 15:22 »

Я тут покапался в исходниках Qt. Вот тебе последовательность вызовов конструкторов для QTcpSocket с их исходниками...

Код:
QTcpSocket::QTcpSocket(QObject *parent)
    : QAbstractSocket(TcpSocket, *new QTcpSocketPrivate, parent)
{
#if defined(QTCPSOCKET_DEBUG)
    qDebug("QTcpSocket::QTcpSocket()");
#endif
    d_func()->isBuffered = true;
}

QAbstractSocket::QAbstractSocket(SocketType socketType,
                                 QAbstractSocketPrivate &dd, QObject *parent)
    : QIODevice(dd, parent)
{
    Q_D(QAbstractSocket);
#if defined(QABSTRACTSOCKET_DEBUG)
    qDebug("QAbstractSocket::QAbstractSocket(%sSocket, QAbstractSocketPrivate == %p, parent == %p)",
           socketType == TcpSocket ? "Tcp" : socketType == UdpSocket
           ? "Udp" : "Unknown", &dd, parent);
#endif
    d->socketType = socketType;
}

QIODevice::QIODevice(QIODevicePrivate &dd, QObject *parent)
    : QObject(dd, parent)
{
}

QObject::QObject(QObjectPrivate &dd, QObject *parent)
    : d_ptr(&dd)
{
    Q_D(QObject);
    ::qt_addObject(d_ptr->q_ptr = this);
    d->threadData = QThreadData::current();
    d->threadData->ref();
    if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))
        parent = 0;
    if (d->isWidget) {
        if (parent) {
            d->parent = parent;
            d->parent->d_func()->children.append(this);
        }
        // no events sent here, this is done at the end of the QWidget constructor
    } else {
        setParent(parent);
    }
}

static bool check_parent_thread(QObject *parent,
                                QThreadData *parentThreadData,
                                QThreadData *currentThreadData)
{
    if (parent && parentThreadData != currentThreadData) {
        QThread *parentThread = parentThreadData->thread;
        QThread *currentThread = currentThreadData->thread;
        qWarning("QObject: Cannot create children for a parent that is in a different thread.\n"
                 "(Parent is %s(%p), parent's thread is %s(%p), current thread is %s(%p)",
                 parent->metaObject()->className(),
                 parent,
                 parentThread ? parentThread->metaObject()->className() : "QThread",
                 parentThread,
                 currentThread ? currentThread->metaObject()->className() : "QThread",
                 currentThread);
        return false;
    }
    return true;
}

как видиш меседж идет из ф-ции check_parent_thread  и только в том случае если parent не равен 0 он выводиться Улыбающийся

З.Ы. Думаю в твоем случае это не компилятор, скорей погода или магнитная буря... Тут нужен хороший танец с бубнами Подмигивающий
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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