Unsolved QTcpSocket client, write problem...
-
@JonB , they are gone now.
-
@J-Hilk , I do register the type.
-
@SPlatten said in QTcpSocket client, write problem...:
@JonB , they are gone now.
:) May I suggest you never put them in? The default for
connect()
isDirectConnection
, and Qt only changes that toQueued
when it sees it's going across threads. At which point as I understand it you need it to do that, so why interfere? :)Might I also suggest you consider simplifying your
qDebugMsgHandler()
? That's an awful lot of code to potentially go wrong, when you are down in a low-level message handler! And if it does you will lose the original message, as you have seen. At minimum: factor all your stuff out to a separate function, so thatqDebugMsgHandler()
is lean & clean. And wrap that with atry
handler. If it goes doolally, allow the originalqDebugMsgHandler()
to not fall over but to issue some message in a simple fashion. That way you have some protection for not fouling up and still getting access to the original fault message. Making sure one's low-level error/message handlers do not themselves introduce errors is an important goal. -
@JonB , thank you, the debugHandler works fine and quite sure there is nothing wrong with it, I accept your comments and in the final release it will be disabled altogether.
-
@SPlatten, fix the reason for the warning, so you don't get the warning. In other words, don't enable/disable sockets from different threads.
@JonB said in QTcpSocket client, write problem...:
The fact that
QMessageLogger::warning
is callingabort()
does not look right.He's running with fatal warnings (as suggested in earlier threads), so it's exactly right!
-
I think I spoke to soon I still have the problem:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
I've modified all my QObject::connect's, ensuring that I am not using Qt::DirectConnection. I'm still getting this message. I've been careful that I am not calling write from any thread, I always emit a write signal which connects to a slot called onWrite which actually does the::
qint64 int64Written = mpsckClient->write(arybytMsg);
-
@SPlatten said in QTcpSocket client, write problem...:
I think I spoke to soon I still have the problem:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another threadAs an experimented developer, you should know that multithreading can solve problems but always introduce new problems ;)
When using QObject based classes, you should always ensure that you are using them in the thread in which they have be created / moved.
So, are you sureqint64 int64Written = mpsckClient->write(arybytMsg);
is done in the right thread (mpsckClient->thread() == QThread::currentThread()
). -
@KroMignon I thought that one of the benefits of using signals and slots is that you can be assured that they are thread safe?
-
@SPlatten said in QTcpSocket client, write problem...:
I thought that one of the benefits of using signals and slots is that you can be assured that they are thread safe?
Yes it is, but it depends how you are doing it.
QObject::connect(srcObject, SIGNAL(), destObject, SLOT())
will ensureSLOT()
will run indestObject
work thread, when signal is emitted.EDIT:
but
QObject::connect(srcObject, SIGNAL(), destObject, SLOT(), Qt::DirectConnection)
will runSLOT()
in thread from which signal has been emitted. -
@KroMignon Thank you, rapidly learning this.
-
@SPlatten said in QTcpSocket client, write problem...:
Thank you, rapidly learning this.
Other things you should take care off when you are doing multithreading, is to set parent for class member to ensure that when you are moving the class instance to another thread, all member will follow.
To better explain what I mean, suppose following example:
class TestClass : public QObject { Q_OBJECT public: explicit TestClass(const QHostAddress &address, quint16 portNum, QHostAddress QObject * parent=nullptr) : QObject(parent), mSocket(new QTcpSocket(this)) { } private: QTcpSocket* mSocket; }; class TestClass2 : public QObject { Q_OBJECT public: explicit TestClass2(const QHostAddress &address, quint16 portNum, QHostAddress QObject * parent=nullptr) : QObject(parent), mSocket(new QTcpSocket()) { } private: QTcpSocket* mSocket; };
And somewhere in code:
auto sock = new TestClass(QHostAddess::LocalHost, 888); auto sock2 = new TestClass2(QHostAddess::LocalHost, 889); auto t = new QThread(); sock->moveToThread(t); sock2->moveToThread(t); t->start();
now:
sock
andsock->mSocket
are moved to threadt
.sock2
is move the theadt
butsock2->mSocket
still lives in thread in which it have been created
-
I feel like I am going around and round in circles and just not progressing. I have one process which listens for data on port 8123. It also starts another process which connects to the first process on the host IP and port 8123.
The processes connect without problem, I am trying to write data from the second process to the first. I emit a signal from the socket that is used to connectToHost on the first process, I have a slot that connects to the emitted write signal:
void clsMsgSender::onWrite(QJsonObject objJSON) { if ( mpsckClient == nullptr ) { return; } QAbstractSocket::SocketState sckState = eGetSockState(); if ( sckState != QAbstractSocket::ConnectedState ) { #if defined(DEBUG_SOCKETS) qdbg() << "Not connected!"; #endif return; } QMutexLocker lock(&mMutex); #if defined(DEBUG_SOCKETS) QString strMsg(QJsonDocument(objJSON).toJson(QJsonDocument::Compact)); qdbg() << QString("clsMsgSender::onWrite, to: %1:%2, data: %3") .arg(mpsckClient->peerAddress().toString(), QString::number(mpsckClient->peerPort()), strMsg); #endif clsJSON::addCommonJSONflds(objJSON, nullptr, mpModule); //Insert a unique message ID into the message objJSON.insert(clsJSON::mscszMsgID, QString::number(clsMsgSender::ulnglngGetUniqueMsgID())); //Associate this TCP socket with the output data stream QByteArray arybytMsg; arybytMsg = QJsonDocument(objJSON).toJson(QJsonDocument::Compact); //Write message qint64 int64Written = mpsckClient->write(arybytMsg); #if defined(DEBUG_SOCKETS) qdbg() << QString("clsMsgSender::onWrite, written:%1") .arg(int64Written); #endif if ( int64Written > 0 ) { //Remove the item from the queue mqueMsgsOut.dequeue(); } //No longer busy mblnBusy = false; }
When I emit the write signal I take a JSON packet from a queue, the item is only removed from the queue when the write completes. I can see using the Qt Creator debugger that in the onWrite slot the socket write is successful and returns the number of bytes written. However I see no evidence that the first application is receiving the data. What could be the cause?
-
@SPlatten said in QTcpSocket client, write problem...:
However I see no evidence that the first application is receiving the data. What could be the cause?
TCP sockets are stream interfaces, and they use buffers (reception/transmission) to avoid sending too much small packets.
To force data transfer you could do:// write to transmission buffer qint64 int64Written = mpsckClient->write(arybytMsg); // force transmission mpsckClient->flush();
-
@KroMignon , I'm wondering if sockets is the best mechanism for the purpose I am trying to use them for.
The main process will launch X number of processes, each process will provide a specific purpose.
Messages sent from the main process will issue requests to the other processes and the other processes will send the results of these requests back to the main. I am using JSON for packaging data messages between all applications.
I'm now looking at QSharedMemory, but I don't know if I can use QSharedMemory for this kind of communication?
-
@SPlatten
You can use shared memory if you prefer. But I think you'll end up with the same sort of thread issues as you're having with TCP. -
@SPlatten said in QTcpSocket client, write problem...:
I'm now looking at QSharedMemory, but I don't know if I can use QSharedMemory for this kind of communication?
If you don't want to lose all done work, I would suggest you to use
QLocalServer
andQLocalSocket
which are kind of TCP socket but which is a more aggressive transmission rule.
Data are sent almost directly, on entering event loop.
And you don't have to handle shared lock for shared memory access. -
@KroMignon , thank you, I will look into QLocalServer and QLocalSocket, it sounds like a better fit as my intention is that all processes will run on the same system.
-
Hi,
As already written in one of your other threads: QTcpSocket shall be created in the thread they are going to be used in. They cannot be moved around. You can pass around the descriptor but not the QTcpSocket itself. See the threaded QTcpServer/QTcpClient examples.
-
@SGaist , I create the sockets in the main thread and then use signals to communicate between threads, where the connections are also created in the main thread.
-
@SPlatten said in QTcpSocket client, write problem...:
@SGaist , I create the sockets in the main thread and then use signals to communicate between threads, where the connections are also created in the main thread.
The place of making the connection(s) is irrelevant. There are 2 things that matter:
-
For sockets - the thread where the descriptor was initialized into, or in other words the thread where the socket's been opened in. @SGaist is half correct in saying they can't be moved around, they can, but iff they've not been opened yet. The thread that socket's been opened in then is and must be the thread the
QObject
lives in and is fixed forever and ever until the end of time. -
For any signal slot emission - the thread that the signal was emitted from and the receiving object context's thread. (direct connections are uninteresting here). The slot's going to be executed in the receiver's thread, while depending on the signal emission's thread either the call will be direct, or queued. For the same connection this may vary within the program if the (same) signal emissions happen from different threads (which is possible), so sometimes it may default to direct calls, sometimes to queued calls. The point is, however, that you shouldn't care. The design should be made such that it doesn't matter if the slot's executed immediately or later, which is also why you shouldn't (99% of cases) tamper with the connection type when doing the
QObject::connect
call.
I imagine your object threads are wrong and you hold instances of objects that live in a different thread, so that's why you get the errors. To be certain this doesn't happen, always provide a valid parent for
QObject
s with the major exception of when the object is a root of a QObject tree that's going to be moved to another thread. Thereafter everything is easy peasy. -