PDA

View Full Version : QTcpSocket: no readyRead() signal even in Qthread event loop



R-Type
20th October 2011, 10:54
Hello,
I have to port some app from Qt3 to Qt4 and have problem with slot connected to readyRead signal. It just doesn get called at all. I thought it is because socket wrapper in app gets called from some thread without event loop. I've inherited this wrapper from QThread, moved socket to this thread as described here http://www.qtcentre.org/threads/27160-Are-event-loops-in-worker-threads-independent-of-the-main-event-loop, but it still doesn't work. I also tried to instantiate QTcpSocket in run(). Below is stripped version of socket wrapper:


class ClientSocket : public QThread
{
Q_OBJECT

public:
ClientSocket();
...
public slots:
void test();

private:
QTcpSocket* m_socket;
};

ClientSocket::ClientSocket() : QThread()
,m_socket(new QTcpSocket())
{
m_socket->moveToThread(this);
connect(m_socket, SIGNAL(readyRead()), this, SLOT(test()));
start();
}

bool ClientSocket::connectToHost(QString host, int port)
{
if (port == 0) return false;
m_socket->connectToHost(host, port);
return m_socket->waitForConnected(1000);
}

void ClientSocket::write(const char* Data,unsigned int nBytes)
{
if (m_socket && (m_socket->state() == QAbstractSocket::ConnectedState))
{
m_socket->write(Data);
m_socket->flush();
}
}

void ClientSocket::test()
{
stop();
}

void ClientSocket::run()
{
exec();
}

At some place in some thread this class is instantiated in constructor like this:


SomeClass::SomeClass()
: m_ClientSocket()
{
bool bConnected = m_ClientSocket.connectToHost("localhost", 1000);
if (bConnected)
{
m_ClientSocket.write("rtst", 4);
}
}

I test it with echo server that writes received string back. But test() slot is never called. connect() with Qt::QueuedConnection didn't change anything.
have somebody any idea why it doesn't work?

nightghost
20th October 2011, 13:17
Did you call QThread::start()? Btw why are you using a Socket inside a Thread?

R-Type
20th October 2011, 13:34
start() is called inside constructor (see source code above). Without QThread it doesn't work either. My suggestion was that I didn' get slot called because of missing event loop where ClientSocket gets instantiated. Therefore I started thread with its own event loop and moved socket to this thread. Unfortunately this way it doesn't work as well.

nightghost
20th October 2011, 13:50
Sorry, missed the start() in the contructor.

Why are you thinking, that the event handling is not working? Is the socket not instantiated inside a QApplication? Are you sure, that the socket really receives data? (Did you check it with wireshark or something else)

nix
20th October 2011, 14:28
Hi,

I have tried the source with a simple QCoreApplication and get the following message at run-time :

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x82c7ab8), parent's thread is ClientSocket(0xbfef92fc), current thread is QThread(0x82ba410)
The program is still running but I do not receive any answer too. I think your moveToThread failed, I already got this message when I was using QThread like that.

Are you sure you realy need another event loop? If you are, maybe you should use QThread in a other way, take a look to http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/.

R-Type
20th October 2011, 16:31
It took some time because Wireshark cannot catch on localhost and I had to setup another PC to test it. But Now I see that response is sent back, so socket should have this data.
@nix: Unfortunately this project is on VS2003 and I don't see qDebug() output and errors like you. I'll try to create socket diret in run(). In this case such error shouldn't occur.

Added after 10 minutes:



Are you sure you realy need another event loop? If you are, maybe you should use QThread in a other way, take a look to http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/.
If it would work without QThread event loop, I would be happy, but it doesn't. Threrfore I've tried it with QThread. May be it's also wrong.
I already read this article, but I don't move thread to itself. I move socket to this new thread and this shold be correct. But I'll try it without QThread subclassing. May be it differs...

Added after 6 minutes:



