PDA

View Full Version : Multiple clients and one server using QTcpServer



deby25
20th March 2019, 08:14
Hello everyone,
This is my first post.and i am new in Qt programming. I wrote a QT GUI program which send files from client to server through QTcpServer and QTcpSocket..I need to modified my code to multiple clients..I tried multi-threading ,,but thread crashed.Is multi-threading only way to handle it?..or can I use any other method?

anda_skoa
20th March 2019, 12:54
You don't need multi-threading, Qt can handle multiple connections in parallel as long as you are using event driven I/O.

Each client connection will result in an emit of the newConnection() signal of QTcpServer, you can then handle each socket from nextPendingConnection() the same way.

Cheers,
_

deby25
3rd April 2019, 08:51
Thanks..please give me example or link for multiple client without multi thread..

anda_skoa
3rd April 2019, 13:41
I suggest you post the code you currently have for handling one client.

Much easier to discuss change than to create some example that then does not relate to your code at all.

Cheers,
_

deby25
7th April 2019, 12:30
Yes Sir,
I am sending my code..both server and client...If Client send text file ,Then code is working fine..but there is problem with pdf file,image file other than text file,..There is problem with the size difference with the file send and receive.my client is Qt-widget and Server is Qt-console.
// myserver.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QTcpServer>
#include "mythread.h"

#define CLIENT_FILE "/home/User1/ClientFile/"

class MyServer : public QTcpServer
{
Q_OBJECT
public:
explicit MyServer(QObject *parent = 0);
~MyServer();

void startServer();
signals:

public slots:
private:
int server_status;

QTcpServer *tcpServer;
// QTcpSocket *tcpServerConnection;



protected:
void incomingConnection(qintptr socketDescriptor);

};

#endif // MYSERVER_H


// mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QDebug>

#include <QtNetwork>
#include <QTcpSocket>
#include <QObject>
#include <QByteArray>
#include <QDebug>

#include <QFile>
#include <QString>
#include <QTextStream>
#include <QIODevice>

#include <QDir>
#include <QTextCodec>

#define CLIENT_FILE "/home/User1/ClientFile/"


class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(qintptr ID, QObject *parent = 0);

void run();

signals:
void error(QTcpSocket::SocketError socketerror);

public slots:
//void readyRead();
void disconnected();


private slots:

void acceptConnection();
void slotReadClient();


private:

QTcpServer *tcpServer;
QTcpSocket *tcpServerConnection;

qintptr socketDescriptor;


QTcpSocket *m_socket;
QString fileName;
QString fileSize;
//QListWidget *showWidget;
//QGridLayout *layout;
bool isInfoGot = false;
int server_status;

qint32 realFileSize;
void sendMessage(int code);


};

#endif // MYTHREAD_H


// myserver.cpp

#include "myserver.h"
#include "mythread.h"

#define CLIENT_PORT 33333
#define SERVER_PORT 44444

MyServer::MyServer(QObject *parent) :
QTcpServer(parent)
{
}
MyServer::~MyServer()
{
server_status=0;
}

void MyServer::startServer()
{

if (!this->listen(QHostAddress::Any, SERVER_PORT) && server_status==0)


{
qDebug() << QObject::tr("Unable to start the server: %1.").arg(tcpServer->errorString());
// printf("Server_status is:%d\n",server_status);
}
else
{
server_status=1;
qDebug() << QString::fromUtf8("Server is running");

}


printf("Console start the server\n");
}

void MyServer::incomingConnection(qintptr socketDescriptor)
{
printf("MyServer::incomingConnection\n");
qDebug() << socketDescriptor << " Thread is Connecting...";


QDir::setCurrent(CLIENT_FILE);


MyThread *thread = new MyThread(socketDescriptor, this);

connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

thread->start();
}







// mythread.cpp

#include "mythread.h"
#include <QDir>

#define CLIENT_FILE "/home/User1/ClientFile/"

