Unsolved QTcpSocket client, write problem...
-
I've got a server that I can connect to with a browser and it responds ok. Now I'm trying to get my client application to connect and write data to the server. My client application is using QTcpSocket, my client calls connectToHost, the connection is established to the server. I then try to write data to the server module by emitting a signal to write:
emit write(objJSON);
objJSON contains:
{"msgType":"ready","source":"Simon"}
The signal is connected to the client socket using:
QObject::connect(this, &clsMsgSender::write ,this, &clsMsgSender::onWrite, Qt::DirectConnection);
The slot onWrite:
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; } #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 the write signal is emitted, Qt Creator halts in qiodevice.h at line 137 on:
{ return write(data.constData(), data.size()); }
The function eGetSockState contains:
QAbstractSocket::SocketState clsMsgSender::eGetSockState() { QMutexLocker lock(&mMutex); if ( mpsckClient != nullptr ) { return mpsckClient->state(); } return QAbstractSocket::UnconnectedState; }
There is no breakpoint at on or near this, the slot checks for a valid connection, so what's going on?
-
@SPlatten said in QTcpSocket client, write problem...:
When the write signal is emitted, Qt Creator halts in qiodevice.h at line 137 on:
And how does the stack trace look like? What is the last line of your own code?
-
@jsulm, when Qt Creator stops at line 137, the stack trace:
1 __pthread_kill (x86_64) /usr/lib/system/libsystem_kernel.dylib 0x7fff20317462 2 pthread_kill (x86_64) /usr/lib/system/libsystem_pthread.dylib 0x7fff20345610 3 abort (x86_64) /usr/lib/system/libsystem_c.dylib 0x7fff20298720 4 qt_message_fatal(QtMsgType, QMessageLogContext const&, QString const&) (x86_64) /Users/sy/Qt/5.15.2/clang_64/lib/QtCore.framework/Versions/5/QtCore 0x101534529 5 QMessageLogger::warning(const char *, ...) const (x86_64) /Users/sy/Qt/5.15.2/clang_64/lib/QtCore.framework/Versions/5/QtCore 0x10153505b 6 QSocketNotifier::setEnabled(bool) (x86_64) /Users/sy/Qt/5.15.2/clang_64/lib/QtCore.framework/Versions/5/QtCore 0x1017506cb 7 QAbstractSocket::writeData(const char *, long long) (x86_64) /Users/sy/Qt/5.15.2/clang_64/lib/QtNetwork.framework/Versions/5/QtNetwork 0x101433d4a 8 QIODevice::write(const char *, long long) (x86_64) /Users/sy/Qt/5.15.2/clang_64/lib/QtCore.framework/Versions/5/QtCore 0x1016615fe 9 QIODevice::write(QByteArray const&) qiodevice.h 137 0x100012341 10 clsMsgSender::onWrite(QJsonObject) clsMsgSender.cpp 195 0x100026ad8 11 QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QJsonObject>, void, void (clsMsgSender:: *)(QJsonObject)>::call(void (clsMsgSender:: *)(QJsonObject), clsMsgSender *, void * *) qobjectdefs_impl.h 152 0x10002c3db 12 void QtPrivate::FunctionPointer<void (clsMsgSender:: *)(QJsonObject)>::call<QtPrivate::List<QJsonObject>, void>(void (clsMsgSender:: *)(QJsonObject), clsMsgSender *, void * *) qobjectdefs_impl.h 185 0x10002c328 13 QtPrivate::QSlotObject<void (clsMsgSender:: *)(QJsonObject), QtPrivate::List<QJsonObject>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void * *, bool *) qobjectdefs_impl.h 418 0x10002c265 14 void doActivate<false>(QObject *, int, void * *) (x86_64) /Users/sy/Qt/5.15.2/clang_64/lib/QtCore.framework/Versions/5/QtCore 0x101747f95 15 clsMsgSender::write(QJsonObject) moc_clsMsgSender.cpp 239 0x10003ce22 16 clsMsgSender::run() clsMsgSender.cpp 284 0x100028b42 17 QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, void (clsMsgSender:: *)()>::call(void (clsMsgSender:: *)(), clsMsgSender *, void * *) qobjectdefs_impl.h 152 0x10002c6d4 18 void QtPrivate::FunctionPointer<void (clsMsgSender:: *)()>::call<QtPrivate::List<>, void>(void (clsMsgSender:: *)(), clsMsgSender *, void * *) qobjectdefs_impl.h 185 0x10002c648 19 QtPrivate::QSlotObject<void (clsMsgSender:: *)(), QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void * *, bool *) qobjectdefs_impl.h 418 0x10002c585 20 QObject::event(QEvent *) (x86_64) /Users/sy/Qt/5.15.2/clang_64/lib/QtCore.framework/Versions/5/QtCore 0x10173fb9f ... <More>
Entry #15 is the emit of the write signal, #10 is the onWrite slot.
-
@SPlatten There should be an error message? (qt_message_fatal(QtMsgType, QMessageLogContext const&, QString const&))
-
@jsulm I can see that at entry #4, but nothing is displayed, it seems to skip over that. I've just run it again and entries #1 to #8 are grayed, if I click on #8 it jumps into assembler.
-
@SPlatten said in QTcpSocket client, write problem...:
but nothing is displayed, it seems to skip over that
? When debugger breaks, you are just supposed to move up to frame #4 or #5 and look at the value of the string of the message?
-
@JonB As I said in my previous post, those entires are grayed and when I click on those there is no data, no source it stops in assembler.
-
@SPlatten Start without debugger
-
@jsulm , how is that going to help? I ran it in the debugger because no data is being written, so I'm trying to determine why it isn't writing.
-
As I said in my previous post, those entires are grayed and when I click on those there is no data, no source it stops in assembler.
Ah, I understand.
So far as I know, all of these messages go via the qInstallMessageHandler(QtMessageHandler handler). In a complex program it is vital you use this to install your own message handler for all messages, including those issued in Qt's own code. Then you can see the message text, or break there in the debugger and look at variables.
Have you done this for your application?
[BTW: When you say you see no message now, you are checking in the appropriate tab of the debugger? Anyway, always install your own handler, so you can break etc. there.]
-
@JonB , Thank you, here is my message handler:
static void qDebugMsgHandler(QtMsgType type, const QMessageLogContext& context, const QString& strMsg) { if ( clsDebugService::blnTerminating() == true ) { //Application is terminating, do not add anything else to the stack return; } // static const QString scstrQJsonObject("QJsonObject("); static const QChar scqcCarriageReturn('\n') ,scqcCloseBracket(')') ,scqcComma(',') ,scqcOpenBracket('('); static QString sstrLastFile, sstrLastFunction; static long slngLastLine = -1; clsDebugService* pService = clsDebugService::pGetService(); quint16 uint16DebugLevel = clsDebugService::mscintDefaultLevel ,uint16MsgOffset; QString strFile, strFunction, strInfo, strPrefix; int intLevelDelimiterIdx = -1; //Look for debug level delimiter: \x2, NOTE we cannot use indexOf to locate it! static const char ccDebugLevelDelimiter = (clsDebugService::msccDebugLevelDelimiter.toLatin1() + '0'); for( int i=0; i<strMsg.length() - 3; i++ ) { if ( strMsg[i + 1].toLatin1() == '\\' && strMsg[i + 2].toLatin1() == 'x' && strMsg[i + 3].toLatin1() == ccDebugLevelDelimiter ) { intLevelDelimiterIdx = ++i; break; } } if ( intLevelDelimiterIdx > 0 ) { //Yes, its the debug level uint16DebugLevel = strMsg.midRef(0, intLevelDelimiterIdx).toUInt(); //Offset to start of message uint16MsgOffset = intLevelDelimiterIdx + 3; } else { uint16MsgOffset = 0; } if ( uint16DebugLevel > clsDebugService::mscintDefaultLevel ) { //Do nothing debug level is higher than this messages level return; } //Start with time stamp QTime tmNow = QTime::currentTime(); strPrefix = tmNow.toString(Qt::ISODateWithMs); strPrefix += " "; switch( type ) { case QtCriticalMsg: strPrefix += "C"; break; case QtDebugMsg: strPrefix += "D"; break; case QtFatalMsg: strPrefix += "F"; break; case QtInfoMsg: strPrefix += "I"; break; case QtWarningMsg: strPrefix += "W"; break; } if ( strPrefix.isEmpty() != true ) { strPrefix += ":"; } //Split message into lines? QStringList slstParts = strMsg.mid(uint16MsgOffset).split(scqcCarriageReturn); quint16 uint16Parts = slstParts.length(); long lngLine = 0; for( quint16 uint16Part=0; uint16Part<uint16Parts; uint16Part++ ) { QString strOutput(strPrefix), strPart; if ( slstParts.length() > 1 ) { strPart = slstParts[uint16Part]; if ( strPart.startsWith(scqcComma) ) { strPart = strPart.remove(scqcComma); } if ( uint16Part == 0 && strPart.startsWith(scqcOpenBracket) ) { strPart = clsDebugService::msccSpace + strPart.mid(1); } if ( uint16Part == uint16Parts && strPart.endsWith(scqcCloseBracket) ) { strPart = strPart.remove(scqcCloseBracket); } } else { strPart = slstParts[uint16Part]; } if ( strPart.isEmpty() == true ) { continue; } /*Is this used??? if ( strPart.startsWith(scstrQJsonObject) ) { //Message contains a JSON object, extract the details int intLength = strPart.length() - (scstrQJsonObject.length() + 1); QString strJSON = strPart.mid(scstrQJsonObject.length(), intLength); QJsonDocument objDoc = QJsonDocument::fromJson(strJSON.toUtf8()); if ( objDoc.isNull() ) { return; } QJsonObject objJSON = objDoc.object(); QString strLine(objJSON.take("line").toString()); strFile = objJSON.take("file").toString(); lngLine = strLine.toLong(); strInfo = objJSON.take("msg").toString(); type = static_cast<QtMsgType>(objJSON.take("type").toInt()); } else */{ uint32_t uint32Line = 0; if ( pService->blnGetOriginDetails(strFile, uint32Line) == true ) { lngLine = static_cast<long>(uint32Line); } if ( context.function ) { strFunction = QString(context.function); strFunction = strFunction.mid(0, strFunction.indexOf(clsDebugService::msccBrktOpen)); } strInfo = strPart; } if ( strInfo.trimmed().isEmpty() == true || strInfo.compare(scqcCloseBracket) == 0 ) { continue; } if ( uint16Part == 0 ) { if ( lngLine >= 0 && !(slngLastLine == lngLine && strFile.compare(sstrLastFile) == 0 && sstrLastFunction.compare(strFunction) == 0) ) { sstrLastFile = strFile; slngLastLine = lngLine; sstrLastFunction = strFunction; if ( lngLine > 0 ) { strOutput += QString("L%1").arg(lngLine, clsDebugService::mscintLineNoLength , clsDebugService::mscintLineNoBase, QChar('0')); } if ( strFile.length() > 0 ) { strOutput += QString("F%1").arg(strFile); } if ( strFunction.length() > 0 ) { strOutput += "[" + strFunction + "]"; } } if ( strInfo.isEmpty() != true ) { strOutput += strInfo; } pService->blnPushDebug(strOutput); } else if ( uint16Parts > 1 ) { strOutput += strInfo; pService->blnPushDebug(strOutput); } } }
I think the issue here is that it crashes before the message queue is processed, I'll try putting breakpoints on the message types when QtCriticalMsg, QtFatalMsg and QtWarningMsg.
So far this is what I've found:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
-
@SPlatten
The fact thatQMessageLogger::warning
is callingabort()
does not look right. With your extremely complex-looking code there, Im wonder whether it isn't falling over in your own code! You handler at this level should be much simpler, IMHO! Anyway, put in judiciois breakpoints and see of you can break where you can view the messages fromQSocketNotifier::setEnabled()
. -
@JonB , I think you posted before I edited, see the end of the last post.
-
@SPlatten said in QTcpSocket client, write problem...:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
So that's the original faulting message? Yep, well, you know this is a problem in the code you're writing :)
-
Is this across threads? the forced DirectConnection lets me believe that
QAbstractStrocket uses internal QTimer objects you're calling onWrite via a forced DirectConnection, that means the write is called from the calling thread and that is != the thread where the socket may live -> QTimers can't be started or stoped from an other thread.
Possibly the reason for your failed write attempt
-
@J-Hilk , thank you, I've removed the Qt::DirectConnection parameter from the connects and now the warning has gone.
-
@SPlatten I don't know if
QJsonObject
is registered with the meta system by default, if not, you'll have to do that yourself, or the connect won't work.But you should get compile/runtime warnings if thats the case
-
@SPlatten
A while back in another of your post threads you were using explicitQt::DirectConnection
s, and I said then (and I think others did too), why are you writing this, it will just lead to trouble.So why do you ever put
Qt::DirectConnection
into yourconnect()s
, I just don't get it, it keeps leading to this kind of problem? -
@JonB , they are gone now.
-
@J-Hilk , I do register the type.