ClientSocket::ClientSocket()
:m_isRunning(false)
,m_socket(new QTcpSocket())
{
QThread* t=new QThread();
connect(m_socket, SIGNAL(readyRead()), this, SLOT(test()));
m_socket->moveToThread(t);
t->start();

This way slot also doesn't get called. And without subclassing QThread I cannot start QThread's event loop because exec() is protected.

^NyAw^
20th October 2011, 17:04
Hi,

Create the socket into the "start()" method and then call "exec()" to start the event loop of the thread. If you create the QTcpSocket into the "start()" method, this object belongs to the thread that created it, that in this moment is the new thread. Instead if you create the socket into the constructor, the object belongs to the calling thread(main thread in this case).



Unfortunately this project is on VS2003 and I don't see qDebug() output and errors like you


I don't remeber which window you have to select, but I think that "results" window shows the console output.

nix
21st October 2011, 10:07
Ok but moving the socket was not my point, I try that with your code :

main.c

#include <QtCore/QCoreApplication>
#include "clientsocket.h"

#include <QThread>
#include <QtDebug>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

ClientSocket m_ClientSocket;
QThread *thread = new QThread(&a);
thread->start();
// now we got another thread
m_ClientSocket.moveToThread(thread);

qDebug() << "Main thread is " <<QThread::currentThreadId();

bool bConnected = m_ClientSocket.connectToHost("10.33.17.37", 8000);
qDebug() << "Connexion done";
if (bConnected)
{
qDebug() << "Ask for Id";
m_ClientSocket.write("*rem\n*idn?\n", 10);
}

// For testing don't do a exec() just wait for msec
// Without the processEvent() qDebug not displaying anything but
// the threadid prove the reception is done in the other thread
thread->wait(500); // After 500ms I should have the response or I give up.
a.processEvents();
thread->quit();
thread->wait(100);
return 0;
}

clientsocket.h

#ifndef CLIENTSOCKET_H
#define CLIENTSOCKET_H

#include <QTcpSocket>

class ClientSocket: public QObject
{
Q_OBJECT

public:
ClientSocket();
bool connectToHost(QString host, int port);
void write(const char* Data,unsigned int nBytes);
//void run();
public slots:
void test();

private:
QTcpSocket* m_socket;
};


#endif // CLIENTSOCKET_H


clientsocket.c

#include "clientsocket.h"

#include <QDebug>
#include <QThread>

ClientSocket::ClientSocket() : QObject()
,m_socket(new QTcpSocket())
{
connect(m_socket, SIGNAL(readyRead()), this, SLOT(test()), Qt::QueuedConnection);
}

bool ClientSocket::connectToHost(QString host, int port)
{
if (port == 0) return false;
m_socket->connectToHost(host, port);
return m_socket->waitForConnected(1000);
}

void ClientSocket::write(const char* Data,unsigned int nBytes)
{
if (m_socket && (m_socket->state() == QAbstractSocket::ConnectedState))
{
m_socket->write(Data);
m_socket->flush();
}
}

void ClientSocket::test()
{
qDebug() << "I got something here!!!" << QThread::currentThreadId() ;
}


This is the output:

Starting /disk2/local/qt/TestDebug/TestDebug...
Main thread is 3070105296
Connexion done
Ask for Id
I got something here!!! 3068005232
/disk2/local/qt/TestDebug/TestDebug exited with code 0
There is something strange with the qDebug in the second thread, it will display nothing until we call a processEvent() but the thread Id displayed in the readyRead connected slot proves that it's the second thread :).

I don't know if you can use it like this in your app but It seems to work fine for me except that I can explain the strange behavior with the qDebug in the second thread.
(You have to take care of the cross thread call in a second time)

Hope it can help

R-Type
21st October 2011, 12:22
I don't remeber which window you have to select, but I think that "results" window shows the console output.
Output comes in Output window (below the editor), but it seems for GUI app it doesn't work or shows stdout only, without stderr.

Added after 14 minutes:

@nix: I tried your version, but readyRead still didn't get called in my case. May be it has something to do with fact that it's all is in DLL. But thanks anyway!

Added after 9 minutes:



Create the socket into the "start()" method and then call "exec()" to start the event loop of the thread. If you create the QTcpSocket into the "start()" method, this object belongs to the thread that created it, that in this moment is the new thread. Instead if you create the socket into the constructor, the object belongs to the calling thread(main thread in this case).

I tried it, but this way nothing will be sent. Echo server becomes no incoming connection and all hangs after exec() ... I tries exec() in both start() and run(). Will now try without exec() at all.

^NyAw^
21st October 2011, 12:36
Hi,



Output comes in Output window (below the editor), but it seems for GUI app it doesn't work or shows stdout only, without stderr.


I'm not able to tell you wich window is because I have Visual Studio in spanish language. What I'm able to say is that qDebug works perfectly as I can see my qDebug output messages into this window.