#define CLIENT_PORT 33333
#define SERVER_PORT 44444

MyThread::MyThread(qintptr ID, QObject *parent) :
QThread(parent)
{
this->socketDescriptor = ID;
//tcpServerConnection = new QTcpSocket();
}

void MyThread::run()
{
// thread starts here
qDebug() << "MyThread::run()" << " Thread started";


tcpServer = new QTcpServer(this);
tcpServerConnection = new QTcpSocket();


// set the ID
if(!tcpServerConnection->setSocketDescriptor(this->socketDescriptor))
{
// something's wrong, we just emit a signal
emit error(tcpServerConnection->error());
return;
}

// connect socket and signal
// note - Qt::DirectConnection is used because it's multithreaded
// This makes the slot to be invoked immediately, when the signal is emitted.

//tcpServer = new QTcpServer(this);
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
if (!tcpServer->listen(QHostAddress::Any, SERVER_PORT) && server_status==0)
// if (!tcpServer->listen(QHostAddress::Any, 44444) && server_status==0)


{
// qDebug() << QObject::tr("Unable to start the server: %1.").arg(tcpServer->errorString());
printf("Server_status is:%d\n",server_status);
}
else
{
server_status=1;
printf("Server_status is:%d\n",server_status);

}

connect(tcpServerConnection, SIGNAL(readyRead()), this, SLOT(slotReadClient()), Qt::DirectConnection);

connect(tcpServerConnection, SIGNAL(disconnected()), this, SLOT(disconnected()));

// We'll have multiple clients, we want to know which is which
qDebug() << socketDescriptor << " 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();
}
void MyThread::acceptConnection()
{
printf("MyThread::acceptConnection()\n");

//Check the connection of Sever
qDebug() << "MainWindow::acceptConnection()";
qDebug() << QString::fromUtf8("Accept the connection");
tcpServerConnection = tcpServer->nextPendingConnection();
connect(tcpServerConnection,SIGNAL(readyRead()),th is, SLOT(slotReadClient()),Qt::DirectConnection);
// tcpServer->close();

QDir::setCurrent(CLIENT_FILE);
//QDir::setCurrent(/user)

}

void MyThread::slotReadClient()
{

qDebug() << "MainWindow::slotReadClient()";
QDataStream in(tcpServerConnection);
QByteArray tmpByteArray;

if (!isInfoGot) {
isInfoGot = true;
in >> fileName;
qDebug() << "Received File name:" <<fileName ;
in >> fileSize;
qDebug() <<"Received File size:" << fileSize;
}
/*************/
//check Duplication of files

QString filePath= CLIENT_FILE + fileName;

qDebug () << "file naem is :" << filePath;

if(QFile::exists(filePath))
{
//int qret;
qDebug() << "File is alreday exist,so Overwrite the file";
printf("File is alreday exist,so Overwrite the file\n");
QFile::remove(filePath);


}
/************************/

QFile loadedFile(fileName);

if (loadedFile.open(QIODevice::Append))
{
while (tcpServerConnection->bytesAvailable())
{
qDebug() << "bytesAvailable:" << tcpServerConnection->bytesAvailable();
in >> tmpByteArray;
QString some(tmpByteArray);
if (some=="#END")
{
isInfoGot = false;
QFileInfo fileInfo(loadedFile);
qDebug() << "loaded File size:" << loadedFile.size();
//printf( "loaded File size=%ld \n",loadedFile.size());
if (loadedFile.size() == fileSize.toInt())
{
// QMessageBox::information(this,"Informations","Sucessfully uploaded to Server");
qDebug() << "Success upload to Server ";
printf( "Success upload to Server \n");

sendMessage(1);
//return 1;
}
else
{
// QMessageBox::warning(this,"Warning","Failed to upload the File to Server");
qDebug() << "Fail upload to Server";

sendMessage(0);
//return 0;
}

break;
}
loadedFile.write(tmpByteArray);
}
loadedFile.close();
}


// get the information
/* QByteArray Data = tcpServerConnection->readAll();

// will write on server side window
qDebug() << socketDescriptor << " Data in: " << Data;

tcpServerConnection->write(Data);*/
}
void MyThread::sendMessage(int code)
{

QTcpSocket *sock = new QTcpSocket(this);

sock->connectToHost("127.0.0.1", CLIENT_PORT);
QDataStream out(sock);
sock->write(QString::number(code).toUtf8().constData());

}

