QTcpSocket gets NULL after QTcpServer::nextPendingConnection() - c++

So I have a QTcpServer here (simplified version of Qt's Fortune Server Example). It was working fine earlier. Then I moved some things around and changed some code. Now my server crashes on start. As far as I can tell, after
tcpSocket = tcpServer->nextPendingConnection();
tcpSocket remains NULL. Thus all calls like tcpSocket->anyCall() will cause a seg fault. Application output shows:
QObject::connect: invalid null parameter
So my question is, why is tcpServer->nextPendingConnection() returning NULL
all of a sudden, when before I moved things around it worked just fine?
Here are the relevant parts of my code:
#include <QtWidgets>
#include <QtNetwork>
#include "server.h"
Server::Server(QWidget *parent)
: QDialog(parent), statusLabel(new QLabel), tcpServer(Q_NULLPTR), tcpSocket(Q_NULLPTR), networkSession(0), blockSize(0), userAuthenticated(false)
{
QNetworkConfigurationManager manager;
QNetworkConfiguration config = manager.defaultConfiguration();
networkSession = new QNetworkSession(config, this);
sessionOpened();
...
// GUI stuff here //
...
this->read_newClient();
}
void Server::sessionOpened()
{
tcpServer = new QTcpServer(this);
// some if else checks here //
tcpSocket = tcpServer->nextPendingConnection(); // problem here //
connect(tcpSocket, &QAbstractSocket::disconnected, tcpSocket, &QObject::deleteLater); // line that crashes //
}
void Server::read_newClient()
{
QString data;
if (!clientSocket->waitForReadyRead())
{
qDebug() << "Cannot read";
return;
}
data = readData();
}

To use nextPendingConnection you need incomming connection. Therefore you have two ways:
Connect to signal newConnection():
...
connect(tcpServer, &QTcpServer::newConnection, this, &Server::OnNewConnection);
...
void Server::OnNewConnection() {
if (tcpServer->hasPendingConnections()) {
tcpSocket = tcpServer->nextPendingConnection();
connect(tcpSocket, &QAbstractSocket::disconnected, tcpSocket, QObject::deleteLater);
}
}
Or use blocking call waitForNewConnection():
if (tcpServer->waitForNewConnection()) {
if (tcpServer->hasPendingConnections()) {
tcpSocket = tcpServer->nextPendingConnection();
connect(tcpSocket, &QAbstractSocket::disconnected, tcpSocket, QObject::deleteLater);
}
}
Do not forget call tcpServer->listen();

Related

QTcpSocket fails to transmit data when reconnecting to server after RemoteHostClosedError

See EDIT1 at the end of the question for a possible solution - It would be great if somebody could comment on my interpretation, so that I can understand better what's happening
I'm writing a simple TCP client, based on QTcpSocket and managed by a QStateMachine (connect to server -> transmit data -> if disconnected for any reason, reconnect to server).
I noticed that if the connection is shut down on the server side (client is notified with RemoteHostClosedError), after reconnection the QTcpSocket write() method succeeds but no data is transmitted on the wire - nothing is received by the server, and the bytesWritten() signal on the client side does not fire up.
I found in the documentation for error() signal (https://doc.qt.io/qt-5/qabstractsocket.html#error) that
When this signal is emitted, the socket may not be ready for a reconnect attempt. In that case,
attempts to reconnect should be done from the event loop".
I think I'm already ok with that, as the reconnection happens in one of the QStateMachine states, and QStateMachine should have its own event loop according to the QT docs.
Below some simplified code to reproduce the issue (sorry, not so minimal but I could not find a simpler way to show the problem):
testclient.h
#ifndef TESTCLIENT_H
#define TESTCLIENT_H
#include <QObject>
#include <QTcpSocket>
#include <QDebug>
#include <QStateMachine>
class TestClient : public QObject
{
Q_OBJECT
public:
explicit TestClient(QObject *parent = nullptr);
public slots:
void start();
signals:
// FSM events
void fsmEvtConnected();
void fsmEvtError();
private slots:
void onSocketConnected(); // Notify connection to TCP server
void onSocketDisconnected(); // Notify disconnection from TCP server
void onSocketBytesWritten(qint64 bytes); // Notify number of bytes written to TCP server
void onSocketError(QAbstractSocket::SocketError err);
// FSM state enter/exit actions
void onfsmConnectEntered();
void onfsmTransmitEntered();
void onfsmTransmitExited();
private:
// Member variables
QTcpSocket* m_socket; // TCP socket used for communications to server
QStateMachine* m_clientFsm; // FSM defining general client behaviour
private:
void createClientFsm(); // Create client FSM
};
#endif // TESTCLIENT_H
testclient.cpp
#include "testclient.h"
#include <QState>
#include <QThread> // Sleep
//-----------------------------------------------------------------------------
// PUBLIC METHODS
//-----------------------------------------------------------------------------
TestClient::TestClient(QObject *parent) : QObject(parent)
{
m_socket = new QTcpSocket(this);
connect(m_socket, SIGNAL(connected()),this, SLOT(onSocketConnected()));
connect(m_socket, SIGNAL(disconnected()),this, SLOT(onSocketDisconnected()));
connect(m_socket, SIGNAL(bytesWritten(qint64)),this, SLOT(onSocketBytesWritten(qint64)));
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
}
void TestClient::start()
{
createClientFsm();
m_clientFsm->start();
}
//-----------------------------------------------------------------------------
// TCP CONNECTION MANAGEMENT SLOTS
//-----------------------------------------------------------------------------
void TestClient::onSocketConnected()
{
qDebug() << "connected...";
emit fsmEvtConnected();
}
void TestClient::onSocketDisconnected()
{
qDebug() << "disconnected...";
emit fsmEvtError();
}
void TestClient::onSocketBytesWritten(qint64 bytes)
{
qDebug() << bytes << " bytes written...";
}
void TestClient::onSocketError(QAbstractSocket::SocketError err)
{
qDebug() << "socket error " << err;
}
//-----------------------------------------------------------------------------
// FSM MANAGEMENT
//-----------------------------------------------------------------------------
void TestClient::createClientFsm()
{
m_clientFsm = new QStateMachine(this);
// Create states
QState* sConnect = new QState();
QState* sTransmit = new QState();
// Add transitions between states
sConnect->addTransition(this, SIGNAL(fsmEvtConnected()), sTransmit);
sTransmit->addTransition(this, SIGNAL(fsmEvtError()), sConnect);
// Add entry actions to states
connect(sConnect, SIGNAL(entered()), this, SLOT(onfsmConnectEntered()));
connect(sTransmit, SIGNAL(entered()), this, SLOT(onfsmTransmitEntered()));
// Add exit actions to states
connect(sTransmit, SIGNAL(exited()), this, SLOT(onfsmTransmitExited()));
// Create state machine
m_clientFsm->addState(sConnect);
m_clientFsm->addState(sTransmit);
m_clientFsm->setInitialState(sConnect);
}
void TestClient::onfsmConnectEntered()
{
qDebug() << "connecting...";
m_socket->connectToHost("localhost", 11000);
// Wait for connection result
if(!m_socket->waitForConnected(10000))
{
qDebug() << "Error: " << m_socket->errorString();
emit fsmEvtError();
}
}
void TestClient::onfsmTransmitEntered()
{
qDebug() << "sending data...";
m_socket->write("TEST MESSAGE");
}
void TestClient::onfsmTransmitExited()
{
qDebug() << "waiting before reconnection attempt...";
QThread::sleep(2);
}
main.cpp
#include <QCoreApplication>
#include "testclient.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
TestClient client(&a);
client.start();
return a.exec();
}
To test, you can just launch netcat (nc -l -p 11000) , then close the nc process after receiving TEST MESSAGE and finally relaunch it again. The second time, TEST MESSAGE is not received, and we don't have the onSocketBytesWritten() printout, see below:
connecting...
connected...
sending data...
12 bytes written... <<<<<<<<<< Correct transmission, event fires up
socket error QAbstractSocket::RemoteHostClosedError
disconnected...
waiting before reconnection attempt...
connecting...
connected...
sending data... <<<<<<<<<< No transmission, event does not fire up, no socket errors!
EDIT1: I found out that if I create the QTcpSocket on connection and destroy it on disconnection, the problem does not happen. Is this the expected/proper way to use sockets?
Wouldn't it be possible instead to create the socket just once and just connect/disconnect? Maybe it is just a matter of flushing or cleaning up in a specific manner, but I could not find it so far.
Here are the modifications that make the code above work on server-side disconnection:
Move socket creation from class constructor to onfsmConnectEntered() - handler for entry in the "Connect" QState:
void TestClient::onfsmConnectEntered()
{
m_socket = new QTcpSocket(this);
connect(m_socket, SIGNAL(connected()),this, SLOT(onSocketConnected()));
connect(m_socket, SIGNAL(disconnected()),this, SLOT(onSocketDisconnected()));
connect(m_socket, SIGNAL(bytesWritten(qint64)),this, SLOT(onSocketBytesWritten(qint64)));
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
qDebug() << "connecting...";
m_socket->connectToHost("localhost", 11000);
// The rest of the method is the same
}
Delete the socket on disconnection, so that it is deallocated and will be created again on reconnection:
void TestClient::onSocketDisconnected()
{
qDebug() << "disconnected...";
m_socket->deleteLater();
m_socket = nullptr;
emit fsmEvtError();
}
Do not use waitForX methods as they block the event loop and prevent them from using that resource as the signals do not do their job correctly or the QStateMachine.
Considering the above, the solution is:
void TestClient::onfsmConnectEntered()
{
m_socket->connectToHost("localhost", 11000);
}
But even so your code has errors since it does not consider other cases such as:
If when you start the client the server is not running, your application will try to connect the error will be launched and nothing else.
If the server fails for a longer time than the 10000 ms timeout set to waitForConnected(), the same will happen as in the previous case.
Then the idea is to try to connect until you are sure of the connection and that can be done through a QTimer with an appropriate period.
testclient.h
#ifndef TESTCLIENT_H
#define TESTCLIENT_H
#include <QObject>
class QTcpSocket;
class QStateMachine;
class QTimer;
#include <QAbstractSocket>
class TestClient : public QObject
{
Q_OBJECT
public:
explicit TestClient(QObject *parent = nullptr);
public slots:
void start();
signals:
// FSM events
void fsmEvtConnected();
void fsmEvtError();
private slots:
void onSocketConnected(); // Notify connection to TCP server
void onSocketDisconnected(); // Notify disconnection from TCP server
void onSocketBytesWritten(qint64 bytes); // Notify number of bytes written to TCP server
void onSocketError(QAbstractSocket::SocketError err);
// FSM state enter/exit actions
void onfsmConnectEntered();
void onfsmTransmitEntered();
private:
// Member variables
QTcpSocket* m_socket; // TCP socket used for communications to server
QStateMachine* m_clientFsm; // FSM defining general client behaviour
QTimer* m_timer;
private:
void createClientFsm(); // Create client FSM
void tryConnect();
};
#endif // TESTCLIENT_H
testclient.cpp
#include "testclient.h"
#include <QState>
#include <QStateMachine>
#include <QTcpSocket>
#include <QThread> // Sleep
#include <QTimer>
//-----------------------------------------------------------------------------
// PUBLIC METHODS
//-----------------------------------------------------------------------------
TestClient::TestClient(QObject *parent) : QObject(parent)
{
m_socket = new QTcpSocket(this);
m_timer = new QTimer(this);
m_timer->setInterval(100);
connect(m_timer, &QTimer::timeout, this, &TestClient::tryConnect);
connect(m_socket, &QAbstractSocket::connected,this, &TestClient::onSocketConnected);
connect(m_socket, &QAbstractSocket::disconnected,this, &TestClient::onSocketDisconnected);
connect(m_socket, &QIODevice::bytesWritten,this, &TestClient::onSocketBytesWritten);
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TestClient::onSocketError);
}
void TestClient::start()
{
createClientFsm();
m_clientFsm->start();
}
//-----------------------------------------------------------------------------
// TCP CONNECTION MANAGEMENT SLOTS
//-----------------------------------------------------------------------------
void TestClient::onSocketConnected()
{
m_timer->stop();
qDebug() << "connected...";
emit fsmEvtConnected();
}
void TestClient::onSocketDisconnected()
{
qDebug() << "disconnected...";
emit fsmEvtError();
}
void TestClient::onSocketBytesWritten(qint64 bytes)
{
qDebug() << bytes << " bytes written...";
}
void TestClient::onSocketError(QAbstractSocket::SocketError err)
{
qDebug() << "socket error " << err;
}
//-----------------------------------------------------------------------------
// FSM MANAGEMENT
//-----------------------------------------------------------------------------
void TestClient::createClientFsm()
{
m_clientFsm = new QStateMachine(this);
// Create states
QState* sConnect = new QState();
QState* sTransmit = new QState();
// Add transitions between states
sConnect->addTransition(this, SIGNAL(fsmEvtConnected()), sTransmit);
sTransmit->addTransition(this, SIGNAL(fsmEvtError()), sConnect);
// Add entry actions to states
connect(sConnect, &QAbstractState::entered, this, &TestClient::onfsmConnectEntered);
connect(sTransmit, &QAbstractState::entered, this, &TestClient::onfsmTransmitEntered);
// Create state machine
m_clientFsm->addState(sConnect);
m_clientFsm->addState(sTransmit);
m_clientFsm->setInitialState(sConnect);
}
void TestClient::tryConnect(){
m_socket->connectToHost("localhost", 11000);
}
void TestClient::onfsmConnectEntered()
{
m_timer->start();
}
void TestClient::onfsmTransmitEntered()
{
qDebug() << "sending data...";
m_socket->write("TEST MESSAGE");
}

