PDA

View Full Version : Threaded TCP server



Sysace
20th February 2008, 13:48
Hello,

I'm writing an application to view MRI Images sent over a TCP connection. My program works well in a single thread but the problems arise when I try to run the server part in a separate thread.

I get the error "-1" when I try to set the descriptor of the socket :


if (clientSocket->setSocketDescriptor(socketId))
{
emit error(clientSocket->error());
cout << "setSocketDescriptor error : " << clientSocket->error() << endl;
return;
}


In the main window, I create a server object which starts listening :


if (!server->listen(QHostAddress(QHostAddress::Any/*ipAddress*/), serverPort)) {
cerr << "Failed to open bind port" << endl;
}


Here is my implementation of the Server class :


#include "server.h"
#include "serverthread.h"
#include <iostream>

using namespace std;

Server::Server(QObject *parent)
: QTcpServer(parent)
{
}


void Server::incomingConnection(int socketId)
{
cout << " socketID : " << socketId << endl;
ServerThread *serverThread = new ServerThread(socketId, NULL);
connect(serverThread, SIGNAL(finished()), serverThread, SLOT(deleteLater()));
emit clientConnected();
serverThread->start();
}


The code of the ServerThread class :


#include "serverthread.h"
#include "clientsocket.h"
#include <QtNetwork>
#include <iostream>

using namespace std;

ServerThread::ServerThread(int socketDescriptor, QObject *parent)
: QThread(parent), socketId(socketId)
{
socketId = socketDescriptor;
}

void ServerThread::run()
{
cout << " socketID 2 : " << socketId << endl;
ClientSocket *clientSocket = new ClientSocket(/*this*/NULL);
if (clientSocket->setSocketDescriptor(socketId)) {
emit error(clientSocket->error());
cout << "setSocketDescriptor error : " << clientSocket->error() << endl;
return;
}
}


And finally the ClientSocket class :


#include <QtNetwork>
#include <cstdlib>
#include <iostream>

#include "clientsocket.h"

using namespace std;

ClientSocket::ClientSocket(QObject *parent)
: QTcpSocket(parent)
{
connect(this, SIGNAL(readyRead()), this, SLOT(readClient()));
connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(displayError(QAbstractSocket::SocketError)));
connect(this, SIGNAL(disconnected()), this, SLOT(deleteLater()));
}

void ClientSocket::readClient()
{
QFile dumpFile("/home/meylan/QT/IRMServer/dump.log");
dumpFile.open(QIODevice::WriteOnly);
QDataStream dump(&dumpFile);
QDataStream in(this);
qint64 bytesDispo;
qint8 data;

in.setVersion(QDataStream::Qt_4_1);

while (this->waitForReadyRead(500)) {
bytesDispo = this->bytesAvailable();
cout << endl << "Taille du paquet : " << bytesDispo << endl;
for (int i = 0; i < bytesDispo; i++) {
in >> data;
dump << data;
}
}
dumpFile.close();
close();
}

void ClientSocket::displayError(QAbstractSocket::Socket Error socketError)
{
switch (socketError) {
case QAbstractSocket::RemoteHostClosedError:
cout << "Remote host closed !" << endl;
break;
case QAbstractSocket::HostNotFoundError:
cout <<"Host not found !" << endl;
break;
case QAbstractSocket::ConnectionRefusedError:
cout << "Connection refused !" << endl;
break;
default:
cout << this->error() << endl;
}
}


Do you have any idea of what I'm doing wrong ?

Thanks in advance for your help.

jpn
20th February 2008, 16:01
Hello,

Quoting QAbstractSocket::setSockerDescriptor() docs:

Returns true if socketDescriptor is accepted as a valid socket descriptor; otherwise returns false.
So I guess it should be:


if (!clientSocket->setSocketDescriptor(socketId))
{
...
}

Sysace
20th February 2008, 16:40
Thank you for your quick reply !

Damn, you're absolutely right, I don't know who has eaten that "!" :D

Still, the problem remains, as I cannot receive the data transferred. My program seems to act as if the readyRead signal was not emitted. I guess the problem is related to the way I coded the thread since the same code works with only one thread (in this case there is no ServerThread class and the clientSocket is created in the server class).

By the way, I had to replace this by NULL here :



ClientSocket *clientSocket = new ClientSocket(/*this*/NULL);


to avoid the Cannot create children for a parent that is in a different thread. error. I guess this is not the correct way to avoid the problem...

Can you tell me if my way of creating the thread is the good one ?

I will check the entire code again to try to figure what makes the difference between the working single thread code and this one.

Thank you very much.

Best regards.

jpn
20th February 2008, 16:53
ServerThread::run() should look more or less something like this:


void ServerThread::run()
{
cout << " socketID 2 : " << socketId << endl;
ClientSocket clientSocket; // <-- exec() blocks so it's ok to allocate on the stack
if (!clientSocket.setSocketDescriptor(socketId)) {
emit error(clientSocket.error());
cout << "setSocketDescriptor error : " << clientSocket.error() << endl;
return;
}
exec(); // <-- enter to the event loop
}

Sysace
21st February 2008, 12:37
Thank you for your help.

The cause of the error was the missing

exec();
instruction.

Best regards.