void MyThread::disconnected()
{


qDebug() << socketDescriptor << " Disconnected";

tcpServerConnection->deleteLater();
exit(0);
}






//main.c
#include <QCoreApplication>
#include "myserver.h"
#include "mythread.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);


MyServer server;
server.startServer();

return a.exec();
}

client code

//client programe
widget.cpp

#include "widget.h"
#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include <QProcess>
#include <QAction>
#include <QIcon>
#include <QFileInfo>
#include <QFileDialog>
#include <QMessageBox>
#include <QAbstractSocket>
#include <QLineEdit>


#define SERVER_FILE "/home/User1/ServerFile/"
#define CLIENT_PORT 33330
#define SERVER_PORT 44440

Widget::Widget(QWidget *parent) :
QWidget(parent)
{


tcpSocket = new QTcpSocket(this);


NameLine= new QLineEdit (this);
NameLine->setPlaceholderText("Enter The User Name");


sendBtn = new QPushButton(this);
sendBtn->setText("Send");

showBtn = new QPushButton(this);
showBtn->setText("Show");


showWidget =new QListWidget(this);



Glayout = new QGridLayout;

// Glayout->addWidget(NameLabel,0,0,1,1);
Glayout->addWidget(NameLine,0,0,1,2);
Glayout->addWidget(showWidget,1,0,1,2);
Glayout->addWidget(showBtn,2,0,1,1);
Glayout->addWidget(sendBtn,2,1,1,1);

setLayout(Glayout);

connect(showBtn, &QPushButton::clicked, this, &Widget::fileShow);
connect(sendBtn, &QPushButton::clicked, this, &Widget::onSend);



}

Widget::~Widget()
{
}

void Widget::fileOpened()
{
qDebug() << " Widget::fileOpened() ";
fileName = QFileDialog::getOpenFileName(this, tr("Open file"));
QFileInfo fileInfo(fileName);
//fileLabel->setText(fileInfo.fileName() + " : " + QString::number(fileInfo.size()));
qDebug() << "Opening The file:" <<fileName;
}

void Widget::onSend()
{

QString UserName;

/**********************************/
if(NameLine->text().isEmpty())
{
QMessageBox::warning(this,"Warning","Please Enter the User Name");
qDebug() << " not User Name ";


}

else
{


QString fileName1;
if(showWidget->selectedItems().count()!=1)
{
QMessageBox::warning(this,"Warning","Please Select a File From List");
return;
}
else
{
//start of Else

QMessageBox::information(this,tr("Information"),tr("%1 file is selected").arg(showWidget->currentItem()->text()));

//1.Send File first
fileName1= SERVER_FILE + showWidget->currentItem()->text();;
qDebug() << "1.Full File Name:" << fileName ;

//File 1:Transfer User Name as file


QFileInfo fileInfo1(fileName);
// fileLabel->setText(fileInfo.fileName() + " : " + QString::number(fileInfo.size()));
qDebug() << "2.File name with Text Label:" << fileName1;

// tcpSocket->connectToHost("172.16.5.250", CLIENT_PORT);
tcpSocket->connectToHost("127.0.0.1", CLIENT_PORT);
QFile file1(fileName1);
qDebug() << "3.File open is:" << fileName1 ;

QDataStream out1(tcpSocket);
int size1 = 0;

if (file1.open(QIODevice::ReadOnly))
{
QFileInfo fileInfo1(file1);
QString fileName1(fileInfo1.fileName());

out1 << fileName1;
qDebug() << "File name is:" << fileName1;
out1 << NameLine->text();

// qDebug() << "username name is:" << fileName1;
out1 << QString::number(fileInfo1.size());
qDebug() << "File size is:" <<fileInfo1.size();

while (!file1.atEnd())
{
QByteArray rawFile1;
rawFile1 = file1.read(5000);
//false size inc
QFileInfo rawFileInfo1(rawFile1);
size1 += rawFileInfo1.size();
out1 << rawFile1;
qDebug() << "ToSend:"<< rawFile1.size();
}

out1 << "#END";

startServer();
}



} //End of else

} //End of else user name
}


