PDA

View Full Version : Socket + thread problem



Daxos
12th July 2010, 11:03
Hi,
I have a multithread application with 1 server and multiple client.

This is my QTCode:



#ifndef MESSAGETHREAD_H
#define MESSAGETHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QTcpServer>

class MessageThread : public QThread
{
Q_OBJECT

public:
MessageThread();

void run();

public slots:
void newMessConnection();

signals:
void error(QTcpSocket::SocketError socketError);

private:
QTcpServer* messageSrv;
QString text;
};

#endif


Implementation:



#include "MessageThread.h"

#include <QtNetwork>


#define IPADDRESS "192.168.1.160"

MessageThread::MessageThread()
{
}

void MessageThread::run()
{
QTcpServer* messageServer = new QTcpServer;
messageSrv = messageServer;
connect(messageServer, SIGNAL(newConnection()), this, SLOT(newMessConnection()));

QHostAddress xIpAddress;
quint16 port;

xIpAddress.setAddress(IPADDRESS);
port = 2223;

if(!messageServer->listen(xIpAddress, port))
{
printf("\n Communication Manager.d: Unable to start the server.");
messageServer->close();
}

if (messageServer->isListening())
{
printf("\n Message Server initialized");
}
exec();


}

void MessageThread::newMessConnection()
{
printf("\n Message server: send data....");
QTcpSocket* connection = messageSrv->nextPendingConnection();
QString test = "test data";

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << test;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
connection->write(block);

}



When I create this thread the line "connection->write(block);" show me this error:

QObject: Cannot create children for a parent that is in a different thread.
Parent's thread is MessageThread, current thread is QThread.


How i can solve it?
Can anyone help me please?

Bye

tbscope
12th July 2010, 11:21
Step 1: do not use threading for socket communication unless you absolutely need to use blocking functions.
Why do you think you need threads?

Ok, now what is wrong with your code. At first sight nothing, but in the background there's a huge problem.
Let's break up your code:


class MessageThread : public QThread
{
...
};

Your MessageThread class is a subclass of QThread.
A QThread subclass is NOT a new thread. Any MessageThread object lives in the main GUI thread. Keep that in mind for the next lines of code.



public slots:
void newMessConnection();

signals:
void error(QTcpSocket::SocketError socketError);


These signals and slots, which you define in your QThread subclass are emitted and called inside the main GUI thread since the QThread object lives in the main gui thread. Try to understand this when we look at the next few lines of code.


void MessageThread::run()
{
QTcpServer* messageServer = new QTcpServer;
messageSrv = messageServer;
connect(messageServer, SIGNAL(newConnection()), this, SLOT(newMessConnection()));
}

The code in the run() function is being run in a separate thread. So far that's a good idea. You define a new QTcpServer object inside this new thread. So far that's also good. You might want to write messageSrv = new QTcpServer directly though. The problems begin with the connection of the signal and the slot.

The signal newConnection() is being emitted in another thread than the slot newMessConnection(). The slot newMessConnection lives in the main gui thread. Ok, so far that's not a big problem as you want to have some feedback to your main thread.

However, let's look what happens in the slot itself:


void MessageThread::newMessConnection()
{
QTcpSocket* connection = messageSrv->nextPendingConnection();
}

The connection object is a huge problem. Remember, as I already mentioned several times, the newMessConnection lives in the main gui thread!!!. BUT, messageSrv points to an object in another thread!!!
This is an error!.

This is the exact reason why you get the following error:

QObject: Cannot create children for a parent that is in a different thread.

Solution, in pseudo code:



class MyConnection : public QObject
{
Q_OBJECT

public slots:
void newConnection();

signals:
void error();
};

Then when you use your connection:

MyConnection *connection = new MyConnection;
connect(connection, SIGNAL(error()), this, SLOT(handleError()));

QThread *connectionThread = new QThread;

connection->moveToThread(connectionThread);


Edit: Since this question gets asked a lot and since the Qt documentation, at the moment, isn't very clear on this complexity, I'll write a wiki tutorial on threading and sockets this week.

Daxos
12th July 2010, 11:44
This is my pboject:

suppose that I have some different application A, B, C....
A is the main programm, run and listening in a specific port.

B connect to A. I want than A can be able to communicate with B and receive some information about B. Then I want that I can be able to create another channel of communication (a new socket that work in another port that A tell to B).

But at the same time I want that A can be able to receive other request of connections for example from C o D.

What do you think about this architecture? Is correct use some different thread to the registration step e one different thread to communicate??

Bye, Thanks

borisbn
12th July 2010, 12:13
Answering for your first question: you can simply do this:


MessageThread::MessageThread()
{
moveToThread( this );
}

tbscope
12th July 2010, 12:17
No, DO NOT do what the previous poster said!!!

wysota
12th July 2010, 13:22
Answering for your first question: you can simply do this:


MessageThread::MessageThread()
{
moveToThread( this );
}


Tell me, what will happen with the following code:


class Thread : public QThread {
Thread() : QThread() {
moveToThread(this);
}
protected:
void customEvent(QEvent *e) {
qDebug() << "Custom event" << QThread::currentThreadId ();
}
};
//...
Thread thread;
QApplication::postEvent(&thread, new QEvent(QEvent::User));
// assuming no thread.start() anywhere here

Will the custom event be delivered or not? And if it will then in context of which thread will it execute?

borisbn
12th July 2010, 13:24
Easy, easy, tbscope... :)
Why not ? I did moveToThread( this ) and it worked. Please say, what is wrong with that ?
Thanks.

franz
12th July 2010, 13:48
http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong

borisbn
12th July 2010, 13:56
// assuming no thread.start() anywhere here

ok, if you will not start the thread, then you will not process events in it. In your example ( but without moveToThread ) events will be processed in main thread, but topic starter ( if I understand correctly ) wanted to process events in separate thread. To do this hi should move objects, that generates events, to that tread. I suggested to move whole object Thread to thread. In this way all it's children will be it thread.

tbscope
12th July 2010, 14:41
Easy, easy, tbscope... :)

Ohh, sorry, I didn't want to insult you.
Text messages are notoriously difficult to get the emotions from.

It's documented that it is wrong to use moveToThread on the QThread, and if you think about it a little bit you know why it doesn't make sense to do so. See the posts above.

You are correct though that the whole object needs to be moved to another thread. But only the object, not QThread.
Therefor you need to create your own object and then use moveToThread on that object to move it to another thread.
QThread needs to live in the main thread and act as a translator/manager/whatever... between the main thread and the object in the other thread.

wysota
12th July 2010, 14:58
ok, if you will not start the thread, then you will not process events in it. In your example ( but without moveToThread ) events will be processed in main thread, but topic starter ( if I understand correctly ) wanted to process events in separate thread. To do this hi should move objects, that generates events, to that tread. I suggested to move whole object Thread to thread. In this way all it's children will be it thread.

There are two things here:
1. Why move the QThread object to another thread instead of creating a separate object and moving THAT to the thread or better yet creating that object in the thread itself?
2. Why move the QThread object to another thread before the thread is actuallys started?

There is also point 3:
- Why use threads here at all?

Daxos
13th July 2010, 11:28
Ok, I think that I have solve my problem. I have use the first replay of tbscope that is the same that I have read here:

http://labs.trolltech.com/blogs/2010...doing-it-wrong

I think that thread's implementations isn't clear, for example if you read " Threaded Fortune Server Example" you can see that FortuneServer subclass QThread and it can be create confusion.

thanks all fro the replay,

See you, Dax