Sending messages from a client to server using network on the same machine
-
The example is much harder than I thought of! :|
I had to simplify the examples as much as possible to get the topic and can get them to work as expected. I'm sorry that I send the examples part of which may be repetitive.
(The purpose is the same: connecting client to the server and then sending messages to it)
client.h
class QTcpSocket; class Client : public QObject { Q_OBJECT public: explicit Client(QObject *parent = nullptr); public slots: void sendMessage(QString, QString); private: QTcpSocket* tcpSocket { nullptr }; QDataStream in; };
client.cpp
#include "client.h" #include <QtNetwork> Client::Client(QObject *parent) : QObject{parent} , tcpSocket(new QTcpSocket(this)) { in.setDevice(tcpSocket); in.setVersion(QDataStream::Qt_4_0); } void Client::sendMessage(QString ip, QString port) { tcpSocket->abort(); tcpSocket->connectToHost(ip, port.toInt()); }
main.qml
Window { width: 300 height: 200 visible: true title: qsTr("Client") color: "lightblue" ColumnLayout { anchors.fill: parent TextField { id: ipAddrs } TextField { id: portNum } RowLayout { Layout.alignment: Qt.AlignBottom TextField { id: txtField Layout.fillWidth: true } Button { text: qsTr("Send") onClicked: myObject.sendMessage(ipAddrs.text.toString(), portNum.text.toString()) } } } MyObject{ id: myObject } }
server.h
class QTcpServer; class QTcpSocket; class Server : public QObject { Q_OBJECT public: explicit Server(QObject *parent = nullptr); public slots: QString initServer(); QString setMessage(); void onNewConnection(); private: QTcpServer* tcpServer { nullptr }; QTcpSocket* tcpSocket { nullptr }; QDataStream in; };
server.cpp
#include "server.h" #include <QtNetwork> #include <QtCore> Server::Server(QObject *parent) : QObject{parent} , tcpServer(new QTcpServer(this)) , tcpSocket(new QTcpSocket(this)) { initServer(); in.setDevice(tcpSocket); in.setVersion(QDataStream::Qt_4_0); connect(tcpServer, &QTcpServer::newConnection, this, &Server::onNewConnection); } QString Server::initServer() { tcpServer = new QTcpServer(this); if(!tcpServer->listen()) return "Server Unable to start the server: " + tcpServer->errorString(); QString ipAddress; QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); // use the first non-local 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(); return " The server is running on\n\n IP: " + ipAddress + "\n port: " + QString::number(tcpServer->serverPort()) + "\n\n Run the Client example now."; } void Server::onNewConnection() { QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); connect(clientConnection, &QAbstractSocket::disconnected, clientConnection, &QObject::deleteLater); connect(clientConnection, &QAbstractSocket::readyRead, this, &Server::setMessage); } QString Server::setMessage() { in.startTransaction(); QString message; in >> message; if (!in.commitTransaction()) return "commitTransaction error" ; return message; }
main.qml
Window { width: 300 height: 200 visible: true title: qsTr("Server") color: "lightblue" ColumnLayout { anchors.fill: parent Label { text: myObject.initServer() } Label { id: msgLabel text: myObject.setMessage() } } MyObject{ id: myObject } }
For now I have only a question. Are the projects to this point fine? I mean I know that there may be many things to be added to them but isn't anything redundant up to here?
-
The example is much harder than I thought of! :|
I had to simplify the examples as much as possible to get the topic and can get them to work as expected. I'm sorry that I send the examples part of which may be repetitive.
(The purpose is the same: connecting client to the server and then sending messages to it)
client.h
class QTcpSocket; class Client : public QObject { Q_OBJECT public: explicit Client(QObject *parent = nullptr); public slots: void sendMessage(QString, QString); private: QTcpSocket* tcpSocket { nullptr }; QDataStream in; };
client.cpp
#include "client.h" #include <QtNetwork> Client::Client(QObject *parent) : QObject{parent} , tcpSocket(new QTcpSocket(this)) { in.setDevice(tcpSocket); in.setVersion(QDataStream::Qt_4_0); } void Client::sendMessage(QString ip, QString port) { tcpSocket->abort(); tcpSocket->connectToHost(ip, port.toInt()); }
main.qml
Window { width: 300 height: 200 visible: true title: qsTr("Client") color: "lightblue" ColumnLayout { anchors.fill: parent TextField { id: ipAddrs } TextField { id: portNum } RowLayout { Layout.alignment: Qt.AlignBottom TextField { id: txtField Layout.fillWidth: true } Button { text: qsTr("Send") onClicked: myObject.sendMessage(ipAddrs.text.toString(), portNum.text.toString()) } } } MyObject{ id: myObject } }
server.h
class QTcpServer; class QTcpSocket; class Server : public QObject { Q_OBJECT public: explicit Server(QObject *parent = nullptr); public slots: QString initServer(); QString setMessage(); void onNewConnection(); private: QTcpServer* tcpServer { nullptr }; QTcpSocket* tcpSocket { nullptr }; QDataStream in; };
server.cpp
#include "server.h" #include <QtNetwork> #include <QtCore> Server::Server(QObject *parent) : QObject{parent} , tcpServer(new QTcpServer(this)) , tcpSocket(new QTcpSocket(this)) { initServer(); in.setDevice(tcpSocket); in.setVersion(QDataStream::Qt_4_0); connect(tcpServer, &QTcpServer::newConnection, this, &Server::onNewConnection); } QString Server::initServer() { tcpServer = new QTcpServer(this); if(!tcpServer->listen()) return "Server Unable to start the server: " + tcpServer->errorString(); QString ipAddress; QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); // use the first non-local 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(); return " The server is running on\n\n IP: " + ipAddress + "\n port: " + QString::number(tcpServer->serverPort()) + "\n\n Run the Client example now."; } void Server::onNewConnection() { QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); connect(clientConnection, &QAbstractSocket::disconnected, clientConnection, &QObject::deleteLater); connect(clientConnection, &QAbstractSocket::readyRead, this, &Server::setMessage); } QString Server::setMessage() { in.startTransaction(); QString message; in >> message; if (!in.commitTransaction()) return "commitTransaction error" ; return message; }
main.qml
Window { width: 300 height: 200 visible: true title: qsTr("Server") color: "lightblue" ColumnLayout { anchors.fill: parent Label { text: myObject.initServer() } Label { id: msgLabel text: myObject.setMessage() } } MyObject{ id: myObject } }
For now I have only a question. Are the projects to this point fine? I mean I know that there may be many things to be added to them but isn't anything redundant up to here?
When I run both projects, I get this issue for the client project:
QIODevice::read (QTcpSocket): device not open and the error message: commitTransaction error
on the server's project's user interface. Let's for now ignore these.I type the IP and port numbers provided by the server UI into the text fields of the client project's UI and write "Hi" in the bottom text field and click on the button Send there for which the sendMessage slot is called and it connectes to the server successfully.
void Client::sendMessage(QString ip, QString port) { tcpSocket->abort(); tcpSocket->connectToHost(ip, port.toInt()); }
Afterwards, we've this connection in the Server project's constructor:
connect(tcpServer, &QTcpServer::newConnection, this, &Server::onNewConnection);
As well as:in.setDevice(tcpSocket); in.setVersion(QDataStream::Qt_4_0);
The connection above calls
onNewConnection
slot since there's a new connection signal.void Server::onNewConnection() { QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); connect(clientConnection, &QAbstractSocket::disconnected, clientConnection, &QObject::deleteLater); connect(clientConnection, &QAbstractSocket::readyRead, this, &Server::setMessage); }
In that function the setMessage slot is called:
QString Server::setMessage() { in.startTransaction(); QString message; in >> message; if (!in.commitTransaction()) return "commitTransaction error" ; return message; }
in
is already set to the socket in the constructor, so it's expected that it's able to get the message sent ('Hi') and return that message to the server's label front-end part where that slot is called (too)!
But in reality no message is shown on the server's UI!Could you please tell me where the first mistake is in the code so that I can firstly fix it and then we go for the others?
-
First thing: you initialize your QDataStream on the wrong socket, it shall use the socket matching the connection that was established.
-
First thing: you initialize your QDataStream on the wrong socket, it shall use the socket matching the connection that was established.
Do you mean to move
in.setDevice(tcpSocket); in.setVersion(QDataStream::Qt_4_0);
from the server's constructor into the onNewConnection slot this way, please?
void Server::onNewConnection() { QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); connect(clientConnection, &QAbstractSocket::disconnected, clientConnection, &QObject::deleteLater); in.setDevice(clientConnection); in.setVersion(QDataStream::Qt_4_0); connect(clientConnection, &QAbstractSocket::readyRead, this, &Server::setMessage); }
The error message when running the projects both at the same time: QDataStream: No transaction in progress
-
Do you mean to move
in.setDevice(tcpSocket); in.setVersion(QDataStream::Qt_4_0);
from the server's constructor into the onNewConnection slot this way, please?
void Server::onNewConnection() { QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); connect(clientConnection, &QAbstractSocket::disconnected, clientConnection, &QObject::deleteLater); in.setDevice(clientConnection); in.setVersion(QDataStream::Qt_4_0); connect(clientConnection, &QAbstractSocket::readyRead, this, &Server::setMessage); }
The error message when running the projects both at the same time: QDataStream: No transaction in progress
-
Do you also use QDataStream to prepare the data to send ?
-
Yes, you're right. I partly changed all six files to match the requirements as follows:
The server part:
server.h
:class Server : public QObject { Q_OBJECT public: explicit Server(QObject *parent = nullptr); public slots: QString initServer(); void setMessage(); QString getMessage() const; void onNewConnection(); private: QTcpServer* tcpServer { nullptr }; QDataStream in; QString message; };
server.cpp
:Server::Server(QObject *parent) : QObject{parent} , tcpServer(new QTcpServer(this)) { initServer(); connect(tcpServer, &QTcpServer::newConnection, this, &Server::onNewConnection); } QString Server::initServer() { //.. Provide the IP address and port number for the client } void Server::onNewConnection() { QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); connect(clientConnection, &QAbstractSocket::disconnected, clientConnection, &QObject::deleteLater); in.setDevice(clientConnection); in.setVersion(QDataStream::Qt_4_0); connect(clientConnection, &QAbstractSocket::readyRead, this, &Server::setMessage); } void Server::setMessage() { in.startTransaction(); QString msg; in >> msg; if (!in.commitTransaction()) message = "commitTransaction error" ; else message = msg; } QString Server::getMessage() const { return message; }
server's qml file
:ColumnLayout { anchors.fill: parent Label { text: myObj.initServer() } Label { id: msgLabel text: myObj.getMessage() } } ServerClass{ id: myObj }
The client part:
client.h
:class Client : public QObject { Q_OBJECT public: explicit Client(QObject *parent = nullptr); public slots: void sendAddress(QString, QString); void sendMessage(const QString&); private: QTcpSocket* tcpSocket { nullptr }; QDataStream out; };
client.cpp
:Client::Client(QObject *parent) : QObject{parent} , tcpSocket(new QTcpSocket(this)) { out.setDevice(tcpSocket); out.setVersion(QDataStream::Qt_4_0); } void Client::sendAddress(QString ip, QString port) { tcpSocket->abort(); tcpSocket->connectToHost(ip, port.toInt()); } void Client::sendMessage(const QString& message) { out.startTransaction(); out << message; if (!out.commitTransaction()) return ; }
client's qml file
:ColumnLayout { anchors.fill: parent TextField { id: ipAddrs } TextField { id: portNum } Button { text: "Send Address" onClicked: myObj.sendAddress(ipAddrs.text.toString(), portNum.text.toString()) } RowLayout { Layout.alignment: Qt.AlignBottom TextField { id: txtField Layout.fillWidth: true } Button { text: qsTr("Send") onClicked: myObj.sendMessage(txtField.text) } } } ClientClass{ id: myObj } }
After running both projects this way, and typing the IP address and port number (given by the server UI) on the client UI and writing a text message there I click on the Send button, but nothing is shown on the server UI.
I'm almost sure now the goal is closer but there're still a number of mistakes that need to be worked out.
-
Are you using the Fortune client and server examples as a base ?
If so, please take a look at the server side implementation, QDataStream is used there to write the data in a QByteArray that is then written in the socket. The transaction handling happens on the receiving end. -
Are you using the Fortune client and server examples as a base ?
If so, please take a look at the server side implementation, QDataStream is used there to write the data in a QByteArray that is then written in the socket. The transaction handling happens on the receiving end.Are you using the Fortune client and server examples as a base ?
Partly.
QDataStream is used there to write the data in a QByteArray that is then written in the socket.
I didn't know QString wouldn't work, but anyway, it's now changed to:
void Client::sendMessage(const QString& message) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_10); out << message; }
But still no change in the result! :|
The transaction handling happens on the receiving end.
Yes, it's implemented in the setMessage() slot.
-
Are you using the Fortune client and server examples as a base ?
Partly.
QDataStream is used there to write the data in a QByteArray that is then written in the socket.
I didn't know QString wouldn't work, but anyway, it's now changed to:
void Client::sendMessage(const QString& message) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_10); out << message; }
But still no change in the result! :|
The transaction handling happens on the receiving end.
Yes, it's implemented in the setMessage() slot.
@qcoderpro said in Sending messages from a client to server using network on the same machine:
But still no change in the result!
Because sendMessage does not send anything.
It only writes the message into a QByteArray.
You also need to send that QByteArray through the socket. -
@qcoderpro said in Sending messages from a client to server using network on the same machine:
But still no change in the result!
Because sendMessage does not send anything.
It only writes the message into a QByteArray.
You also need to send that QByteArray through the socket.@jsulm
Yeah, right, so I added this line at the end of the slot:
tcpSocket->write(block);
But I think there's an earlier problem. No connection arrives. I set a debug in theonNewConnection()
slot in the server app but nothing is printed although I put the IP and port in the client and click on the "Send Address" button! :| -
@jsulm
Yeah, right, so I added this line at the end of the slot:
tcpSocket->write(block);
But I think there's an earlier problem. No connection arrives. I set a debug in theonNewConnection()
slot in the server app but nothing is printed although I put the IP and port in the client and click on the "Send Address" button! :|@qcoderpro Then you should debug on the client side
-
@qcoderpro Then you should debug on the client side
The client's sendAddress slot sends the IP and port number as a string and int respectively, based on this version. So that connection is expected to be received in the server's NewConnection() slot, but it doesn't, I don't know why! :|
-
The client's sendAddress slot sends the IP and port number as a string and int respectively, based on this version. So that connection is expected to be received in the server's NewConnection() slot, but it doesn't, I don't know why! :|
@qcoderpro said in Sending messages from a client to server using network on the same machine:
sendAddress slot sends the IP and port number
It does not send anything, it just calls connectToHost. Did you check what happens on client side? Is https://doc.qt.io/qt-5/qabstractsocket.html#connected emitted? Is https://doc.qt.io/qt-5/qabstractsocket.html#errorOccurred signal emitted?
-
@qcoderpro said in Sending messages from a client to server using network on the same machine:
sendAddress slot sends the IP and port number
It does not send anything, it just calls connectToHost. Did you check what happens on client side? Is https://doc.qt.io/qt-5/qabstractsocket.html#connected emitted? Is https://doc.qt.io/qt-5/qabstractsocket.html#errorOccurred signal emitted?
I set this in the client's sendAddress slot:
connect(tcpSocket, &QAbstractSocket::connected, []() { qDebug() << "Connected to the host"; });
And it prints that message on Application Output window. So the connection is assumed to be established correctly without errors. Right?
-
I set this in the client's sendAddress slot:
connect(tcpSocket, &QAbstractSocket::connected, []() { qDebug() << "Connected to the host"; });
And it prints that message on Application Output window. So the connection is assumed to be established correctly without errors. Right?
-
@qcoderpro I'd suggest that you go back to the fortune example and debug what is happening on the client and server side until you have a good understanding. I found it very useful when I was figuring out how Qt did sockets.
Do you have any background with sockets at all? If not, I wonder if it would be worth having a play around with something like Python first to firm up the ideas. The official Python docs on sockets are very good.
-
@qcoderpro I'd suggest that you go back to the fortune example and debug what is happening on the client and server side until you have a good understanding. I found it very useful when I was figuring out how Qt did sockets.
Do you have any background with sockets at all? If not, I wonder if it would be worth having a play around with something like Python first to firm up the ideas. The official Python docs on sockets are very good.
@Bob64
The fortune client/server apps are different from (at least) that point of view that the server sends messages to the client by a button on the client. It's way different from mine.
Well, these two QML apps are considerably simple but I don't know why we (helpers and me) can't work it out and get them to work! :(