void Widget::startServer()
{
qDebug() << "Widget::startServer().";
qDebug() << "waiting for reply...";
tcpServer = new QTcpServer(this);
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(acceptReplyConnection()));
if (!tcpServer->listen(QHostAddress::Any, SERVER_PORT))
{

//QMessageBox::warning(this,"Warning","Unable to start the server.");
qDebug() << QObject::tr("Unable to start the server: %1.").arg(tcpServer->errorString());
}
else
{
QMessageBox::information(this,"Information","Server is connected");
//qDebug() << QString::fromUtf8("Server is connected");
}
}


void Widget::acceptReplyConnection()
{
qDebug() << "Widget::acceptReplyConnection()";
qDebug() << QString::fromUtf8("Waiting for connection");
tcpServerConnection = tcpServer->nextPendingConnection();
connect(tcpServerConnection,SIGNAL(readyRead()),th is, SLOT(slotReadClient()));
tcpServer->close();

}


void Widget::slotReadClient()
{
qDebug() << "Widget::slotReadClient() ";
QDataStream in(tcpServerConnection);
QByteArray tmpArr;
QString tmpString;
in << tmpArr;
qDebug() << "bytesAvailable:" << tcpServerConnection->bytesAvailable();
qDebug() << tmpArr;


if (tmpString.toInt()==1)
{
qDebug() << "successfully uploaded to Server";
}
else
{
qDebug() << "fail to upload to server";
}
}

void Widget::fileShow()
{

qDebug() << " Widget::fileShow() ";

QStringList list_item;
QDir dir(SERVER_FILE);

//Check Whether Name is blank or not

if(NameLine->text().isEmpty())
{
QMessageBox::warning(this,"Warning","Please Enter the User Name");
qDebug() << " not User Name ";

}

else
{
if(!dir.exists())
{
// qDebug() << " not exists ";
QMessageBox::warning(this,"Warning","Cant find the any files of Server path");
// qWarning("canot find the directory");
}
else
{
// qDebug() << " It exists ";
dir.setFilter(QDir::Files | QDir::NoSymLinks );

QFileInfoList list=dir.entryInfoList();

for(int i=0;i<list.size();i++)
{
QFileInfo fileinfo=list.at(i);
list_item << fileinfo.fileName();


}

}

showWidget->clear();
showWidget->addItems(list_item);
} //EnD else for user name

}

//widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QFileDialog>
#include <QGridLayout>
#include <QPushButton>
#include <QDebug>
#include <QString>
#include <QLabel>
#include <QNetworkAccessManager>
#include <QTcpSocket>
#include <QFile>
#include <QDataStream>
#include <QProgressBar>
#include <QTcpServer>
#include <QListWidget>


namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = 0);
QFileDialog *fileDialog;
QGridLayout *layout;
QPushButton *fileBtn;
QPushButton *sendBtn;
QPushButton *showBtn;
QLabel *fileLabel;
QLabel *NameLabel;
QLineEdit *NameLine;
QLabel *progressLabel;
QProgressBar *progressBar;
QTcpServer *tcpServer;
QTcpSocket *tcpServerConnection;

QGridLayout *Glayout;