I tried it, but this way nothing will be sent. Echo server becomes no incoming connection and all hangs after exec() ... I tries exec() in both start() and run(). Will now try without exec() at all.


Are you sure that it hangs on "exec()". Think on that when you call "exec()" the thread enters into an eventLoop. So have your tryied to call "exec()" into the "run()" method after you create the socket?


void myThread::run()
{
...
//create the socket here
exec(); //The thread enters into the eventLoop, so the next line of code only will be called when you stop the thread.
//delete the socket here, ore close connection, ...
}

R-Type
21st October 2011, 13:32
What I'm able to say is that qDebug works perfectly as I can see my qDebug output messages into this window.

You are right. I just tested it with another application and qDebug prints in Output window. May be application I have redirects somewhere standard streams and they go somewhere else.


Are you sure that it hangs on "exec()". Think on that when you call "exec()" the thread enters into an eventLoop. So have your tryied to call "exec()" into the "run()" method after you create the socket?

yes, tried it in start() and in run(). Normally run() is executed in new thread and exec() in run() shouldn't affect main thread flow, but in debugger I still hanging in main thread at the line that started the thread...

R-Type
21st October 2011, 15:54
I tested with blocking methods and it works very strange. If I modify write() method so:


m_socket->write(Data);
m_socket->flush();
m_socket->waitForBytesWritten();
qApp->processEvents();
bool hasData=m_socket->waitForReadyRead();
int num=m_socket->bytesAvailable();
QByteArray data=m_socket->readAll();
qDebug()<<num<<data;
hasData is true and I can read my data. After that test() slot, connected to readyRead() is called. BUT if I comment

bool hasData=m_socket->waitForReadyRead(); out then there is no data (num=0) and no test() slot call.
I used TcpSocket and these signals in several projects without any problem, but now I stuck with it and have no idea what could be the problem.

^NyAw^
21st October 2011, 16:16
Hi,

First of all, are you sure that you need threads for this?

If you comment "waitForReadyRead()" is normal that you get "num == 0" because you are not waiting to get data into the socket, you only ask how many data is into the buffer and the buffer is not filled until some data arrives. You are expecting to have data into the buffer, but the application that have to write data still not wrote it.

hugh_lou
21st October 2011, 19:16
The client gets response from the server so fast that before the thread starts running.
At the moment when the response arrives, the event loop of the thread has not started yet, so you can not receive the signal.
Try to sleep some time before you start sending message to the server.

R-Type
24th October 2011, 12:05
Hi,
First of all, are you sure that you need threads for this?
No, it was just attempt to solve the problem this way.

If you comment "waitForReadyRead()" is normal that you get "num == 0" because you are not waiting to get data into the socket
I did step by step in debugger so that echo server that writes response immediately had enough time to write back. May be the reason was that until I'm in this function there are no events processed and bytesAvailable() relies on it.

Added after 4 minutes:



Try to sleep some time before you start sending message to the server.
I did it with sleep and also saw in debugger that thread already started before I send data. It has no effect.


m_ClientSocket.start();
Sleep(10000);
bool bConnected = m_ClientSocket.connectToHost("localhost", 1000);
if (bConnected) m_ClientSocket.write("rtst", 4);


Added after 10 minutes:

Once again last version of the source code:


ClientSocket::ClientSocket() : QThread() {}

bool ClientSocket::connectToHost(QString host, int port) {
m_socket->connectToHost(host, port);
return m_socket->waitForConnected(1000);
}

void ClientSocket::write(const char* Data,unsigned int nBytes) {
if (m_socket && (m_socket->state() == QAbstractSocket::ConnectedState)) {
m_socket->write(Data);
m_socket->flush();
m_socket->waitForBytesWritten();
//qApp->processEvents();
//bool hasData=m_socket->waitForReadyRead();
//int num=m_socket->bytesAvailable();
//QByteArray data=m_socket->readAll();
//qDebug()<<num<<data;
}
}

void ClientSocket::test() {
qDebug() << "I got something here!!!" << QThread::currentThreadId() ;
}

void ClientSocket::run() {
m_socket=new QTcpSocket();
bool connected=connect(m_socket, SIGNAL(readyRead()), this, SLOT(test()), Qt::QueuedConnection);
qDebug()<<connected;
//m_socket->moveToThread(this);
exec();
}

Usage:


