PDA

View Full Version : Write to Threaded Server



Suncell
2nd June 2010, 21:46
Hello, i got stuck while trying writing to a threaded server. Receiving works fine but the other way around...
Method readData() at tcpsocket.cpp is never called. What am I doing wrong?

Server:



MultiServer server;
if (!server.listen(QHostAddress::Any, 15000)) {
close();
return;
}


multiserver.cpp


MultiServer::MultiServer(QObject *parent)
: QTcpServer(parent)
{

}

void MultiServer::incomingConnection(int socketDescriptor)
{
QString msg = "Message from server";
ConnectionThread *thread = new ConnectionThread(socketDescriptor, msg, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}


connectionthread.cpp


ConnectionThread::ConnectionThread(int socketDescriptor, const QString &msg, QObject *parent)
: QThread(parent)
, socketDescriptor(socketDescriptor)
, text(msg)
{

}

void ConnectionThread::run()
{
my_sock = new CTcpSocket(socketDescriptor);

my_sock->writeData(text);
}


tcpsocket.cpp


CTcpSocket::CTcpSocket(int socketDescriptor, QObject *parent)
: QObject(parent)
, m_socketDescriptor(socketDescriptor)
{
m_tcpSocket = new QTcpSocket();

if (!m_tcpSocket->setSocketDescriptor(m_socketDescriptor)) {
qDebug() << m_tcpSocket->error();
return;
}

connect(m_tcpSocket, SIGNAL(readyRead()), SLOT(readData()));
}


void CTcpSocket::readData()
{
//ToDo Implement reading Data
qDebug() << "try to read";
//m_tcpSocket->waitForReadyRead(10000);
}


void CTcpSocket::writeData(QString txt)
{
qDebug() << "try to write";

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << txt;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));

m_tcpSocket->write(block);

m_tcpSocket->waitForBytesWritten(10000);

m_tcpSocket->disconnectFromHost();
m_tcpSocket->waitForDisconnected();
}

numbat
3rd June 2010, 10:46
Why do you call disconnectFromHost if you want a reply?

Suncell
3rd June 2010, 16:49
ohh sure - but i fixed it already - this i shouldn't do of course. But it doesn't work anyway :-?

tcpsocket.cpp



CTcpSocket::CTcpSocket(int socketDescriptor, QObject *parent)
: QObject(parent)
, m_socketDescriptor(socketDescriptor)
{
m_tcpSocket = new QTcpSocket();

if (!m_tcpSocket->setSocketDescriptor(m_socketDescriptor)) {
qDebug() << m_tcpSocket->error();
return;
}

connect(m_tcpSocket, SIGNAL(readyRead()), SLOT(readData()));
}


void CTcpSocket::readData()
{
//ToDo Implement reading Data
qDebug() << "try to read";
//m_tcpSocket->waitForReadyRead(10000);
}


void CTcpSocket::writeData(QString txt)
{
qDebug() << "try to write";

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << txt;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));

m_tcpSocket->write(block);

m_tcpSocket->waitForBytesWritten(10000);
}

tbscope
3rd June 2010, 17:02
One reason you're not being able to read is because there's nothing to read.
Are you sure you are receiving data? Are you sure that what you send is correct?

Suncell
3rd June 2010, 17:44
There is something to read for sure - I've a working not threaded server. There the message arrives.

Client is like this:



InstructionGen::InstructionGen(..)
{
...
socket = new QTcpSocket(this);
...
socket->connectToHost(server->text(), port->value());
...
}

void InstructionGen::sendInstruction()
{
socket->write(prefix->text().toLatin1() + message->text().toLatin1() + "\n");
log_stat->append((prefix->text().toLatin1() + message->text().toLatin1() + "\n").simplified());
message->clear();
}

tbscope
3rd June 2010, 17:48
Where do you call void InstructionGen::sendInstruction() in your server?

Edit: and you don't need to use threads and the socket's blocking functions.

Suncell
3rd June 2010, 17:57
InstructionGen is the client, which should send data to the server.

Thx for the hint with the blocking functions... but should work anyway - am I right?

tbscope
3rd June 2010, 17:58
You have lost me completely.
Can you post the complete code please?

Suncell
3rd June 2010, 18:06
Okay: server is complete.

Now the Client: its actual the ChatSample Client in a slightly different form.

Client:

main.cpp


#include <QApplication>
#include "instructiongen.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
InstructionGen generator;
generator.show();
return a.exec();
}


instructiongen.cpp


#include "instructiongen.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QLabel>
#include <QLineEdit>
#include <QTcpSocket>
#include <QBuffer>
#include <QSpinBox>
#include <QErrorMessage>