QT Connect Slot / Signal not working

I am having trouble to connect a Signal to a Slot in the following code:
#include "myserver.h"
MyServer::MyServer(QObject *parent) :
QTcpServer(parent)
{
}
void MyServer::StartServer()
{
if(listen(QHostAddress::Any, 45451))
{
qDebug() << "Server: started";
emit servComando("Server: started");
}
else
{
qDebug() << "Server: not started!";
emit servComando("Server: not started!");
}
}
void MyServer::incomingConnection(int handle)
{
emit servComando("server: incoming connection, make a client...");
// at the incoming connection, make a client
MyClient *client = new MyClient(this);
client->SetSocket(handle);
//clientes.append(client);
//clientes << client;
connect(client, SIGNAL(cliComando(const QString&)),this, SLOT(servProcesarComando(const QString&)));
// para probar
emit client->cliComando("prueba");
}
void MyServer::servProcesarComando(const QString& texto)
{
emit servComando(texto);
}
The emit client->cliComando("prueba"); works, but the real "emits" don't.
The console does not show any connection error, and the QDebug texts shows everything works well.
Original code was copied from http://www.bogotobogo.com/cplusplus/sockets_server_client_QT.php
I found the problem, Im sending a signal BEFORE connecting:
client->SetSocket(handle);
sends the signal, and Im CONNECTing after it... Now it is:
// at the incoming connection, make a client
MyClient *client = new MyClient(this);
connect(client, SIGNAL(cliComando(const QString&)),this, SLOT(servProcesarComando(const QString&)));
client->SetSocket(handle);
And it works. I noticed it after read the following:
13. Put all connect statements before functions calls that may fire their signals, to ensure that the connections are made before the signals are fired. For example:
_myObj = new MyClass();
connect(_myObj, SIGNAL(somethingHappend()), SLOT(doSomething()));
_myObj->init();
not
_myObj = new MyClass();
_myObj->init();
connect(_myObj, SIGNAL(somethingHappend()), SLOT(doSomething()));
I found it at https://samdutton.wordpress.com/2008/10/03/debugging-signals-and-slots-in-qt/
Anyway, thanks for your answers!