SomeClass::SomeClass() : m_ClientSocket() {
m_ClientSocket.start();
Sleep(10000);
bool bConnected = m_ClientSocket.connectToHost("localhost", 1000);
if (bConnected) {
m_ClientSocket.write("rtst", 4);
}
}

@^NyAw^: I also tried the same code without QThread and run(), just QObject as parent. The same strange effect of readyRead() absence.

Added after 15 minutes:

I just tested it inside my echo server app (simple Qt application) an it works! (Talker is the echo server). test() slot gets called. It should has something to do with DLL/EventLoop, I guess...


int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Talker* t=new Talker();
ClientSocket* c=new ClientSocket();
c->start();
Sleep(10000);
bool bConnected = c->connectToHost("localhost", 1000);
if (bConnected)
{
c->write("rtst", 4);
}

return a.exec();
}

^NyAw^
24th October 2011, 13:14
Hi,

Well, think on that the debugger stops the eventLoop and using sleep also will stop the eventLoop.

R-Type
24th October 2011, 16:53
It's clear, but after sleep it should be possible to send readyRead(). Echo server is another stand alone app and it's event loop not affected by debugger or Sleeps, so it sends data during this sleep.

wysota
24th October 2011, 22:22
Why are you using a dedicated thread for the socket? You don't need it for anything and it is causing you problems because you are trying to access the socket from within a thread it doesn't belong to.

R-Type
25th October 2011, 09:05
In #3 and #6 I already wrote that I've envolved QThread to get event loop, because it didn't work as I had Client Socket inherited from QObject. This was just suggestion that it doesn't work because of missing event loop. By the way, socket belongs to thread as it's instantiated in run() (see code above). As I wrote before, this all works also if I use this ClientSocket in my simple appplication from main(). But in Dll of project (plugin) I'm now working with, I have a problem that non blocking methods didn't get called whereas blocking methods work.
To avoid further confusions with threads I post version without QThread that behaves the same way.


class ClientSocket : public QObject {
Q_OBJECT
public:
ClientSocket() : QObject()
, m_socket(new QTcpSocket(this)) {
connect(m_socket, SIGNAL(readyRead()), this, SLOT(test()));
}
virtual ~ClientSocket() {};
bool connectToHost(QString host, int port) {
m_socket->connectToHost(host, port);
return m_socket->waitForConnected(1000);
}
void write(const char* Data,unsigned int nBytes) {
if (m_socket && (m_socket->state() == QAbstractSocket::ConnectedState))
{
m_socket->write(Data);
m_socket->flush();
m_socket->waitForBytesWritten();
//bool hasData=m_socket->waitForReadyRead();
//int num=m_socket->bytesAvailable();
//QByteArray data=m_socket->readAll();
//qDebug()<<num<<data;
}
}
public slots:
void test() { qDebug()<<"readyRead()"; }
private:
QTcpSocket* m_socket;
};

From DLL code it's used like this


SomeClass::SomeClass() : m_ClientSocket() {
bool bConnected = m_ClientSocket.connectToHost("localhost", 1000);
if (bConnected) m_ClientSocket.write("rtst", 4);
}

test() slot is not called. if I uncomment lines in write() I have expected response (num=4, data="rtst") and slot is called. But waitForReadyRead() shouldn't be called in order to get slot working...

wysota
28th October 2011, 06:52
In #3 and #6 I already wrote that I've envolved QThread to get event loop, because it didn't work as I had Client Socket inherited from QObject.
The main thread also has (or at least can have) an event loop, I really don't see the point of using a thread here.


By the way, socket belongs to thread as it's instantiated in run() (see code above).
Sure, but you are calling the socket's functions directly from the main thread which is forbidden.

R-Type
28th October 2011, 08:48
The main thread also has (or at least can have) an event loop

In application (plugin) where it doesn't work, call to ClientSocked takes place not from the main thread. And in example above, where I use ClientSocket from main(), it works.


Sure, but you are calling the socket's functions directly from the main thread which is forbidden.
You are right, connect and write had to be called also in run in this example.

wysota
28th October 2011, 08:55
No, connect() can be called anywhere. The point is not to call any of the methods of objects belonging to thread B from thread A. Regardless of whether there are slots involved anywhere.

So this is forbidden:

ClientSocket* c=new ClientSocket();
c->start();
Sleep(10000); // doesn't make sense
bool bConnected = c->connectToHost("localhost", 1000); // forbidden

