Russian Qt Forum

Qt => Работа с сетью => Тема начата: demaker от Апрель 22, 2011, 11:02



Название: Работа с сетью
Отправлено: demaker от Апрель 22, 2011, 11:02
Помогите ??? Выдается ошибка - хочу написать сервер и чтобы он начинал работать по нажатию кнопки, при этом вынес сервер в отдельный поток.
Код:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x9dae6e8), parent's thread is MyServerThread(0x9c47180), current thread is QThread(0x9bfc838)
Вот код:

MyServer.CCP
Код
C++ (Qt)
==========================================
MyServer::MyServer(QWidget *pwgt)
{
   m_ptxt = new QTextEdit;
   pb_start = new QPushButton("START");
 
   ServerThread = new MyServerThread; //QThread
 
   m_ptxt->setReadOnly(true);
 
   QVBoxLayout* pvbxLayout = new QVBoxLayout;
   pvbxLayout->addWidget(new QLabel("<H1>Server</H1>"));
   pvbxLayout->addWidget(m_ptxt);
   pvbxLayout->addWidget(pb_start);
   setLayout(pvbxLayout);
 
   connect(pb_start,SIGNAL(clicked()),ServerThread,SLOT(start()));
   connect(ServerThread,SIGNAL(signal_errorServer(QString)),m_ptxt,SLOT(append(QString)));
   connect(ServerThread,SIGNAL(signal_New_Connection(QString)),m_ptxt,SLOT(append(QString)));
}
 
MyServerThread::MyServerThread()
{
   m_nNextBlockSize = 0;
   nPort = 502;
 
   m_ptcpServer = new QTcpServer;
   m_ptcpServer->moveToThread(this);
}
 
void MyServerThread::run()//(int nPort, QWidget* pwgt /*=0*/): QWidget(pwgt),m_nNextBlockSize(0)
{
 
   /* OBRABOTKA OSHIBKI SOEDINENIA */
   if(!m_ptcpServer->listen(QHostAddress::Any, nPort))
   {
       emit signal_errorServer(m_ptcpServer->errorString());
       m_ptcpServer->close();
       return;
   }
   connect(m_ptcpServer, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));*/
   exec();
}
 
/*virtual*/
void MyServerThread::slotNewConnection()
{
   QTcpSocket* pClientSocket = m_ptcpServer->nextPendingConnection();
   connect(pClientSocket, SIGNAL(disconnected()), pClientSocket, SLOT(deleteLater()) );
   connect(pClientSocket, SIGNAL(readyRead()), this, SLOT(slotReadClient()) );
   emit signal_New_Connection("New connection");
}
 
void MyServerThread::slotReadClient()
{
       QTcpSocket* pClientSocket = (QTcpSocket*)sender();
       Buffer_IN.clear();
       while( pClientSocket->bytesAvailable() )
             Buffer_IN += pClientSocket->readAll();
 
      CheckUp_Answer();
      sendToClient(pClientSocket, Buffer_OUT);
}
 
void MyServerThread::sendToClient(QTcpSocket * pSocket, const QByteArray &arOut)
{
   pSocket->write(arOut);
}
 
void MyServerThread::CheckUp_Answer()
{
           for(int i = 1;i <= 200 ;i++)
               {
                   Buffer_OUT[i+8] = 2*i;
               }
}
 
MyServer.H
Код
C++ (Qt)
======================================
class MyServerThread: public QThread
{
   Q_OBJECT
 
private:
   int nPort;
   QByteArray Buffer_IN,
              Buffer_OUT;
 
   QTcpServer * m_ptcpServer;
 
private:
   void sendToClient(QTcpSocket* pSocket, const QByteArray& arOut);
   void CheckUp_Answer();
 
public:
   MyServerThread();
   void run();
signals:
   void signal_New_Connection(QString);
   void signal_errorServer(QString);
 
public slots:
   virtual void slotNewConnection();
           void slotReadClient();
};
//**********************************************************************
class MyServer : public QWidget
{
   Q_OBJECT
 
private:
   MyServerThread *ServerThread;
 
   QTextEdit * m_ptxt;
   QPushButton *pb_start;
 
public:
   MyServer(QWidget* pwgt = 0);
};
 


Название: Re: Работа с сетью
Отправлено: blood_shadow от Апрель 22, 2011, 11:53
ты сервер переместил в другой поток:
Код
C++ (Qt)
m_ptcpServer->moveToThread(this);
 
слот slotNewConnection() вызывается в главном потоке, потому вся проблема в
ф-ции m_ptcpServer->nextPendingConnection(); она создает QTcpSocket как потомок
твоего m_ptcpServer, но пытается его создать в главном потоке, идею улавливаешь?
нельзя создавать потомка и родителя в разных потоках, о чем тебе и сказали:
Код:
QObject: Cannot create children for a parent that is in a different thread.



Название: Re: Работа с сетью
Отправлено: demaker от Апрель 22, 2011, 12:04
 ???А почему он вызвается в главном потоке, когда я создаю его в конструкторе потока MyServerThread ???
Что мне нужно сделать???