unable to send message from client to server

I am writing a client server program that server is multi thread , The code compile without any error, but it doesn't show any message from client.
just it run up to the "qDebug() << " Client connected";"
Here is my code .I would be grateful if you can say where is the problem.
myclient.cpp
#include "myclient.h"
#include "QTcpsocket"
#include "QTcpServer"
#include "mainwindow.h"
#include "QHostAddress"
myclient::myclient(QObject* parent): QObject(parent)
{
}
void myclient::start(QString address, quint16 port)
{
QHostAddress LocalHost;
LocalHost.setAddress(address);
m_client.connectToHost(LocalHost, 6666);
QObject::connect(&m_client, SIGNAL(connected()),this, SLOT(startTransfer()));
}
void myclient::startTransfer()
{
m_client.write("Hello", 5);
}
mythread.cpp
#include "mythread.h"
#include "myserver.h"
mythread::mythread(QTcpSocket*, QObject *parent) :
QThread(parent)
{
}
void mythread::run()
{
qDebug() << " Thread started";
if (m_client)
{
connect(m_client, SIGNAL(connected()), this, SLOT(readyRead()), Qt::DirectConnection);
}
qDebug() << " Client connected";
exec();
}
void mythread::readyRead()
{
QByteArray Data = m_client->readAll();
qDebug()<< " Data in: " << Data;
m_client->write(Data);
}
void mythread::disconnected()
{
qDebug() << " Disconnected";
m_client->deleteLater();
exit(0);
}
myserver.cpp
#include "myserver.h"
#include "mythread.h"
myserver::myserver(QObject *parent) :
QObject(parent)
{
}
void myserver::startserver()
{
connect(&m_server,SIGNAL(newConnection()), this ,SLOT(newConnection()));
int port = 6666;
if(m_server.listen(QHostAddress::Any, port))
{
qDebug() << "Listening to port " ;
}
else
{
qDebug() << "Could not start server "<<m_server.errorString();
}
}
void myserver::newConnection()
{
m_client = m_server.nextPendingConnection();
qDebug() << " Connecting...";
mythread *thread = new mythread(m_client,this);
thread->start();
}
Documentation about nextPendingConnection() says:
Note: The returned QTcpSocket object cannot be used from another
thread. If you want to use an incoming connection from another thread,
you need to override incomingConnection().
So, you can't use that socket in another thread. As the docs say, you can subclass QTcpServer and override incomingConnection(), this method is invoked whenever a client tries to connect to your server.
incomingConnection() method provides a socket descriptor (just like regular file descriptors). And then you can pass that socket descriptor to another thread and create the QTcpSocket completely over there.
Inside that thread, you would need something like this:
QTcpSocket client = new QTcpSocket();
client.setSocketDescriptor(sockId);
// Now, you can use this socket as a connected socket.
// Make sure to connect its ready read signal to your local slot.
Once you get in newConnection the client is already connected, and you just need to start a thread that calls readAll and then replies.
There's no need to wait for a connected() signal again.
EDIT
The QSocket class is designed to work asynchronously on the main thread, based on events. From the documentation:
Warning: QSocket is not suitable for use in threads. If you need to uses sockets in threads use the lower-level QSocketDevice class.

