Solved QTcpSocket / QTcpServer, connection closed
-
@KroMignon , I've stopped the main application from creating the process, so I can debug it in two instances of Qt Creator and see whats going on.
So for the sake of this past A = the original parent application and B is the other application. A uses QTcpServer and B uses QTcpCSocket.
When A starts up:
clsSocketServer::clsSocketServer(QObject* pParent) : QTcpServer(pParent) { QString strListenFailure = QString("Cannot listen to port: ") + QString::number(clsSocketServer::mscuint16port); listen(QHostAddress::Any, clsSocketServer::mscuint16port); QList<QHostAddress> lstAddresses = QNetworkInterface::allAddresses(); QString strIP; for( int i=0; i<lstAddresses.size(); i++ ) { if ( lstAddresses[i] != QHostAddress::LocalHost && lstAddresses[i].toIPv4Address() ) { strIP = lstAddresses[i].toString(); break; } } if ( strIP.isEmpty() == true ) { strIP = QHostAddress(QHostAddress::LocalHost).toString(); } qdbg() << tr("XMLMPAM is listening on: %1:%2\n") .arg(strIP).arg(clsSocketServer::mscuint16port); connect(this, &QTcpServer::newConnection, this, &clsSocketServer::sayHello); Q_ASSERT_X(isListening(), "clsSocketServer::clsSocketServer", strListenFailure.toLatin1().data()); }
The slot connected to newConnection:
void clsSocketServer::sayHello() { clsSocketClient* pClient = new clsSocketClient(nextPendingConnection()); if ( pClient == nullptr ) { return; } QByteArray arybytMsg; QDataStream dsOut(&arybytMsg, QIODevice::WriteOnly); dsOut.setVersion(clsJSON::mscintQtVersion); QJsonObject objMsg; objMsg.insert(clsJSON::mscszModule, clsMainWnd::mscstrTitle); dsOut << QJsonDocument(objMsg).toJson(QJsonDocument::Compact); connect(pClient, &QAbstractSocket::disconnected ,pClient, &QObject::deleteLater); pClient->sendJSON(objMsg); }
When B starts up:
clsModHelper::clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion) : QTcpSocket(pParent) , mdblVersion(dblVersion) , mfpDbgLog(nullptr) , mint64AppPID(QCoreApplication::applicationPid()) , mstrTitle(cpszTitle) , muint16ModulePort(0), muint16LauncherPID(0) , muint16XMLMPAMport(0) { if ( intArgc < CLA_LAUNCHER_PID ) { std::cout << "Insufficient arguments, aborting!" << std::endl; exit(EXIT_FAILURE); } if ( mspThis == nullptr ) { mspThis = this; } muint16XMLMPAMport = (quint16)atoi(parystrArgv[CLA_XMLMPAM_PORT]); muint16ModulePort = (quint16)atoi(parystrArgv[CLA_MODULE_PORT]); muint16LauncherPID = (quint16)atoi(parystrArgv[CLA_LAUNCHER_PID]); setSocketOption(QAbstractSocket::LowDelayOption, 1); //Connect up the signals QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit ,this, &clsModHelper::onExitModule); QObject::connect(this, SIGNAL(connected()), this, SLOT(onConnected())); QObject::connect(this, SIGNAL(connected()), this, SLOT(onSendModuleStartupMsg())); QObject::connect(this, SIGNAL(connected()), this, SLOT(onStartHeartbeat())); QObject::connect(this, SIGNAL(disconnected()), this, SLOT(onDisconnected())); QObject::connect(this, &QAbstractSocket::errorOccurred, this, &clsModHelper::onErrorOccurred); QObject::connect(this, &QIODevice::readyRead, this, &clsModHelper::onDataIn); QObject::connect(this, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64))); QObject::connect(this, &clsModHelper::terminateModule, this, &clsModHelper::onExitModule); qdbg() << state(); //Install custom message handler clsDebugService::serviceDebugQueue(cpszTitle, true, true); //Build up title for output to the console qdbg() << QString::asprintf("%s Version: %.2lf", cpszTitle, mdblVersion); connectToXMLMPAM(); }
The function connectToXMLMPAM():
/** * @brief clsModHelper::connectToXMLMPAM */ void clsModHelper::connectToXMLMPAM() { //Start of one-shot timer to connect to XMLMPAM QTimer::singleShot(clsModHelper::mscintModuleCycleFrequency, this, SLOT(onConnectToXMLMPAM())); }
If there is anything else I haven't posted, please let me know, I'm not seeing anything received by A, yet B keeps logging that its sent data and there are no errors.
-
@SPlatten did you remove
clsSocketServer::incomingConnection()
? -
@KroMignon yes
-
@SPlatten said in QTcpSocket / QTcpServer, connection closed:
yes
I am not sure you are understanding what you are doing, and that's bad.
The protected virtual function
QTcpServer::incomingConnection()
is called byQTcpServer
on each new connection request.
The default implementation of this function will create aQTcpSocket
instance with the connection informations and use theQTcpServer
as parent and finally add the new instance to the connection queue withaddPendingConnection()
.With this default implementation, all clients will run in same thread as
QTcpServer
, because QObject with a parent cannot be move to another thread, only the parent QObject.
The calladdPendingConnection()
will also trigger a signalQTcpServer::newConnection()
.
And finally you can retrieve to instance you have add to queue withQTcpServer::nextPendingConnection()
.This is why
QTcpServer::incomingConnection()
exists and how it has to be used.So again, please take time to read documentation of the classes you are using.
The documentation is not so bad!To summarize: in your first code, you are subclassing
incomingConnection()
but not add the new connection to pending queue withaddPendingConnection()
and finally waiting for signalQTcpServer::newConnection()
which will never be triggered. -
@KroMignon , What I'm struggling to get to grips with is getting the QTcpServer and QTcpSocket to work. I'm using the examples to prod and learn. I have written lots of socket based applications in the past, not Qt.
I am an experience programmer, the samples FortuneServer and FortuneClient do not use incomingConnection directly either, they are very lean examples. The examples to not call addPendingConnection either.
Initialisation of QTcpServer in FortuneServer:
tcpServer = new QTcpServer(this); if (!tcpServer->listen()) { QMessageBox::critical(this, tr("Fortune Server"), tr("Unable to start the server: %1.") .arg(tcpServer->errorString())); close(); return; } //! [0] QString ipAddress; QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); // use the first non-localhost IPv4 address for (int i = 0; i < ipAddressesList.size(); ++i) { if (ipAddressesList.at(i) != QHostAddress::LocalHost && ipAddressesList.at(i).toIPv4Address()) { ipAddress = ipAddressesList.at(i).toString(); break; } } // if we did not find one, use IPv4 localhost if (ipAddress.isEmpty()) ipAddress = QHostAddress(QHostAddress::LocalHost).toString(); statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n" "Run the Fortune Client example now.") .arg(ipAddress).arg(tcpServer->serverPort()));
The slot that is connected to the newConnection signal:
void Server::sendFortune() { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_10); out << fortunes[QRandomGenerator::global()->bounded(fortunes.size())]; QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); qDebug() << "Server::sendFortune clientConnection->isOpen(): " << clientConnection->isOpen(); qDebug() << "Server::sendFortune clientConnection->error(): " << clientConnection->error(); connect(clientConnection, &QAbstractSocket::disconnected, clientConnection, &QObject::deleteLater); clientConnection->write(block); clientConnection->disconnectFromHost(); }
The FortuneClient doesn't use addPendingConnection either.
In my application newConnection is getting called, I've said this before. Its sending data successfully that I'm now looking at as this isn't happening.
-
@SPlatten said in QTcpSocket / QTcpServer, connection closed:
What I'm struggling to get to grips with is getting the QTcpServer and QTcpSocket to work. I'm using the examples to prod and learn. I have written lots of socket based applications in the past, not Qt.
I am an experience programmer, the samples FortuneServer and FortuneClient do not use incomingConnection directly either, they are very lean examples. The examples to not call addPendingConnection either.I don't want to hurt you, explain by writing in a foreign language is not so easy, maybe my words are not everytime appropriated.
I also had many problems to start with Qt, even if I was developing embedded software more than 20 years before in C/C++/Assembler.
There is a philosophy to understand and accept.
I am trying to follow this simple rule "If you are fight against the framework, than you are doing something wrong!".I know there are many examples available, but most of them are outdated, but as Qt is open source, you can always take a look behind the scene and look what the code really does:
void QTcpServer::incomingConnection(qintptr socketDescriptor) { #if defined (QTCPSERVER_DEBUG) qDebug("QTcpServer::incomingConnection(%i)", socketDescriptor); #endif QTcpSocket *socket = new QTcpSocket(this); socket->setSocketDescriptor(socketDescriptor); addPendingConnection(socket); }
My big problem is that I don't really understand what do you want to achieve with your TCP server. I think you have a "bad start". That is normal, s***t happens ;)
I would love to help you, but I am a little lost, because I don't really understand your code and I can't understand the final result you want to have.
Create a TCP server with Qt is very simple, it takes less than 100 line codes. -
@KroMignon , I'm starting to think the problem is not source code related but another factor. In the example I am looking at FortuneServer there is a fundamental difference. It gets the port to use automatically, where as in my code I am using a specific port 8123.
I've got the source to both FortuneServer and my own side by side and FortuneServer is much simpler in that it only implements one signal from QTcpServer "newConnection".
When a new client connection in the FortuneServer occurs it calls in the sendFortune slot:
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
The difference is that the clientConnection status of isOpen is true, where as in my own slot, it's false.
In my slot that is called on a new Connection:
void clsSocketServer::sayHello() { clsSocketClient* pClient = new clsSocketClient(nextPendingConnection()); if ( pClient != nullptr ) { //Construct the message to send QJsonObject objMsg; objMsg.insert(clsJSON::mscszModule, clsMainWnd::mscstrTitle); pClient->sendJSON(objMsg); } }
In the function sendJSON:
void clsSocketClient::sendJSON(QJsonObject& objJSON) { //Associate this TCP socket with the output data stream QByteArray arybytMsg; QDataStream dsOut(&arybytMsg, QIODevice::WriteOnly); dsOut.setVersion(clsJSON::mscintQtVersion); //Send message to data stream dsOut << QJsonDocument(objJSON).toJson(QJsonDocument::Compact); //Write message qint64 int64Written = write(arybytMsg); //Make sure the data is written now flush(); qdbg() << "sendJSON: " << int64Written; }
The call to flush was just to ensure that the written content is not held in a buffer instead of calling disconnect, because I want the connection to remain open. But "int64Written" is -1 and I see in the "Application Output":
device not open
[Edit] Observing something odd...in my constructor for clsSocketClient I output:
qdbg() << __FILE__ << "," << __LINE__ << ",isOpen: " << pClient->isOpen();
This results in:
D00000000000000000020:../clsSocketClient.cpp,28,isOpen: true
Ignore the D number thats just my own message prefix, 28 is the line number, the at this point the socket is open. Then when it returns from the constructor and calls the function sendJSON:
void clsSocketClient::sendJSON(QJsonObject& objJSON) { //Associate this TCP socket with the output data stream QByteArray arybytMsg; QDataStream dsOut(&arybytMsg, QIODevice::WriteOnly); dsOut.setVersion(clsJSON::mscintQtVersion); //Send message to data stream dsOut << QJsonDocument(objJSON).toJson(QJsonDocument::Compact); //Write message qint64 int64Written = write(arybytMsg); //Make sure the data is written now flush(); //disconnectFromHost(); //Is this required? qdbg() << "sendJSON: " << int64Written; }
The "Application Output" shows:
W00000000000000000022:QIODevice::write (clsSocketClient): device not open D00000000000000000023:sendJSON: -1
-
@SPlatten No this cannot work.
As far as I can understand your code
clsSocketClient
is as subclass ofQTcpSocket
.
So simple C++ error:auto pClient = new clsSocketClient(nextPendingConnection());
is calling QTcpSocket copy constructoris using the QTcpSocket as parent.One solution would be to use
incomingConnection
to create the instance and save it in the queue:/** * @brief clsSocketService::incomingConnection * @param sfd : Client Socket Descriptor */ void clsSocketServer::incomingConnection(qintptr sfd) { auto pClient = new clsSocketClient(this); pClient->setSocketDescriptor(sfd); addPendingConnection(pClient); }
And then:
void clsSocketServer::sayHello() { auto pClient = qobject_cast<clsSocketClient*>(nextPendingConnection()); if ( pClient != nullptr ) { //Construct the message to send QJsonObject objMsg; objMsg.insert(clsJSON::mscszModule, clsMainWnd::mscstrTitle); pClient->sendJSON(objMsg); } }
-
@KroMignon, thank you, for some reason I'm having some problems with the line:
clsSocketClient* pClient = new clsSocketClient(this);
no matching constructor for initialization of 'clsSocketClient', the header is present, I've even tried changing the constructor to expect QAbstractSocket*, still the same.
-
@SPlatten said in QTcpSocket / QTcpServer, connection closed:
clsSocketClient
How does its constructors look like?
-
@SPlatten said in QTcpSocket / QTcpServer, connection closed:
no matching constructor for initialization of 'clsSocketClient', the header is present, I've even tried changing the constructor to expect QAbstractSocket*, still the same.
I don't know what
clsSocketClient
is.
I supposed it was:class clsSocketClient : public QTcpSocket { Q_OBJECT .... };
-
@KroMignon , clsSocketClient:
class clsSocketClient : public QTcpSocket { Q_OBJECT public: explicit clsSocketClient(QTcpSocket* pClient = nullptr); ...
Implementation:
clsSocketClient::clsSocketClient(QTcpSocket* pClient) : QTcpSocket(pClient) { QObject::connect(this, &QTcpSocket::connected, this, &clsSocketClient::onConnected); QObject::connect(this, &QTcpSocket::bytesWritten, this, &clsSocketClient::onBytesWritten); QObject::connect(this, &QTcpSocket::disconnected, this, &QObject::deleteLater); QObject::connect(this, &QTcpSocket::disconnected, this, &clsSocketClient::onDisconnected); QObject::connect(this, &QTcpSocket::errorOccurred, this, &clsSocketClient::onErrorOccurred); QObject::connect(this, &QTcpSocket::readyRead, this, &clsSocketClient::onDataIn); QObject::connect(this, &QTcpSocket::stateChanged, this, &clsSocketClient::onStateChanged); }
-
@SPlatten said in QTcpSocket / QTcpServer, connection closed:
explicit clsSocketClient(QTcpSocket* pClient = nullptr);
I guess "this" is not QTcpSocket right? So, how can that work?
-
@jsulm , this is instance of clsSocketServer which is:
class clsSocketServer : public QTcpServer { Q_OBJECT ...
As I said I tried changing the clsSocketClient to accept QAbstractSocket* but still the same, aren't QTcpServer and QTcpSocket both based on QAbstractSocket ?
-
@SPlatten I'm reffering to this:
clsSocketClient* pClient = new clsSocketClient(this);
What is "this" here? If it is not QTcpSocket then it can't work!
-
@jsulm , I did answer, this is clsSocketServer which is derived from QTcpServer.
-
@jsulm ,@KroMignon , sorted, it's now working and communicating, thank you:
void clsSocketServer::incomingConnection(qintptr sfd) { clsSocketClient* pClient = new clsSocketClient(new QTcpSocket()); pClient->setSocketDescriptor(sfd); addPendingConnection(pClient); }
-
@SPlatten said in QTcpSocket / QTcpServer, connection closed:
class clsSocketClient : public QTcpSocket {
Q_OBJECTpublic: explicit clsSocketClient(QTcpSocket* pClient = nullptr);
...
Please change this to:
class clsSocketClient : public QTcpSocket { Q_OBJECT public: explicit clsSocketClient(QObject *parent = nullptr);
-
This post is deleted! -
@SPlatten said in QTcpSocket / QTcpServer, connection closed:
void clsSocketServer::incomingConnection(qintptr sfd) {
clsSocketClient* pClient = new clsSocketClient(new QTcpSocket());
pClient->setSocketDescriptor(sfd);
addPendingConnection(pClient);
}Why to you do this?
clsSocketClient* pClient = new clsSocketClient(new QTcpSocket());
is an nonsense!