Название: Re: Работа с сетью
Отправлено: blood_shadow от Апрель 22, 2011, 12:33
???А почему он вызвается в главном потоке, когда я создаю его в конструкторе потока MyServerThread ???
Что мне нужно сделать???
MyServerThread - создается в главном потоке, это только обертка для потока, другой поток на самом деле
имеет место только в run(), то есть объект MyServerThread живет в основном потоке и слот slotNewConnection()
вызывается для объекта с основного потока
если чесно я неоч понимаю что надо делать, но можно сделать вот что:
сравни указатели this в MyServerThread и MyServerThread->thread(), если они разные то поменяешь
m_ptcpServer->moveToThread(this->thread()) и
connect(m_ptcpServer, SIGNAL(newConnection()), this->thread(), SLOT(slotNewConnection()));


Название: Re: Работа с сетью
Отправлено: demaker от Апрель 22, 2011, 12:42
 :)Сча попробую


Название: Re: Работа с сетью
Отправлено: demaker от Апрель 22, 2011, 12:46
Попробовал
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpServer(0x8e0f880), parent's thread is QThread(0x8d96838), current thread is MyServerThread(0x8de1180)
 :( ???


Название: Re: Работа с сетью
Отправлено: demaker от Апрель 22, 2011, 12:49
Наверное надо делать как в Fortune Server Thread примерах Qt ???
создать
отдельно Окно(Qwidget),Server(QTcpServer) и ServerThread(QThread) - 3 объета  ??? ???


Название: Re: Работа с сетью
Отправлено: kuzulis от Апрель 22, 2011, 13:04
Код
C++ (Qt)
MyServerThread::MyServerThread()
{
   m_nNextBlockSize = 0;
   nPort = 502;
}
 
void MyServerThread::run()//(int nPort, QWidget* pwgt /*=0*/): QWidget(pwgt),m_nNextBlockSize(0)
{
   m_ptcpServer = new QTcpServer();
 
   /* OBRABOTKA OSHIBKI SOEDINENIA */
   if(!m_ptcpServer->listen(QHostAddress::Any, nPort))
   {
       emit signal_errorServer(m_ptcpServer->errorString());
       m_ptcpServer->close();
       return;
   }
   connect(m_ptcpServer, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));*/
   exec();
}
 
 

Только вместо QTcpServer можно (нужно) сделать свой класс типа MyServer: public QTcpServer
в котором реализовать слот slotReadClient .


Код
C++ (Qt)
 
//*.h
 
class MyServer : public QTcpServer
{
...
...
 
private slots:
    void slotReadClient();
...
}
 
void MyServer::slotReadClient()
{
       QTcpSocket* pClientSocket = (QTcpSocket*)sender();
       Buffer_IN.clear();
       while( pClientSocket->bytesAvailable() )
             Buffer_IN += pClientSocket->readAll();
 
      CheckUp_Answer();
      sendToClient(pClientSocket, Buffer_OUT);
}
 

И создавать в ::run() не QTcpServer , а MyServer.

Как то так, думаю идея ясна.


Название: Re: Работа с сетью
Отправлено: mutineer от Апрель 22, 2011, 13:09
2 kuzulis
Ну и где-то еще удалять m_ptcpServer


Название: Re: Работа с сетью
Отправлено: kuzulis от Апрель 22, 2011, 13:10
Ну да, естественно.


Название: Re: Работа с сетью
Отправлено: blood_shadow от Апрель 22, 2011, 13:19
у меня появилась еще идея:
Код
C++ (Qt)
void MyServerThread::run()//(int nPort, QWidget* pwgt /*=0*/): QWidget(pwgt),m_nNextBlockSize(0)
{
   connect(m_ptcpServer, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
 
   forever {
        m_ptcpServer = new QTcpServer();
 
        {
              QMutexLocker locker(&mutex);
              if (!pendingSocket)
                   waitCondition.wait(&mutex)
              socket = m_ptcpServer.nextPendingConnection();
              pendingSocket = false;
        }
 
        /* OBRABOTKA OSHIBKI SOEDINENIA */
        if(!m_ptcpServer->listen(QHostAddress::Any, nPort))
       {
           emit signal_errorServer(m_ptcpServer->errorString());
           m_ptcpServer->close();
           return;
       }
   }
 
}
 

Код
C++ (Qt)
void MyServerThread::slotNewConnection()
{
   mutex.lock();
   pendingSocket = true;
   mutex.unlock();
   waitCondition.wakeOne();
}
 

Идея такова pendingSocket == 0(по умолчанию) waitCondition(QWaitCondition) усыпляет вторичный поток, потом когда
появляется новый сокет испускается сигнал slotNewConnection() и будиться вторичный поток waitCondition.wakeOne();
далее во втор. потоке мы получаем наш новый сокет nextPendingConnection(); доступ к переменной защищаем с помощью
мьютекса


Название: Re: Работа с сетью
Отправлено: demaker от Апрель 22, 2011, 13:39
 :)Попробуем


Название: Re: Работа с сетью
Отправлено: demaker от Апрель 25, 2011, 10:57
Спасибо,заработало  :)

А вот почему надо создавать было дочерний объет???Подскажите :)


Название: Re: Работа с сетью
Отправлено: blood_shadow от Апрель 25, 2011, 14:43
Спасибо,заработало  :)

А вот почему надо создавать было дочерний объет???Подскажите :)
потому что, объект созданный в ф-ции run создается в дочерном потоке, а сам же объект
MyServerThread создается в родительском и потому все ф-ции(слоты) вызываются в родительском
потоке для твоего MyServerThread
тут почитай - http://habrahabr.ru/blogs/qt_software/115830/ две части