Qt c++ double emit Data

I have a multiserverapp that works fine so far. I got 4 cpp files.
Main.cpp constructs the program. MainWindow.cpp constructs the ui and starts (via buttonclick) MyServer.cpp. MyServer.cpp creates a thread and starts MyThread.cpp.
My aim is to show several major steps (like the "server started", "new connection", etc..) on a textBrowser.
I pass the outputs from MyServer.cpp via emit updateUI("server started"); to mainwindow.cpp where the output gets catched by:
//Mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "myserver.h"
#include "mythread.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::AppendToBrowser(const QString text)
{
ui->textBrowser->append(text);
}
void MainWindow::on_startButton_clicked()
{
MyServer* mServer = new MyServer;
connect(mServer, SIGNAL(updateUI(const QString)), this, SLOT(AppendToBrowser(const QString)));
mServer->StartServer();
ui->textBrowser->setPlainText("Server Started");
}
That works just right because the connect command is just in the mainwindow.cpp itself.
The problem starts one step "deeper" in the mythread.cpp.
I created another signal in the
//MyThread.h
signals:
void updateUI_serv(const QString text);
and connected it in the MyServer.cpp with the MainWindow.cpp.
//MyServer.cpp
#include "myserver.h"
#include "mainwindow.h"
MyServer::MyServer(QObject *parent) :
QTcpServer(parent)
{
}
void MyServer::StartServer()
{
if(!this->listen(QHostAddress::Any,1234))
{
qDebug("Server Error");
}
else
{
qDebug("Server started");
}
}
void MyServer::incomingConnection(int socketDescriptor)
{
qDebug("new connection");
MyThread *thread = new MyThread(socketDescriptor,this);
MainWindow *mMain = new MainWindow;
connect(thread, SIGNAL(updateUI_serv(const QString)),mMain ,SLOT(AppendToBrowser(const QString)));
//flags thread for selfdeletion
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
//calls run
thread->start();
emit updateUI("thread started!");
}
// MyThread.cpp
#include "mythread.h"
#include "mainwindow.h"
#include "myserver.h"
MyThread::MyThread(int ID, QObject *parent) :
QThread(parent)
{
this->socketDescriptor = ID;
emit updateUI_serv("start");
}
void MyThread::run()
{
//thread stars here
qDebug("Starting thread");
socket = new QTcpSocket();
emit updateUI_serv("hallo");
//set socketdescriptor number
if(!socket->setSocketDescriptor(this->socketDescriptor))
{
emit error(socket->error());
return;
}
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection);
qDebug("client connected");
exec();
}
void MyThread::readyRead()
{
QByteArray Data = socket->readAll();
QString Datain = QString::fromLatin1(Data);
qDebug("Date in:");
emit updateUI_serv("data in:");
socket->write(Data);
}
void MyThread::disconnected()
{
qDebug("Disconnected");
socket->deleteLater();
exit(0);
}
The connect command lays here "in between" the signal (updateUI_serv from mythread.cpp) and the slot (AppendToBrowser from mainwindow.cpp) file.
At this point the program crashes as soon as I try to write data (as a client via telnet) to the serverapp.
I tried to set the connect command into the mainwindow and the mythread as well, but both times I get different problems (like debugging problems, or the text does just not show up in the textBrowser).
Thanks so far.
Eventually the myServer-Object is not running in the MainThread, therefor accessing an ui element from another thread crashes your app.
You can make sure only Messages from mainThread will get displayed by adding the following code to your AppendToBrowser Slot:
if( QApplication::instance()->thread() == QThread::currentThread() )
ui->textBrowser->setPlainText("whateverTextThereShallBe");
else
return;
//You should not run into this else...
This if section checks if the current object calling the update is the mainThread. The else-section checks for erros. If you are running in the else-section you are trying to change ui-elements form a thread which is not the mainThread (UI-Thread). Connect your SIGNAL in server to another SIGNAL (SIGNAL->SIGNAL connection) and add a connect to SIGNAL(SERVER) -> SLOT(MainWindow) in your MainWindow.cpp. Eventually try your connect-call with the 5th. parameter for Queued Connection (Qt::QueuedConnection IIRC).
Ahh i got it on my own.
I solved the problem by creating a NEW function ( void forwarding(const Qstring); ) and in that function i emitted it with the ordinary emit updateUI(text); .. stuff works finaly!