QString fileName;
QTcpSocket *tcpSocket;
QListWidget *showWidget;

void startServer();

~Widget();
public slots:
void fileOpened();
void onSend();
void acceptReplyConnection();
void slotReadClient();
void fileShow();
void displayError(QAbstractSocket::SocketError socketError);
};

#endif // WIDGET_H

//main.c
#include "widget.h"
#include <QApplication>
#include <QLabel>
#include<QPixmap>
#include <QDesktopWidget>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.setFixedWidth(300);
w.setFixedHeight(200);
w.show();


return a.exec();*
}

anda_skoa
7th April 2019, 13:27
Ok, this definitely does not need threads on the server side, but that part seems to be reasonably done.




// myserver.h

QTcpServer *tcpServer;
// QTcpSocket *tcpServerConnection;

Why is there a QTcpServer member inside the QTcpServer subclass?



// mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QDebug>

#include <QtNetwork>
#include <QTcpSocket>
#include <QObject>
#include <QByteArray>
#include <QDebug>

#include <QFile>
#include <QString>
#include <QTextStream>
#include <QIODevice>

#include <QDir>
#include <QTextCodec>

Oh my, so many unnecessary includes



QTcpServer *tcpServer;
QTcpSocket *tcpServerConnection;


Why on earth does the client handler contain a QTcpServer?



// myserver.cpp

That looks reasonably ok.



// mythread.cpp

void MyThread::slotReadClient()
{

qDebug() << "MainWindow::slotReadClient()";
QDataStream in(tcpServerConnection);
QByteArray tmpByteArray;

if (!isInfoGot) {
isInfoGot = true;
in >> fileName;

You are not covering the case that there is not yet enough data for the full string.



qDebug() << "Received File name:" <<fileName ;
in >> fileSize;

The client writes the file name, then a user name, then the file size.
Where are you reading the user name here?

And again, the available data might not be sufficient enough yet.



QString some(tmpByteArray);
if (some=="#END")

Well, that will break if you transmit binary data or text data that contains this string.

Totally unnecessary since you know the file size and thus know when you have read all of the file content.




void MyThread::sendMessage(int code)
{

QTcpSocket *sock = new QTcpSocket(this);

sock->connectToHost("127.0.0.1", CLIENT_PORT);
QDataStream out(sock);
sock->write(QString::number(code).toUtf8().constData());

}

Why don't you simply sent through the connection you already have?



// tcpSocket->connectToHost("172.16.5.250", CLIENT_PORT);
tcpSocket->connectToHost("127.0.0.1", CLIENT_PORT);

connectToHost() is an asynchronous operation.
You need to connect to the socket's conncted() signal and then continue there.



out1 << NameLine->text();

That is never read on the server side



out1 << QString::number(fileInfo1.size());

Why send the number as a string?



QByteArray rawFile1;
rawFile1 = file1.read(5000);
//false size inc
QFileInfo rawFileInfo1(rawFile1);

You create a QFileInfo object using the 5000 bytes of file1 content as a file name?



void Widget::startServer()

Totally unnecessary, the server can simply reply on the already established connection



void Widget::slotReadClient()
{
qDebug() << "Widget::slotReadClient() ";
QDataStream in(tcpServerConnection);
QByteArray tmpArr;
QString tmpString;
in << tmpArr;

Why send an empty byte array to the server?



if (tmpString.toInt()==1)

This will always be false because tmpString is empty.

I would suggest the following steps

1) Clean up the code, there are way too many commented out parts, duplicated or unnecessary includes, etc.

2) Ensure that the data serialization and deserialization code matches. Currently the client sends three strings before the file, the server reads only two.

3) In a slot connected to readyRead() you can't assume you are seeing a single write from the client. E.g. the client could write a block with 10 bytes, the server could received this as two blocks. you will need a bit more protocol to handle that.

Cheers,
_

deby25
8th April 2019, 05:03
Thanks..i will go through my code once again according to your suggestion.