static const quint16 DEFAULT_PORT = 15001;

InstructionGen::InstructionGen(QWidget* parent, Qt::WFlags flags)
: QWidget(parent, flags)
{
QVBoxLayout* main = new QVBoxLayout(this);
QGridLayout* top = new QGridLayout;
QHBoxLayout* bottom = new QHBoxLayout;

QLabel* label = new QLabel("Server:", this);
server = new QLineEdit(this);
port = new QSpinBox(this);
conn = new QPushButton("Connect", this);
port->setRange(1, 32767);
port->setValue(DEFAULT_PORT);
server->setText("localhost");
top->addWidget(label, 0, 0);
top->addWidget(server, 0, 1);
top->addWidget(port, 0, 2);

label = new QLabel("Prefix:", this);
prefix = new QLineEdit(this);
prefix->setText("sim.");
top->addWidget(label, 1, 0);
top->addWidget(prefix, 1, 1);
top->addWidget(conn, 1, 2);

log_stat = new QTextEdit(this);
log_stat->setReadOnly(true);

label = new QLabel("Instruction:", this);
message = new QLineEdit(this);
send = new QPushButton("Send", this);
send->setDefault(true);
bottom->addWidget(label);
bottom->addWidget(message);
bottom->addWidget(send);

main->addLayout(top);
main->addWidget(log_stat);
main->addLayout(bottom);
setLayout(main);

buffer = new QBuffer(this);
socket = new QTcpSocket(this);
buffer->open(QIODevice::ReadWrite);

connect(message, SIGNAL(returnPressed()), SLOT(sendInstruction()));
connect(send, SIGNAL(clicked()), SLOT(sendInstruction()));
connect(conn, SIGNAL(clicked()), SLOT(toggleConnection()));

connect(socket, SIGNAL(connected()), SLOT(setConnected()));
connect(socket, SIGNAL(disconnected()), SLOT(setDisconnected()));
connect(socket, SIGNAL(readyRead()), SLOT(receiveMessage()));

setDisconnected();
}

InstructionGen::~InstructionGen()
{
buffer->close();
}

void InstructionGen::setConnected()
{
port->setEnabled(false);
server->setEnabled(false);
prefix->setEnabled(true);
message->setEnabled(true);
log_stat->setEnabled(true);
log_stat->clear();
send->setEnabled(true);
conn->setText("Disconnect");
}

void InstructionGen::setDisconnected()
{
port->setEnabled(true);
server->setEnabled(true);
prefix->setEnabled(false);
message->setEnabled(false);
log_stat->setEnabled(false);
send->setEnabled(false);
conn->setText("Connect");
}

void InstructionGen::toggleConnection()
{
if (socket->state() == QAbstractSocket::UnconnectedState)
{
socket->connectToHost(server->text(), port->value());
}
else
{
socket->disconnectFromHost();
}
}

void InstructionGen::sendInstruction()
{
socket->write(prefix->text().toLatin1() + message->text().toLatin1() + "\n");
log_stat->append((prefix->text().toLatin1() + message->text().toLatin1() + "\n").simplified());
message->clear();
}

void InstructionGen::receiveMessage()
{
// missing some checks for returns values for the sake of simplicity
qint64 bytes = buffer->write(socket->readAll());
// go back as many bytes as we just wrote so that it can be read
buffer->seek(buffer->pos() - bytes);
// read only full lines, line by line
while (buffer->canReadLine())
{
QString line = buffer->readLine();
log_stat->append("received: "+line.simplified());
}
}

tbscope
3rd June 2010, 20:29
Here's your problem

connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
Do you understand why?

If you want to keep it simple, I've just added an example to the wiki on how to do all this without using threads.
A lot of people seem to think they need threads for some reason.
http://www.qtcentre.org/wiki/index.php?title=Server_with_multiple_clients_witho ut_threads

Suncell
3rd June 2010, 21:17
Outsch yeah - thx.

And I need to loop in the run method - right?

Why isn't a thread necessary. The program will receive tons of data out of a medical device. It should be prevent in any case blocking biosignal analysis.
Or doesn't it matter?

tbscope
3rd June 2010, 21:28
You might want to have an event loop in your thread.


It should be prevent in any case blocking biosignal analysis
And yet you use blocking functions :-)

It doesn't really matter. In general, do not use threads for your connections. Do use them if you need to perform long tasks in parallel (send or receive a lot of data is not one of those tasks as it is received in packets).
And if you don't use threads, don't use the blocking functions at all. Don't use them in threads either. The blocking functions are there for a few special cases where the client or server doesn't allow you to communicate asynchronously.

