@dmitttri
[SOLVED]
I am posting the solution for the original post:
Client had an issue, after socket write call it's required to call sync waitForBytesWritten, which will process actual socket writing
Server had an issue, in the run method, we can't just call socket read, it's required to call waitForReadyRead which takes care to process socket and fill it with incoming data.
Thanks everyone for their suggestions and help!
I am also posting working sample code, which is only meant for demo purposes, to show a simple synchronous approach to TCP client and server
DEMO TCP CLIENT AND SERVER WITH THREADS
MainClient.cpp
#include "SimpleClient.h"
int main(int argc, char *argv[])
{
SimpleClient client;
if (!client.connectToServer("127.0.0.1", 9000))
{
return -1;
}
if (!client.pokeServer("Hello Server"))
{
return -1;
}
return 0;
}
SImpleClient.h
#ifndef SIMPLECLIENT_H
#define SIMPLECLIENT_H
#include <QTcpSocket>
#include <QAbstractSocket>
#define SOCKET_OPERATION_TIMEOUT_MS 3000
class SimpleClient
{
public:
SimpleClient();
bool connectToServer(QString serverIP, qint16 serverPort);
int pokeServer(const char *pokeMsg);
private:
QTcpSocket *socket = nullptr;
};
#endif // SIMPLECLIENT_H
SImpleClient.cpp
#include <QByteArray>
#include "SimpleClient.h"
SimpleClient::SimpleClient()
{
this->socket = new QTcpSocket;
}
bool SimpleClient::connectToServer(QString serverIP, qint16 serverPort)
{
qDebug() << "Connecting to" << serverIP << ":" << serverPort;
socket->connectToHost(QHostAddress(serverIP), serverPort);
if (!socket->waitForConnected(SOCKET_OPERATION_TIMEOUT_MS))
{
qDebug() << socket->errorString();
return false;
}
qDebug() << "Connected";
return true;
}
int SimpleClient::pokeServer (const char *pokeMsg)
{
qDebug() << "Sending :" << QString(pokeMsg);
this->socket->write(pokeMsg, strlen(pokeMsg));
if (!this->socket->waitForBytesWritten(SOCKET_OPERATION_TIMEOUT_MS))
{
qDebug() << socket->errorString();
return false;
}
if (!this->socket->waitForReadyRead(SOCKET_OPERATION_TIMEOUT_MS))
{
qDebug() << this->socket->errorString();
return false;
}
QByteArray serverReply = this->socket->readAll();
QString replyString(serverReply.data());
qDebug() << "Received:" << replyString;
qDebug() << "Success";
return true;
}
MainServer.cpp
#include <QCoreApplication>
#include <QtCore>
#include "SimpleServer.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
SimpleServer server;
server.start("127.0.0.1", 9000);
return app.exec();
}
SimpleServer.h
#ifndef SIMPLESERVER_H
#define SIMPLESERVER_H
#include <QDebug>
#include <QTcpServer>
class SimpleServer : public QTcpServer
{
Q_OBJECT;
public:
SimpleServer(QObject *parent = nullptr);
bool start(QString serverIP, qint16 serverPort);
protected:
void incomingConnection(qintptr socketDescriptor) override; \
};
#endif // SIMPLESERVER_H
SimpleServer.cpp
#include <QHostAddress>
#include "SimpleServer.h"
#include "ServerJobThread.h"
SimpleServer::SimpleServer(QObject *parent)
: QTcpServer(parent) {}
bool SimpleServer::start(QString serverIP, qint16 serverPort)
{
qDebug() << "Awaiting for new connections "
<< "IP" << serverIP
<< "PORT" << serverPort;
this->listen(QHostAddress(serverIP), serverPort);
return true;
}
void SimpleServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << "New incoming connection";
ServerJobThread *thread = new ServerJobThread(socketDescriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
ServerJobThread.h
#ifndef SERVERJOBTHREAD_H
#define SERVERJOBTHREAD_H
#include <QThread>
#include <QTcpSocket>
class ServerJobThread : public QThread
{
Q_OBJECT
public:
ServerJobThread(qintptr socketDescriptor, QObject *parent);
void run() override;
private:
qintptr socketDescriptor;
};
#endif // SERVERJOBTHREAD_H
ServerJobThread.cpp
#include <QByteArray>
#include <QTime>
#include "ServerJobThread.h"
#define SOCKET_OPERATION_TIMEOUT_MS 3000
#define THREAD_MAX_RUNNING_TIME_S 5
#define THREAD_ID this->currentThreadId()
ServerJobThread::ServerJobThread(qintptr socketDescriptor, QObject *parent)
: QThread(parent)
{
this->socketDescriptor = socketDescriptor;
}
void ServerJobThread::run()
{
bool workerThreadShouldRun = true;
QTcpSocket tcpSocket;
if (!tcpSocket.setSocketDescriptor(this->socketDescriptor))
{
qDebug() << THREAD_ID << "setSocketDescriptor fail";
return;
}
QTime workerThreadTimeoutTime = QTime::currentTime().addSecs(THREAD_MAX_RUNNING_TIME_S);
while (workerThreadShouldRun)
{
if (tcpSocket.bytesAvailable() == 0)
{
tcpSocket.waitForReadyRead(SOCKET_OPERATION_TIMEOUT_MS);
}
else
{
QByteArray buffer = tcpSocket.readAll();
if (buffer.size() > 0)
{
qDebug() << THREAD_ID
<< "Received:" << QString(buffer.data());
qDebug() << THREAD_ID
<< "Sending :" << QString("Hello Client");
tcpSocket.write("Hello Client");
tcpSocket.waitForBytesWritten(SOCKET_OPERATION_TIMEOUT_MS);
workerThreadShouldRun = false;
}
if (QTime::currentTime() > workerThreadTimeoutTime)
{
qDebug() << THREAD_ID
<< "Thread max running time expired";
workerThreadShouldRun = false;
}
}
}
qDebug() << THREAD_ID << "Thread finished";
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(Simple-TCP-Client-Server LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS core Network)
add_executable(Server
ServerJobThread.h
ServerJobThread.cpp
SimpleServer.h
SimpleServer.cpp
MainServer.cpp
)
target_link_libraries(Server Qt6::Core Qt6::Network)
add_executable(Client
SimpleClient.h
SimpleClient.cpp
MainClient.cpp
)
target_link_libraries(Client Qt6::Core Qt6::Network)
CLIENT STDOUT
$ ./Client
Connecting to "127.0.0.1" : 9000
Connected
Sending : "Hello Server"
Received: "Hello Client"
Success
SERVER STDOUT
$ ./Server
Awaiting for new connections IP "127.0.0.1" PORT 9000
New incoming connection
0x9a60 Received: "Hello Server"
0x9a60 Sending : "Hello Client"
0x9a60 Thread finished