Unsolved QTcpSocket client, write problem...
-
@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. -
-
@kshegunov , my listening class implements the incomingConnection method:
void clsListener::incomingConnection(qintptr sckDescriptor) { //Every new connection will be run in a newly created thread clsServer* pThread = new clsServer(sckDescriptor, this); //We have a new connection qDebug() << sckDescriptor << " Connecting..."; //Connect signal/slot //Once a thread is not needed, it will be beleted later QObject::connect(pThread, &QThread::finished ,pThread, &QThread::deleteLater); pThread->start(); }
From what you are saying the thread created here is wrong in that it passes the socket descriptor?
The constructor for clsServer:
clsServer::clsServer(qintptr sckDescriptor, QObject* pParent) : QThread(pParent) , msckDescriptor(sckDescriptor) , mpsckIncoming(nullptr) { }
The prototype for clsServer:
class clsServer : public QThread { Q_OBJECT private: qintptr msckDescriptor; QTcpSocket* mpsckIncoming; mqueJSON mqueMsgsIn; public: explicit clsServer(qintptr sckDescriptor, QObject* pParent = nullptr); ~clsServer(); void cleanup(); void run(); signals: void error(QTcpSocket::SocketError socketerror); public slots: void onDataIn(); void onDisconnected(); void onErrorOccurred(QAbstractSocket::SocketError error); };
The thread body:
void clsServer::run() { QTcpSocket* pSocket = new QTcpSocket(); //Set the ID qdbg() << "clsServer::run"; if( !pSocket->setSocketDescriptor(msckDescriptor) ) { //Something's wrong, we just emit a signal emit error(pSocket->error()); return; } //Connect socket and signal mpsckIncoming = pSocket; QObject::connect(mpsckIncoming, &QAbstractSocket::readyRead ,this, &clsServer::onDataIn); QObject::connect(mpsckIncoming, &QAbstractSocket::errorOccurred ,this, &clsServer::onErrorOccurred); QObject::connect(mpsckIncoming, &QAbstractSocket::disconnected ,this, &clsServer::onDisconnected); //We'll have multiple clients, we want to know which is which qdbg() << msckDescriptor << " Client connected"; //Make this thread a loop, //thread will stay alive so that signal/slot to function properly //not dropped out in the middle when thread dies exec(); }
Actually, the descriptor passed into the constructor is not actually used until the thread body run loop is entered. Again, it looks very wrong now.
However, the above is the class that listens for incoming data, the class that sends messages from one process to another:
class clsMsgSender : public QThread { Q_OBJECT private: static const quint16 mscuint16ConnectionTO; static mpMsgSenders msmpMsgSenders; static clsMsgSender* mspService; static qulonglong msulnglngUniqueMsgID; mutable QMutex mMutex; bool mblnBusy, mblnConnected, mblnRun, mblnStopped; clsModule* mpModule; QTcpSocket* mpsckClient; mqueJSON mqueMsgsOut; QString mstrHost; quint16 muint16Port; QAbstractSocket::SocketState eGetSockState(); QJsonObject objAnythingToDo(); void push(const QJsonObject& crobjJSON); void setModule(clsModule* pModule); quint16 uint16GetPort(); public: explicit clsMsgSender(clsModule* pModule, QObject* pParent = nullptr); ~clsMsgSender(); bool blnBusy(); bool blnRunning() { return mblnRun; } mpMsgSenders::iterator itGetMsgSndr(quint16 uint16Port, QString& rstrKey); QTcpSocket* pGetClient() { return mpsckClient; } static clsMsgSender* pGetMsgSndr(quint16 uint16Port); static clsMsgSender* pGetService() { return clsMsgSender::mspService; } void setHost(QString strHost); static QString strGetLocalIP(); void terminate() { mblnRun = false; } static qulonglong ulnglngGetUniqueMsgID(); signals: void connectToHost(const QString& crstrHost, quint16 uint16Port); void removeTrkrs(clsMsgSender* pMsgSndr); void sendJSON(const QJsonObject& robjJSON); void write(QJsonObject robjJSON); public slots: void onConnectToHost(const QString& crstrHost, quint16 uint16Port); void onConnected(); void onDisconnected(); #if defined(DEBUG_SOCKETS) void onErrorOccurred(QAbstractSocket::SocketError sckError); void onHostFound(); #endif void onRemoveTrkrs(clsMsgSender* pMsgSndr); void onSendJSON(const QJsonObject& crobjJSON); void onWrite(QJsonObject robjJSON); void run(); };
Implementation:
clsMsgSender::clsMsgSender(clsModule* pModule, QObject* pParent) : QThread(pParent) , mblnBusy(false), mblnConnected(false) , mblnRun(true), mblnStopped(false) , mpModule(nullptr) , mpsckClient(new QTcpSocket()) { setModule(pModule); clsMsgSender::mspService = this; QObject::connect(this, &clsMsgSender::connectToHost ,this, &clsMsgSender::onConnectToHost); QObject::connect(this, &clsMsgSender::removeTrkrs ,this, &clsMsgSender::onRemoveTrkrs); QObject::connect(this, &clsMsgSender::sendJSON ,this, &clsMsgSender::onSendJSON); QObject::connect(this, &clsMsgSender::write ,this, &clsMsgSender::onWrite); //Thread body QObject::connect(this, &QThread::started ,this, &clsMsgSender::run); QObject::connect(this, &QThread::finished ,this, &QThread::deleteLater); QObject::connect(mpsckClient, &QAbstractSocket::connected ,this, &clsMsgSender::onConnected); QObject::connect(mpsckClient, &QAbstractSocket::disconnected ,this, &clsMsgSender::onDisconnected); #if defined(DEBUG_SOCKETS) QObject::connect(mpsckClient, &QAbstractSocket::errorOccurred ,this, &clsMsgSender::onErrorOccurred); QObject::connect(mpsckClient, &QAbstractSocket::hostFound ,this, &clsMsgSender::onHostFound); #endif mpsckClient->setSocketOption(QAbstractSocket::LowDelayOption, 1); }
-
@SPlatten As I told you in previous posts,
QThread
class is a little bit tricky and sub-classing it is in most case not the best way to do.Because:
- the
clsServer
instance leaves in the thread in which it have been created, supposing main thread mpsckIncoming
will leave in the hosted thread
this means, with:
QObject::connect(mpsckIncoming, &QAbstractSocket::readyRead, this, &clsServer::onDataIn);
slot
onDatin()
will run inthis
thread which is the main thread and usingmpsckIncoming
will not work properly, because you are in the wrong thread. - the
-
@KroMignon, this morning whilst looking closer at the code and trying to resolve it I found the problem, there is nothing wrong with my threads of sockets, the actual problem is that my debug isn't reporting it properly, which I am looking at now.