PDA

View Full Version : QTcpSockets and QThreads



TheRonin
20th June 2006, 16:17
I'm trying to write a multithreaded chat program based on the threaded fortune server example.

The setup:
My first step was to prevent the thread from terminating immediately, a while loop in the Thread::run() method, as shown below, did this nicely...or so i thought.



void Thread::closeConnectionNow()
{
socket->disconnectFromHost();
socket->waitForDisconnect(1);
}

void Thread::closeConnection()
{
socket->deleteLater();
}

void Thread::readData()
{
//code for reading data
}

void Thread::run()
{
...
connect(socket, SIGNAL(readyRead()), this, SLOT(readData()));
connect(socket, SIGNAL(disconnected()), this, SLOT(closeConnection()));
while (socket->state() == QAbstractSocket::ConnectedState)
socket->waitForDisconnected(-1);
}


note: The closeConnectionNow()-method is available to the thread's parent.

What works:
The thread runs, and waits in the loop while still answering readyRead() signals properly. When the client closes the connection, the disconnected() signal is received correctly and the thread exits.

What doesn't work:
Calling the closeConnectionNow()-method from the parent thread does not have any effect. It executes but the socket refuses to close.

Have i missed some critical step that would allow the thread to close the connection?

Perhaps the solution i've chosen is flawed to begin with and i should find a single-threaded solution instead?

Any input is appreciated! Thanks in advance!

jpn
20th June 2006, 19:26
What doesn't work:
Calling the closeConnectionNow()-method from the parent thread does not have any effect. It executes but the socket refuses to close.
Sounds like you are calling the function directly from another thread. This is a no no. So basically, I'm afraid you have two parallel executions going in the code above: 1) the actual thread execution is blocked in the end of the run()-method at QSocket::waitForDisconnected() and 2) another thread (where closeConnectionNow() is called from) executes closeConnectionNow().

You might want to consider entering to an event loop in the thread and using signals and slots and a queued connection instead of a direct call from another thread.

So the code should look more like this:


// create a queued connection between the thread and the parent
Thread* thread = new Thread(this);
connect(this, SIGNAL(somethingHappened()), thread, SLOT(closeConnection()), Qt::QueuedConnection);

// ..and when you would call closeConnectionNow(), now you will just emit somethingHappened() instead
// remember to declare somethingHappened() as a signal
emit somethingHappened();

// declare Thread::closeConnectionNow() as a slot
void Thread::closeConnectionNow()
{
socket->disconnectFromHost();
}

void Thread::run()
{
...
connect(socket, SIGNAL(readyRead()), this, SLOT(readData()));
connect(socket, SIGNAL(disconnected()), this, SLOT(closeConnection()));
exec(); // enter to the event loop
}

TheRonin
21st June 2006, 09:16
Thanks for the tip! I changed the structure and now it works fine! :D

Just one thing though; when i have more threads running, won't emiting somethingHappened() cause all of them to close?

jpn
21st June 2006, 09:41
Just one thing though; when i have more threads running, won't emiting somethingHappened() cause all of them to close?
Yes it will unless you map the signal somehow to the appropriate thread (check QSignalMapper (http://doc.trolltech.com/4.1/qsignalmapper) docs).
Another a bit simplier way could be to simply pass some identifier number (eg. socket descriptor) as an argument for the signal and then the thread closes connection only if the identifier matches.