Suncell
4th June 2010, 21:10
^^ yeah I should remove them.

But now - I'm still stuck. The readData() function is never called. Maybe deriving the Class CTcpSocket from QObject() is wrong?

Now, server code looks like that:

Server:



MultiServer server;
if (!server.listen(QHostAddress::Any, 15000)) {
close();
return;
}


MultiServer.cpp


MultiServer::MultiServer(QObject *parent)
: QTcpServer(parent)
{

}

void MultiServer::incomingConnection(int socketDescriptor)
{
QString msg = "Message from server";
ConnectionThread *thread = new ConnectionThread(socketDescriptor, msg, this);
thread->start();
}


ConnectionThread.cpp


ConnectionThread::ConnectionThread(int socketDescriptor, const QString &msg, QObject *parent)
: QThread(parent)
, socketDescriptor(socketDescriptor)
, text(msg)
{

}

void ConnectionThread::run()
{
my_sock = new CTcpSocket(socketDescriptor);

my_sock->writeData(text);

while(1)
{

}
}


tcpsocket.cpp


CTcpSocket::CTcpSocket(int socketDescriptor, QObject *parent)
: QObject(parent)
, m_socketDescriptor(socketDescriptor)
{
m_tcpSocket = new QTcpSocket();

if (!m_tcpSocket->setSocketDescriptor(m_socketDescriptor)) {
qDebug() << m_tcpSocket->error();
return;
}

connect(m_tcpSocket, SIGNAL(readyRead()), SLOT(readData()));
}


void CTcpSocket::readData()
{
//ToDo Implement reading Data
qDebug() << "try to read";
}


void CTcpSocket::writeData(QString txt)
{
qDebug() << "try to write";

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << txt;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));

m_tcpSocket->write(block);
}

numbat
5th June 2010, 09:39
Instead of "while (1)" which just loops forever and doesn't allow anything else to happen in that thread, yoiu need to run "exec()". This starts the event loop for a thread and will allow your events to run. Note, it is your responsibility to call "exit()" when you want event processing (and the thread) to finish.

Suncell
6th June 2010, 00:30
okay instead of while I use now exec(). Am I doing it right with the exit()?

The connect works fine and also the reading of the incoming data.
But writing to the socket by emitting a signal does not work - Cannot create children for a parent that is in a different thread - is the error. I tried moving several objects to several threads by using moveToThread(currentThread()). What should I move to which place?

multiserver.cpp


MultiServer::MultiServer(QObject *parent)
: QTcpServer(parent)
{

}