Server thread inside a qt class (need mutex?)

I made this server class that starts a thread when new connection comes in. It works ok with some cases, but it's not very stable. I am trying to solve where it breaks. My debugger tells me something about qmutex. If anyone can spot the problem. ty
It connects with parent with signal&slots and gets data back also.
Here is the header:
#ifndef FORTUNESERVER_H
#define FORTUNESERVER_H
#include <QStringList>
#include <QTcpServer>
#include <QThread>
#include <QTcpSocket>
#include <string>
using namespace std;
class FortuneServer : public QTcpServer
{
Q_OBJECT
public:
FortuneServer(QObject *parent = 0);
public slots:
void procesServerString(string serverString);
void getStringToThread(string serverString);
protected:
void incomingConnection(int socketDescriptor);
private:
QStringList fortunes;
signals:
void procesServerStringToParent(string serverString);
void getStringToThreadSignal(string serverString);
};
class FortuneThread : public QObject
{
Q_OBJECT
public:
FortuneThread(int socketDescriptor, QObject *parent);
public slots:
void getString();
void sendString(string sendoutString);
signals:
void error(QTcpSocket::SocketError socketError);
void fromThreadString(string serverString);
void finished();
private:
int socketDescriptor;
QString text;
QTcpSocket tcpSocket;
};
#endif
and cc:
#include <stdlib.h>
#include <QtNetwork>
#include "MeshServer.hh"
#include <iostream>
#include "TableView.hh"
using namespace std;
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
}
void FortuneServer::procesServerString(string serverString){
emit procesServerStringToParent(serverString);
}
void FortuneServer::getStringToThread(string serverString){
emit getStringToThreadSignal(serverString);
}
void FortuneServer::incomingConnection(int socketDescriptor)
{
FortuneThread *serverthread = new FortuneThread(socketDescriptor, this);
//connect(&serverthread, SIGNAL(finished()), &serverthread, SLOT(deleteLater()));
QThread* thread = new QThread;
serverthread->moveToThread(thread);
connect(thread, SIGNAL(started()), serverthread, SLOT(getString()));
connect(serverthread, SIGNAL(fromThreadString(string)), this, SLOT(procesServerString(string)));
connect(this, SIGNAL(getStringToThreadSignal(string)), serverthread, SLOT(sendString(string)));
connect(serverthread, SIGNAL(finished()), thread, SLOT(quit()));
connect(serverthread, SIGNAL(finished()), serverthread, SLOT(deleteLater()));
connect(serverthread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
FortuneThread::FortuneThread(int socketDescriptor, QObject *parent)
: QObject(parent), socketDescriptor(socketDescriptor)
{
}
void FortuneThread::getString()
{
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
cout<<"socket error"<<endl;
return;
}
//in part
if(!tcpSocket.waitForReadyRead(10000)){
emit finished();
return;
}
int joj = tcpSocket.bytesAvailable();
char inbuffer[1024];
tcpSocket.read(inbuffer,1024);
string instring;
instring = inbuffer;
instring.resize(joj);
emit fromThreadString(instring);
}
void FortuneThread::sendString(string sendoutString)
{
//out part
char buffer[1024];
int buffer_len = 1024;
int bytecount;
memset(buffer, '\0', buffer_len);
string outstring = sendoutString;
int TempNumOne= (int)outstring.size();
for (int a=0;a<TempNumOne;a++)
{
buffer[a]=outstring[a];
}
QByteArray block;
block = buffer;
tcpSocket.write(block);
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
emit finished();
}
this is from parent:
//server start
QHostAddress adr;
adr.setAddress( QString("127.0.0.1") );
adr.toIPv4Address();
quint16 port = 1101;
if (!server.listen( adr, port)) {
QMessageBox::critical(this, tr("CR_bypasser"),
tr("Unable to start the server: %1.")
.arg(server.errorString()));
close();
return;
}
QString ipAddress;
ipAddress = server.serverAddress().toString();
statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
"Run the Fortune Client example now.")
.arg(ipAddress).arg(server.serverPort()));
connect (&server, SIGNAL(procesServerStringToParent(string)), this, SLOT(procesServerString(string)));
connect (this, SIGNAL(StringToServer(string)), &server, SLOT(getStringToThread(string)));
edit: what I am trying to do:
I have a client (part of a game engine(Cryengine)) that I made to send string of ingame coordinates and some other things with a socket like its done in a link I gave before. This works ok. I get data on "127.0.0.1" port 1101. Now I just need this data to be evaluated in my own program, that has this TableView class, inside which I can collect coordinates I get from the string, callculate some data from coordinates and then return this new string back through the server to gameengine. In game I will click on objects get their coor., make a string out of that (containing coor,entityid, etc..), send this string to server, that returns callculated info from TableView. I just need this one way flow only one client that is sending strings. I am not sure about recv(hsock, buffer, buffer_len, 0), I guess node that is responsible for string sending in game will wait for return string? This is one of my first programs atm I am realy confused...
The code you present is exemplary of cargo cult coding: you do various unnecessary things, apparently in hopes of fixing the problem.
The Likely Crasher ...
There are tons of problems with the code, but I think the cause of the crash is this: tcpSocket.write(block) does not send out a zero-terminated string down the wire. The block is zero-terminated, but the assignment to a byte array does not add this zero termination to the size() of QByteArray. The following code prints 1, even though there is a zero terminating byte internally in the contents of the byte array.
QByteArray arr = "a";
qDebug("len=%d", arr.size());
The receiving code expects the zero termination, but never receives it. You then proceed to assign a non-zero-terminated buffer to std::string:
string instring;
instring = inbuffer;
instring.resize(joj);
The subsequent resize is cargo cult: you're trying to fix the problem after std::string & std::string::operator=(const char*) has already read past your buffer, in all likelihood.
Do not take this to mean that fixing just that is the right way to proceed. Not at all. The right way to proceed is to delete the code you wrote and do it right, without a ton of unnecessary incantations that don't help.
... and All The Other Problems
You've fallen into the trap of believing in magic, perpetuated endlessly in various forums.
The threads are not magical objects that you can just apply to any problem out there in hopes that they help. I don't know what makes people think that threads are magical, but the rule of thumb is: If someone tells you "oh, you should try threads", they are most likely wrong. If they tell that in relation to networking, they are pretty much never right, they are unhelpful, and they don't understand your problem at all (neither do you, it seems). More often than not, threads will not help unless you clearly understand your problem. Qt's networking system is asynchronous: it doesn't block the execution of your code, if you don't use the waitxxxx() functions. You shouldn't use them, by the way, so all is good here. No need for a bazillion threads.
So, it is completely unnecessary to start a new thread per each incoming connection. It will decrease the performance of your server -- especially if the server does simple processing because you add the overhead of context switching and thread creation/dismantling to each connection. You want less than 2 threads per each core in your system, so using QThread::idealThreadCount() for the number of threads in the pool would be a good starting point.
You are also depriving yourself of the benefit of threading since you use the networking thread only to receive the data, and you then send out a fromThreadString(string) signal. I presume that signal is sent to your application's main thread. Now that's just silly, because receiving a bunch of bytes from a network socket is downright trivial. Your threads don't do any work, all the work they do is wasted on their creation and removal.
The code below is a simple example of how one might correctly use the Qt APIs to implement a client-server system that distributes work across the physical cores in a round-robin fashion. It should perform quite well. The Fortune client example included in Qt is very unfortunate indeed, because it's precisely the wrong way to go about things.
What one will notice is:
It's not entirely trivial. Qt could be more helpful, but isn't.
Both the clients and the senders are moved into threads from a thread pool.
Disconnected clients are not deleted, but merely returned to a list of clients
kept by the tread pool. They are reused when a client is called for.
QThread is not derived from. QTcpServer is only derived to access the socket handle.
No functions whose name begins with wait() are used. Everything is handled asynchronously.
The ThreadPool keeps a looked-up QMetaMethod for the newConnection(int) slot of the Client. This is faster than using QMetaObject::invokeMethod() as it has to look things up every time.
A timer running in the main thread sets off a signal-slot chain by deleting the first sender. Each senders' deletion triggers the deletion of the next one. Eventually, the last sender sets off the quit() slot in the thread pool. The latter emits the finished() signal when all threads are indeed finished.
#include <QtCore/QCoreApplication>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtCore/QQueue>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtCore/QMetaMethod>
// Processes data on a socket connection
class Client : public QObject
{
Q_OBJECT
public:
Client(QObject* parent = 0) : QObject(parent), socket(new QTcpSocket(this))
{
connect(socket, SIGNAL(readyRead()), SLOT(newData()));
connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
SLOT(newState(QAbstractSocket::SocketState)));
qDebug("Client()");
}
~Client() { qDebug("~Client()"); }
signals:
void done();
public slots:
void newConnection(int descriptor) {
socket->setSocketDescriptor(descriptor);
}
private slots:
void newData() {
QByteArray data = socket->readAll();
if (0) qDebug("got %d bytes", data.size());
if (0) qDebug("got a string %s", data.constData());
// here we can process the data
}
void newState(QAbstractSocket::SocketState state) {
qDebug("client new state %d", state);
if (state == QAbstractSocket::UnconnectedState) { emit done(); }
}
protected:
QTcpSocket* socket;
int descriptor;
};
// Connects to a client and sends data to it
class Sender : public QObject
{
Q_OBJECT
public:
Sender(const QString & address, quint16 port, QObject * parent = 0) :
QObject(parent), socket(new QTcpSocket(this)),
bytesInFlight(0), maxBytesInFlight(65536*8)
{
connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
SLOT(newState(QAbstractSocket::SocketState)));
connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(sentData(qint64)));
socket->connectToHost(address, port);
qDebug("Sender()");
}
~Sender() { qDebug("~Sender()"); }
protected:
// sends enough data to keep a maximum number of bytes in flight
void sendData() {
qint64 n = maxBytesInFlight - bytesInFlight;
if (n <= 0) return;
bytesInFlight += n;
socket->write(QByteArray(n, 44)); // 44 is the answer, after all
}
protected slots:
void sentData(qint64 n) {
bytesInFlight -= n;
Q_ASSERT(bytesInFlight >= 0);
sendData();
}
void newState(QAbstractSocket::SocketState state) {
qDebug("sender new state %d", state);
if (state == QAbstractSocket::ConnectedState) sendData();
}
protected:
QTcpSocket* socket;
qint64 bytesInFlight;
qint64 maxBytesInFlight;
};
// Keeps track of threads and client objects
class ThreadPool : public QTcpServer
{
Q_OBJECT
public:
ThreadPool(QObject* parent = 0) : QTcpServer(parent), nextThread(0) {
for (int i=0; i < QThread::idealThreadCount(); ++i) {
QThread * thread = new QThread(this);
connect(thread, SIGNAL(finished()), SLOT(threadDone()));
thread->start();
threads << thread;
}
const QMetaObject & mo = Client::staticMetaObject;
int idx = mo.indexOfMethod("newConnection(int)");
Q_ASSERT(idx>=0);
method = mo.method(idx);
}
void poolObject(QObject* obj) const {
if (nextThread >= threads.count()) nextThread = 0;
QThread* thread = threads.at(nextThread);
obj->moveToThread(thread);
}
protected:
void incomingConnection(int descriptor) {
Client * client;
if (threads.isEmpty()) return;
if (! clients.isEmpty()) {
client = clients.dequeue();
} else {
client = new Client();
connect(client, SIGNAL(done()), SLOT(clientDone()));
}
poolObject(client);
method.invoke(client, Q_ARG(int, descriptor));
}
signals:
void finished();
public slots:
void quit() {
foreach (QThread * thread, threads) { thread->quit(); }
}
private slots:
void clientDone() {
clients.removeAll(qobject_cast<Client*>(sender()));
}
void threadDone() {
QThread * thread = qobject_cast<QThread*>(sender());
if (threads.removeAll(thread)) delete thread;
if (threads.isEmpty()) emit finished();
}
private:
QList<QThread*> threads;
QQueue<Client*> clients;
QMetaMethod method;
mutable int nextThread;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ThreadPool server;
if (!server.listen(QHostAddress::Any, 1101)) qCritical("cannot establish a listening server");
const int senderCount = 10;
Sender *prevSender = 0, *firstSender = 0;
for (int i = 0; i < senderCount; ++ i) {
Sender * sender = new Sender("localhost", server.serverPort());
server.poolObject(sender);
if (!firstSender) firstSender = sender;
if (prevSender) sender->connect(prevSender, SIGNAL(destroyed()), SLOT(deleteLater()));
prevSender = sender;
}
QTimer::singleShot(3000, firstSender, SLOT(deleteLater())); // run for 3s
server.connect(prevSender, SIGNAL(destroyed()), SLOT(quit()));
qApp->connect(&server, SIGNAL(finished()), SLOT(quit()));
// Deletion chain: timeout deletes first sender, then subsequent senders are deleted,
// finally the last sender tells the thread pool to quit. Finally, the thread pool
// quits the application.
return a.exec();
}
#include "main.moc"
Given your explanation, you game engine starts up and creates a connection to some port on localhost. Your Qt program is supposed to accept that connection on port 1101, receive some strings, process them, then send them back.
The code is modified to accept the connection on a fixed port number. All of the data processing, including sending the response back, has to be done from the newData() slot. You can also pass that data off to a different thread, if your computations are very complex. By complex I mean tens of thousands of operations like additions and multiplications, or thousands of trig operations.
The Sender class is there just as an example. Your game engine does the sending, of course, so you don't need the Sender class.
I got my old "the wrong way to do it" code to work. I guess this part was where the error was:
//removed
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
emit finished();
...
#include <stdlib.h>
#include <QtNetwork>
#include "MeshServer.hh"
#include <iostream>
#include "TableView.hh"
using namespace std;
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
}
void FortuneServer::procesServerString(string serverString){
emit procesServerStringToParent(serverString);
}
void FortuneServer::getStringToThread(string serverString){
emit getStringToThreadSignal(serverString);
}
void FortuneServer::incomingConnection(int socketDescriptor)
{
FortuneThread *serverthread = new FortuneThread(socketDescriptor, this);
//connect(&serverthread, SIGNAL(finished()), &serverthread, SLOT(deleteLater()));
QThread* thread = new QThread;
serverthread->moveToThread(thread);
connect(serverthread, SIGNAL(fromThreadString(string)), this, SLOT(procesServerString(string)));
connect(this, SIGNAL(getStringToThreadSignal(string)), serverthread, SLOT(sendString(string)));
connect(serverthread, SIGNAL(finished()), thread, SLOT(quit()));
connect(serverthread, SIGNAL(finished()), serverthread, SLOT(deleteLater()));
connect(serverthread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
FortuneThread::FortuneThread(int socketDescriptor, QObject *parent): QObject(parent), socketDescriptor(socketDescriptor)
{
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
cout<<"socket error"<<endl;
emit finished();
return;
}
connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(getString()));
//connect(&tcpSocket, SIGNAL(disconnected()), this, SLOT(ondisconnected()));
}
void FortuneThread::getString()
{
int joj = tcpSocket.bytesAvailable();
if(joj==0){
tcpSocket.disconnectFromHost();
emit finished();
return;
}
char inbuffer[1024];
int buffer_len = 1024;
memset(inbuffer, '\0', buffer_len);
tcpSocket.read(inbuffer,1024);
string instring;
instring = inbuffer;
instring.resize(joj);
emit fromThreadString(instring);
}
void FortuneThread::sendString(string sendoutString)
{
char buffer2[1024];
int buffer_len = 1024;
memset(buffer2, '\0', buffer_len);
strcat(buffer2,sendoutString.c_str());
tcpSocket.write(buffer2,buffer_len);
}
void FortuneThread::ondisconnected()
{
emit finished();
}