and if you change those calls to proper ones (using signals and slots), you'll notice there is no advantage of having a thread anywhere, it only causes trouble. Qt works in asynchronous fashion, when you try to force it to be synchronous (like in your code above), you'll have more problems with it than benefits from not having to switch your thinking into asynchronous mode.

R-Type
28th October 2011, 09:40
Beow is "true" asynchronous version where communication takes place only via signals and slots (no threads!):


class ClientSocket : public QObject
{
Q_OBJECT
public:
ClientSocket() : QObject() , m_socket(new QTcpSocket(this)) {
connect(m_socket, SIGNAL(readyRead()), this, SLOT(test()));
}
virtual ~ClientSocket() {};
bool connectToHost(QString host, int port) {
m_socket->connectToHost(host, port);
return m_socket->waitForConnected(1000);
}
void write(const char* Data,unsigned int nBytes) {
if (m_socket && (m_socket->state() == QAbstractSocket::ConnectedState))
{
m_socket->write(Data);
m_socket->flush();
}
}

public slots:
void writeData(const char* Data,unsigned int nBytes) { write(Data, nBytes); }
void connectSlot(const QString& host, int port) { connectToHost(host, port); }
void test() { throw new std::runtime_error("ok"); }
private:
QTcpSocket* m_socket;
};

Here is the calling part:


connect(this, SIGNAL(connectSignal(const QString&, int)), &m_ClientSocket, SLOT(connectSlot(const QString&, int)));
connect(this, SIGNAL(writeData(const char*,unsigned int)), &m_ClientSocket, SLOT(writeData(const char*,unsigned int)));
emit(connectSignal("localhost", 1000));
emit(writeData("rtst", 4));

test() signal still not called. What is wrong?

connectSlot() and writeData() slots get called.

Added after 4 minutes:

if I use Qt::QueuedConnection for 2 last connect()s then neither connectSlot() nor writeData() are called.

wysota
28th October 2011, 10:51
I see some synchronous things in your code. Besides, if you want to post an example, make it a complete one, including main().

R-Type
28th October 2011, 12:17
I see some synchronous things in your code.
Do you mean connect/write signals sequence? It's just to make example smaller. Normally write should be called in slot for connected() signal, but in example it doesn't matter because local connections are quite fast.

Besides, if you want to post an example
As I mentioned, the problem is that in example it works and in real project the same code doesn't call test() slot. Here is the complete example:
test.h


#include <ClientSocket.h>

class Test : public QObject
{
Q_OBJECT

signals:
void connectSignal(const QString& host, int port);
void writeData(const char* data,unsigned int length);

public:
Test();
private:
ClientSocket m_ClientSocket;
};

test.cpp


#include "test.h"

Test::Test() : QObject(), m_ClientSocket() {
connect(this, SIGNAL(connectSignal(const QString&, int)), &m_ClientSocket, SLOT(connectSlot(const QString&, int)));
connect(this, SIGNAL(writeData(const char*,unsigned int)), &m_ClientSocket, SLOT(writeData(const char*,unsigned int)));
emit(connectSignal("localhost", 1000));
emit(writeData("rtst", 4));
}
ClientSocket.h


#include <QTcpSocket>

class ClientSocket : public QObject
{
Q_OBJECT

public:
ClientSocket() : QObject()
, m_socket(new QTcpSocket(this)) {
connect(m_socket, SIGNAL(readyRead()), this, SLOT(test()));
}
bool connectToHost(QString host, int port) {
m_socket->connectToHost(host, port);
return m_socket->waitForConnected(1000);
}
void write(const char* Data,unsigned int nBytes) {
if (m_socket && (m_socket->state() == QAbstractSocket::ConnectedState))
{
m_socket->write(Data);
m_socket->flush();
}
}

public slots:
void writeData(const char* Data,unsigned int nBytes) { write(Data, nBytes); }
void connectSlot(const QString& host, int port) { connectToHost(host, port); }
void test() { qDebug()<<"ok"; }

private:
QTcpSocket* m_socket;
};

main.cpp


#include <QtCore/QCoreApplication>
#include "test.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test t;
return a.exec();
}

wysota
28th October 2011, 20:22
Do you mean connect/write signals sequence?
I mean waitFor*() calls and using sleep().


As I mentioned, the problem is that in example it works and in real project the same code doesn't call test() slot.
There is no point in fixing code that works :) Your "real project" apparently does something which breaks the code. It may block the event loop or don't start one at all.