void MultiServer::incomingConnection(int socketDescriptor)
{
ConnectionThread *thread = new ConnectionThread(socketDescriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}


ConnectionThread.h


#ifndef CONNECTIONTHREAD_H
#define CONNECTIONTHREAD_H

#include <QThread>
#include <QTcpSocket>

#include <QMutex>
#include <QWaitCondition>

#include "tcpsocket.h"

class ConnectionThread : public QThread
{
Q_OBJECT

public:
ConnectionThread(int socketDescriptor, QObject *parent);
~ConnectionThread();

void run();

void stop();

signals:
void error(QTcpSocket::SocketError socketError, const QString &message);
void dataAvailable(QString &msg);

private slots:
void readData();
void writeData(QString &msg);

private:
int socketDescriptor;
QString text;

QMutex mutex;
QWaitCondition cond;

QBuffer* buffer;

QTcpSocket* m_tcpSocket;
};
#endif


ConnectionThread.cpp


#include "ConnectionThread.h"

#include <QtNetwork>

ConnectionThread::ConnectionThread(int socketDescriptor, QObject *parent)
: QThread(parent)
, socketDescriptor(socketDescriptor)
{
qDebug() << currentThreadId ();//Thread Y
}

ConnectionThread::~ConnectionThread ()
{
stop();
}

void ConnectionThread::stop()
{
exit();
}

void ConnectionThread::run()
{
qDebug() << currentThreadId();//Thread X - Different thread ID than all others


m_tcpSocket = new QTcpSocket();

buffer = new QBuffer();
buffer->open(QIODevice::ReadWrite);


if (!m_tcpSocket->setSocketDescriptor(socketDescriptor)) {
emit error(m_tcpSocket->error(), m_tcpSocket->errorString());
return;
}

connect(m_tcpSocket, SIGNAL(readyRead()), SLOT(readData()));
connect(this, SIGNAL(dataAvailable(QString &)), SLOT(writeData(QString &)));

exec();
}

void ConnectionThread::readData()
{
qDebug() << "data arrived";
qDebug() << currentThreadId (); // Thread Y

qint64 bytes = buffer->write(m_tcpSocket->readAll());
buffer->seek(buffer->pos() - bytes);
while (buffer->canReadLine())
{
QString line = buffer->readLine();
//log_stat->append("received: "+line.simplified());
qDebug() << line;
}

QString msg = "reply code";

emit dataAvailable(msg);//here is the Error -Cannot create children for a parent that is in a different thread - emitted
}


void ConnectionThread::writeData(QString &msg)
{
qDebug() << msg;
qDebug() << currentThreadId (); //Thread Y
qint64 blockSize = msg.size();
m_tcpSocket->write(msg.toAscii().data(),blockSize);
}






AND just for Information:
Working version without signal and slots - but with a strict order of reading and writing

ConnectionThread.cpp version 2


#include "ConnectionThread.h"

#include <QtNetwork>

ConnectionThread::ConnectionThread(int socketDescriptor, QObject *parent)
: QThread(parent)
, socketDescriptor(socketDescriptor)
{
}


void ConnectionThread::run()
{
while(1)// ToDo no quit
{
// int st;
// switch(tcpSocket.state())
// {
// case QTcpSocket::UnconnectedState:
// st= 0;
// break;
// case QTcpSocket::HostLookupState:
// st= 1;
// break;
// case QTcpSocket::ConnectingState:
// st= 2;
// break;
// case QTcpSocket::ConnectedState:
// st= 3;
// break;
// case QTcpSocket::BoundState:
// st= 4;
// break;
// case QTcpSocket::ListeningState:
// st= 5;
// break;
// case QTcpSocket::ClosingState:
// st= 6;
// break;
// }
// qDebug() << st;


while (tcpSocket.bytesAvailable() < (int)sizeof(quint16)) {
if (!tcpSocket.waitForReadyRead(Timeout)) {
qDebug() << "Nothing arrived!";
if(tcpSocket.state()!=QTcpSocket::ConnectedState)
{
qDebug() << "Connection broken!";
emit error(tcpSocket.error(), tcpSocket.errorString());
return;
}
continue;
}
}

char *buffer = (char*) malloc (1024);
quint64 n = tcpSocket.read(buffer,1024);



mutex.lock();
QString txt = QString(buffer).mid(0,n);

qDebug() << txt;

//cond.wait(&mutex); //wait condition to wake up thread -> obsolete waiting above for data arriving - maybe connect with incoming data

mutex.unlock();


QString msg = "Hello, world - here is rtproc! Give me some data to process! \n";//Todo just for debug purpose
qint64 blockSize = msg.size();//Todo just for debug purpose

tcpSocket.write(msg.toAscii().data(),blockSize);//Todo just for debug purpose
tcpSocket.flush();//Todo just for debug purpose
}


// tcpSocket.disconnectFromHost();
// tcpSocket.waitForDisconnected();
}

Suncell
6th June 2010, 20:24
Found a very useful thread in the forum: http://www.qtcentre.org/threads/11971-Threaded-TCP-server

But writing to the client is now a problem.... :confused:

Code looks now like that:

multiserver.cpp


#include "multiserver.h"

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

void MultiServer::incomingConnection(int socketDescriptor)
{
server = new ServerThread(socketDescriptor, this);
connect(server, SIGNAL(finished()), server, SLOT(deleteLater()));
server->start();
}


serverthread.cpp


#include "serverthread.h"

ServerThread::ServerThread(int socketDescriptor, QObject *parent)
: QThread(parent)
, m_socketDescriptor(socketDescriptor)
{
qDebug() << currentThreadId ();
}

ServerThread::~ServerThread ()
{
stop();
}

void ServerThread::stop()
{
exit();
}

void ServerThread::run()
{
serverSocket = new ServerTcpSocket();

if (!serverSocket->setSocketDescriptor(m_socketDescriptor)) {
emit error(serverSocket->error(), serverSocket->errorString());
qDebug() << "setSocketDescriptor error : " << serverSocket->error() << endl;
return;
}

exec();
}


servertcpsocket.cpp


#include "servertcpsocket.h"

ServerTcpSocket::ServerTcpSocket(QObject *parent)
: QTcpSocket(parent)
{
buffer = new QBuffer();
buffer->open(QIODevice::ReadWrite);

connect(this, SIGNAL(readyRead()), this, SLOT(readData()));

connect(this, SIGNAL(disconnected()), this, SLOT(deleteLater()));
}


void ServerTcpSocket::readData()
{
qint64 bytes = buffer->write(this->readAll());
buffer->seek(buffer->pos() - bytes);
while (buffer->canReadLine())
{
QString line = buffer->readLine();
qDebug() << line;
}

emit dataAvailable();
}


Any clue how to solve the writing to the client :rolleyes:

Suncell
9th June 2010, 17:21
Actually writing to the client does work, but with annoying messages.
When I try to write a reply immediately after receiving a message. (For debug purpose it is solved with a connect.)
Then I got always "QObject: Cannot create children for a parent that is in a different thread." But reply message is send anyway. So i could not care about, but its a kind of distrubing. This error appears with and without moving to thread. Is there a way to fix this problem?

serverthread.cpp


#include "serverthread.h"

ServerThread::ServerThread(int socketDescriptor, QObject *parent)
: QThread(parent)
, m_socketDescriptor(socketDescriptor)
{
qDebug() << currentThreadId ();
}

ServerThread::~ServerThread ()
{
stop();
}

void ServerThread::stop()
{
exit();
}

void ServerThread::run()
{
serverSocket = new ServerTcpSocket();

if (!serverSocket->setSocketDescriptor(m_socketDescriptor)) {
emit error(serverSocket->error(), serverSocket->errorString());
qDebug() << "setSocketDescriptor error : " << serverSocket->error() << endl;
return;
}

connect(serverSocket, SIGNAL(dataAvailable()), this, SLOT(writeData()));

exec();
}

void ServerThread::writeData()
{
serverSocket->moveToThread(currentThread());
//QObject::moveToThread: Current thread (0x1b102fc0) is not the object's thread (0x1b01b6a0).
//Cannot move to target thread (0x1b01b6a0)

m_mutex.lock();

QString msg = "Message from Server\n";
qint64 blockSize = msg.size();

serverSocket->write(msg.toAscii().data(),blockSize);
// QObject: Cannot create children for a parent that is in a different thread.
//(Parent is QNativeSocketEngine(0x103df1b0), parent's thread is ServerThread(0x103c2910), current thread is QThread(0x101966a0)
m_mutex.unlock();
}

Suncell
9th June 2010, 19:46
Solved it finally!
Now writing and reading are running in one different thread :cool:

I'm thankful for every improvement. :rolleyes:

Now the Source code:
multiserver.cpp


#include "multiserver.h"

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

MultiServer::~MultiServer()
{
delete server;
}

void MultiServer::incomingConnection(int socketDescriptor)
{
qDebug() << "MultiServer thread " << QThread::currentThreadId ();

server = new ServerThread(socketDescriptor);
connect(server, SIGNAL(finished()), server, SLOT(deleteLater()));
server->start();
}


serverthread.cpp


#include "serverthread.h"

ServerThread::ServerThread(int socketDescriptor)
: QThread()
, m_socketDescriptor(socketDescriptor)
{
QObject::moveToThread(this); //destroy the obsolete thread
qDebug() << "ServerThread " << QThread::currentThreadId ();
}

ServerThread::~ServerThread ()
{
stop();
delete serverSocket;
}

void ServerThread::stop()
{
this->quit();
this->wait();
}

void ServerThread::run()
{
serverSocket = new ServerTcpSocket();

if (!serverSocket->setSocketDescriptor(m_socketDescriptor)) {
emit error(serverSocket->error(), serverSocket->errorString());
qDebug() << "setSocketDescriptor error : " << serverSocket->error() << endl;
return;
}

connect(serverSocket, SIGNAL(dataAvailable()), this, SLOT(writeData()));

this->exec();
}

void ServerThread::writeData()
{

qDebug() << "writing thread" << currentThreadId ();

QString msg = "Message from Server\n";//Todo just for debug purpose
qint64 blockSize = msg.size();//Todo just for debug purpose

serverSocket->write(msg.toAscii().data(),blockSize);
}


servertcpsocket.cpp


#include "servertcpsocket.h"

ServerTcpSocket::ServerTcpSocket(QObject *parent)
: QTcpSocket(parent)
{
buffer = new QBuffer();
buffer->open(QIODevice::ReadWrite);

connect(this, SIGNAL(readyRead()), this, SLOT(readData()));

connect(this, SIGNAL(disconnected()), this, SLOT(deleteLater()));
}


void ServerTcpSocket::readData()
{
qDebug() << "reading thread" << QThread::currentThreadId ();
qint64 bytes = buffer->write(this->readAll());
buffer->seek(buffer->pos() - bytes);
while (buffer->canReadLine())
{
QString line = buffer->readLine();
qDebug() << line;
}

emit dataAvailable();
}