Qt : How to emit QNetworkReply Slot after QNetworkRequest accoumplish? - qt

I am trying to use multiple threads to request data from cloud. My problem is after the QnetworkReply finishes replyFinished(QnetworkReply*) slot never emitted. How do i do this ?
pc_gateway.h
//Main class
#include "dhvac_datacollector_thread.h"
class PC_Gateway : public QMainWindow
{
Q_OBJECT
DHVAC_DataCollector_Thread *m_DHVAC_DataCollector_Thread[70];
PC_Gateway(QWidget *parent = 0, Qt::WFlags flags = 0);
~PC_Gateway();
public slots:
void slot_start_comm();
};
pc_gateway.cpp
#include "pc_gateway.h"
PC_Gateway::PC_Gateway(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
}
PC_Gateway::~PC_Gateway()
{
}
void PC_Gateway::slot_start_comm()
{
int nNumOfThreads = 4;
for(int i=0; i<nNumOfThreads ; i++)
{
this->m_DHVAC_DataCollector_Thread[i] = new DHVAC_DataCollector_Thread(this);
this->m_DHVAC_DataCollector_Thread[i]->start();
}
}
DHVAC_DataCollector_Thread.h
//Thread class
class DHVAC_DataCollector_Thread : public QThread
{
Q_OBJECT
public:
DHVAC_DataCollector_Thread(QObject *parent);
~DHVAC_DataCollector_Thread();
void run();
void Stop();
public slots:
void replyFinished(QNetworkReply*);
};
DHVAC_DataCollector_Thread.cpp
#include "dhvac_datacollector_thread.h"
DHVAC_DataCollector_Thread::DHVAC_DataCollector_Thread(QObject *parent)
: QThread(parent)
{
}
DHVAC_DataCollector_Thread::~DHVAC_DataCollector_Thread()
{
}
void DHVAC_DataCollector_Thread::run()
{
while(true)
{
QString sUrl = "https://api.uno.taww.com/api/v2/data/sensor/"+sNodeId+"/10m/_";
QUrl url(sUrl);
bool bUrl = url.isValid();
if(!bUrl) //If URL is invalid
return false;
QNetworkRequest request;
request.setUrl(url);
request.setRawHeader("api_token","eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9");
request.setRawHeader("user_id","102473df5c9106e55d");
request.setRawHeader("cache-control","no-cache");
QNetworkAccessManager *manager = new QNetworkAccessManager();
connect(manager, SIGNAL(finished(QNetworkReply*)),this,
SLOT(replyFinished(QNetworkReply*)));
QNetworkReply *reply = manager->get(request);
}
}
void DHVAC_DataCollector_Thread::replyFinished(QNetworkReply *reply)
{
QByteArray responseData;
if(reply->error() == QNetworkReply::NoError)
{
responseData = reply->readAll();
QString sData (responseData);
reply->close();
reply->deleteLater();
qDebug()<<"Raw data:" <<sData;
}
}

You can try send your requests after receiving reply, in your replyFinished slot not in a loop. Just do one request, if replyFinished works fine that way then do the second request.
void DHVAC_DataCollector_Thread::replyFinished(QNetworkReply *reply)
{
QByteArray responseData;
if(reply->error() == QNetworkReply::NoError)
{
responseData = reply->readAll();
QString sData (responseData);
reply->close();
reply->deleteLater();
qDebug()<<"Raw data:" <<sData;
}
// nextRequest()
}
Also you have a leak. You are creating tons of QNetworkAccessManager and never releasing.

Related

Received Image from socket and display in Qml

I want to received image through using TCP socket and display in QMl window.
currently :I am able to receive .png image from socket. and : able to display the image from local disk in qml using image provider. but while integrating the both things i am facing the issue. how to call the socket's function.
tcpserver.h
class MyTcpServer : public QObject
{
Q_OBJECT
public:
explicit MyTcpServer(QObject *parent = 0);
signals:
public slots:
void newConnection();
void on_readyRead();
private:
QTcpServer *server;
};```
```
tcpserver.cpp
QByteArray array,array1;
MyTcpServer::MyTcpServer(QObject *parent) :
QObject(parent)
{server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()),
this, SLOT(newConnection()));
connect(server,SIGNAL(readyRead()),this,SLOT(readyRead()));
if(!server->listen(QHostAddress::Any, 9999))
{
qDebug() << "Server could not start";
}
else
{
qDebug() << "Server started!";
}
}
void MyTcpServer::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
if(socket)
{
connect(socket ,SIGNAL(readyRead()),this,SLOT(on_readyRead()));
socket->write("Hello client \r\n");
socket->flush();
socket->waitForBytesWritten(3000);
}
}
void MyTcpServer::on_readyRead()
{
QTcpSocket * server = dynamic_cast<QTcpSocket*>(sender());
qDebug()<<"reading..";
if(server)
{
qDebug()<<"Array size " << array.size() ;
array.append(server->readAll());
if(array.size()<230400)
{
qDebug()<<"Array size " << array.size() ;
}
else {
qDebug()<<"Array size else " << array.size() ;
cv::Mat img,img1;
cv::Size const frame_size(240,320);
img = cv::Mat(240,320, CV_8UC3,array.data());
qDebug()<<"Image type " <<img.type();
qDebug()<<"Array size " << array.size() ;
qDebug()<<"beforeImage size size " << img.dims;
cv::imshow("image display",img);
cv::waitKey(5000);
QImage imdisplay((uchar*)img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
}
}
}
//socket->close()
}
```
imageprovider.cpp
QImage QmlImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
printf("In request function ..\n");
MyTcpServer *m = new MyTcpServer();
m->newConnection();
printf("after connectrion....");
QImage image1= m->on_readyRead();
printf("after request....");
return image;
}
```
You should move MyTcpServer *m = new MyTcpServer(); to ImageProvider class constructor and m to class members, also create a signal in your MyTcpServer class and connect it to your ImageProvider updateImage slot.
P.S Maybe need to change updateImage from simple function to slot.
tcpserver.h
class MyTcpServer : public QObject
{
Q_OBJECT
public:
explicit MyTcpServer(QObject *parent = 0);
signals:
void newImageReceived(QImage);
.
.
.
tcpserver.cpp on_readyRead()
cv::imshow("image display",img);
cv::waitKey(5000);
QImage imdisplay((uchar*)img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
emit newImageReceived(imdisplay);
qmlimageprovider.cpp
QmlImageProvider::QmlImageProvider()
: QQuickImageProvider(QQuickImageProvider::Image)
{
m = new MyTcpServer();
connect(m, &MyTcpServer::newImageReceived, this, &QmlImageProvider::updateImage);
m->newConnection();
}
qmlimageprovider.h
class QmlImageProvider : public QQuickImageProvider
{
public:
QmlImageProvider();
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
public slots:
void updateImage(QImage new_image);
private:
QImage image;
MyTcpServer *m;
};

Qt - Create WriteOnly and ReadOnly socket

I have a socketDescriptor.
I need to two sockets. One of them just for ReadOnly and another one for WriteOnly.
Is it impossible? If so, how?
Qt's sockets don't support having more than one socket object per native socket, but you can approximate this by creating appropriate unidirectional interfaces.
In the example below, the interfaces don't perform any buffering, thus they enforce that there is only once of each kind of interface per socket. Once you use the interface, you shouldn't be using the socket directly.
// https://github.com/KubaO/stackoverflown/tree/master/questions/socket-split-43409221
#include <QtNetwork>
class SocketBase : public QIODevice {
Q_OBJECT
public:
explicit SocketBase(QAbstractSocket * parent) : QIODevice{parent} {
connect(parent, &QAbstractSocket::connected, this, &SocketBase::connected);
connect(parent, &QAbstractSocket::disconnected, this, &SocketBase::disconnected);
connect(parent, &QAbstractSocket::stateChanged, this, [this](QAbstractSocket::SocketState state){
emit stateChanged(state);
setOpenMode(m_dev->openMode());
});
connect(parent,
static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
this, [this](QAbstractSocket::SocketError error){
setErrorString(m_dev->errorString());
emit this->error(error);
});
setOpenMode(m_dev->openMode());
}
QAbstractSocket::SocketError error() const {
return m_dev->error();
}
QAbstractSocket::SocketState state() const {
return m_dev->state();
}
Q_SIGNAL void connected();
Q_SIGNAL void disconnected();
Q_SIGNAL void error(QAbstractSocket::SocketError);
Q_SIGNAL void stateChanged(QAbstractSocket::SocketState);
bool isSequential() const override { return true; }
protected:
QAbstractSocket * const m_dev = static_cast<QAbstractSocket*>(parent());
};
class ReadSocket : public SocketBase {
Q_OBJECT
public:
explicit ReadSocket(QAbstractSocket * parent) : SocketBase(parent) {
for (auto proxy : parent->findChildren<ReadSocket*>())
Q_ASSERT(proxy == this);
connect(m_dev, &QIODevice::readyRead, this, &QIODevice::readyRead);
}
bool atEnd() const override {
return QIODevice::atEnd() && m_dev->atEnd();
}
qint64 bytesAvailable() const override {
return m_dev->bytesAvailable();
}
bool canReadLine() const override {
return m_dev->canReadLine();
}
protected:
qint64 readData(char * data, qint64 maxLength) override {
return m_dev->read(data, maxLength);
}
qint64 readLineData(char *data, qint64 maxLength) override {
return m_dev->readLine(data, maxLength);
}
qint64 writeData(const char *, qint64) override {
return -1;
}
};
class WriteSocket : public SocketBase {
Q_OBJECT
public:
explicit WriteSocket(QAbstractSocket * parent) : SocketBase(parent) {
for (auto proxy : parent->findChildren<WriteSocket*>())
Q_ASSERT(proxy == this);
connect(m_dev, &QIODevice::bytesWritten, this, &QIODevice::bytesWritten);
}
qint64 bytesToWrite() const override {
return m_dev->bytesToWrite();
}
bool flush() {
return m_dev->flush();
}
protected:
qint64 readData(char *, qint64) override {
return -1;
}
qint64 writeData(const char * data, qint64 length) override {
return m_dev->write(data, length);
}
};
int main(int argc, char *argv[])
{
QCoreApplication app{argc, argv};
QHostAddress addr{"127.0.0.1"};
quint16 port{9341};
QTcpServer server;
if (! server.listen(addr, port)) qFatal("can't listen");
QObject::connect(&server, &QTcpServer::newConnection, &server, [&]{
auto s = server.nextPendingConnection();
QObject::connect(s, &QTcpSocket::readyRead, s, [s]{
s->write(s->readAll());
});
QObject::connect(s, &QTcpSocket::disconnected, s, &QObject::deleteLater);
});
const char data_[] = "dhfalksjdfhaklsdhfklasdfs";
auto const data = QByteArray::fromRawData(data_, sizeof(data_));
QTcpSocket client;
WriteSocket writer(&client);
ReadSocket reader(&client);
QObject::connect(&writer, &WriteSocket::connected, [&]{
writer.write(data);
});
QObject::connect(&reader, &ReadSocket::readyRead, [&]{
if (reader.bytesAvailable() >= data.size()) {
auto const read = reader.read(data.size());
Q_ASSERT(read == data);
qApp->quit();
}
});
client.connectToHost(addr, port);
return app.exec();
}
#include "main.moc"

QTcpServer::incomingConnection(qintptr) not calling

I'm trying to make client and server using QTcpSocket and QTcpServer.
So, what happens to server.
I run the server, it starts listening (successfully [checked by myself])
I run client, enter 127.0.0.1 for IP address and 30000 for port
Client says that connection estabilished
Server doesn't do anything, just keep waiting
Parts of my program:
//server-work.h
#ifndef SERVERWORK_H
#define SERVERWORK_H
#include <QtCore>
#include <QSqlError>
#include <QDebug>
#include <QSqlQuery>
#include <unistd.h>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtSql/QSqlDatabase>
#include "lssclient.h"
class LSSClient;
class LTcpServer : public QTcpServer
{
Q_OBJECT
public:
class LTWorkWithClients : public QThread
{
//Q_OBJECT
public:
LTcpServer* server;
LTWorkWithClients(QObject* parent = 0, LTcpServer *server = 0):
QThread(parent)
{
this->server = server;
}
protected:
void run() {
qDebug() << "started";
server->workWithIncomingConnection();
}
};
QSqlDatabase db; // clients in database
LTWorkWithClients* t_workWithClients;
QQueue<quintptr> clientsToBeAttached;
QList<LSSClient*> clients;
LResult workWithIncomingConnection ();
LResult serverInit ();
LResult createDatabase ();
static QTextStream& qStdout ();
void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;
protected:
private:
};
#endif // SERVERWORK_H
I know that this nested thread class is strongly wrong way to do this thing, but i don't have much time to do it correctly
// server-work.cpp [PART]
LResult LTcpServer::serverInit()
{
checkError;
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("serverDB");
if (!db.open()) {
qDebug() << "Error opening database...";
qDebug() << db.lastError().text();
return LVError;
} else {
qDebug() << "Database successfully opened";
}
if(!this->listen(QHostAddress::Any, 30000)){
qDebug() << "Error starting listening...";
return LVError;
} else {
qDebug() << "Start listening successfully";
}
t_workWithClients = new LTWorkWithClients(this, this);
t_workWithClients->start();
return LVSuccess;
}
void LTcpServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << Q_FUNC_INFO << " new connection";
clientsToBeAttached.enqueue(socketDescriptor);
qDebug() << "here1";
}
LResult LTcpServer::workWithIncomingConnection()
{
bool toExit = false;
while (!toExit) {
checkError;
qDebug() << "1";
if (clientsToBeAttached.length() != 0) {
quintptr eachClientDescriptor = clientsToBeAttached.dequeue();
qDebug() << "2";
LSSClient* client = new LSSClient(this);
client->server = this;
client->socket->setSocketDescriptor(eachClientDescriptor);
qDebug() << "3";
client->registered = false;
client->server = this;
qDebug() << client->socket->localAddress();
connect(client->socket, SIGNAL(readyRead()), client, SLOT(onSokReadyRead()));
connect(client->socket, SIGNAL(connected()), client, SLOT(onSokConnected()));
connect(client->socket, SIGNAL(disconnected()), client, SLOT(onSokDisconnected()));
connect(client->socket, SIGNAL(error(QAbstractSocket::SocketError)),client, SLOT(onSokDisplayError(QAbstractSocket::SocketError)));
clients.append(client);
}
usleep(1000000);
}
return LVSuccess;
}
So, server just keep writing 1 (nothing more) every second, but client says that it is connected to the server.
This structure might help...
in your ltcpserver.h file:
class LTcpServer : public QTcpServer
{
Q_OBJECT
public:
explicit LTcpServer(QObject * parent = 0);
void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;
private:
QThread *myThread;
};
and in your ltcpserver.cpp:
LTcpServer::LTcpServer(QObject * parent) : QTcpServer(parent)
{
if(this->listen(QHostAddress::Any,30000))
{
qDebug() << "server started...";
}
else
{
qDebug() << "server could not be started...";
}
myThread = new QThread();
myThread->start();
}
void LTcpServer::incomingConnection(qintptr socketDescriptor)
{
LSSClient * yourClient = new LSSClient(socketDescriptor);
yourClient->moveToThread(myThread);
clients->append(yourClient);
}
and in your lssclient.h
class LSSClient: public QObject
{
Q_OBJECT
public:
explicit LSSClient(qintptr socketDescriptor,QObject * parent = 0);
private:
void setupSocket(qintptr socketDescriptor);
QTcpSocket * socket;
public slots:
void readyRead();
void disconnected();
};
and in your lssclient.cpp
LSSClient::LSSClient(qintptr socketDescriptor,QObject * parent) : QObject(parent)
{
setupSocket(qintptr socketDescriptor);
}
void LSSClient::setupSocket(qintptr socketDescriptor)
{
socket = new QTcpSocket(this);
socket->setSocketDescriptor(sDescriptor);
connect(socket,SIGNAL(readyRead()),this,SLOT(readyRead()));
connect(socket,SIGNAL(disconnected()),this,SLOT(disconnected()));
}
void LSSClient::readyRead()
{
// do whatever you want here with incoming data
}
void LSSClient::disconnected()
{
// do what happens to client when disconnected
}

QMessagebox not show text when call show()

my problem is I need to show a message ask users wait when I check network availability of other clients.My way is I have a class workerThread to do the business, before start it I create a qMessageBox. But the message only shows the title, not the content. I have no idea why, pls help :(
Here's the worker thread:
#include <QObject>
#include <QString>
#include "clientdataobj.h"
class WorkerThread : public QObject
{
Q_OBJECT
public:
explicit WorkerThread(QObject *parent = 0);
QList<ClientDataObj> listClient() const;
void setListClient(const QList<ClientDataObj> &listClient);
signals:
void finished();
void error(QString err);
void listClientPingChecked( QList <ClientDataObj> list);
public slots:
void testPing();
private:
QList <ClientDataObj> mListClient;
bool pingEachClient(QString ip);
};
implement:
#include "workerthread.h"
#include <QFile>
#include <QMessageBox>
#include <QTextStream>
WorkerThread::WorkerThread(QObject *parent) :
QObject(parent)
{
}
void WorkerThread::testPing()
{
if (mListClient.size()==0) {
emit finished();
return;
}
else{
for(unsigned i=0;i<mListClient.size();i++){
bool result = pingEachClient(mListClient[i].ip());
if(result)
mListClient[i].setStatus(true);
else
mListClient[i].setStatus(false);
}
emit listClientPingChecked(mListClient);
}
emit finished();
}
bool WorkerThread::pingEachClient(QString ip)
{
QString pingCommand = "ping " +ip + " -c 3 | grep loss | awk ' {print $7}' > pingResult.txt";
system(qPrintable(pingCommand));
QString lossPercentTxt = readFileText("pingResult.txt") ;
lossPercentTxt.chop(1);
int lossPercent = lossPercentTxt.toInt();
if(lossPercent<10){
return true;
}
else return false;
}
QList<ClientDataObj> WorkerThread::listClient() const
{
return mListClient;
}
void WorkerThread::setListClient(const QList<ClientDataObj> &listClient)
{
mListClient = listClient;
}
How I call it in MainWindow:
on_pbSendUpdate_clicked()
{
changeModeWaitPing();
getClientOnlineList();
}
getClientOnlineList()
{
if(mListClient.size()==0){
return;
}
mpThreadPing = new QThread;
mpWorkerThread = new WorkerThread;
mpWorkerThread->setListClient(mListClient);
connectThreadPingToGui();
mpThreadPing->start();
}
changeModeWaitPing()
{
ui->pbSendUpdate->setEnabled(false);
callMsgBox("Pinging client... Pls wait!");
// callWaitDialog();
}
callMsgBox( QString text)
{
if (NULL==mMsg) {
return;
}
mMsg->setWindowTitle("INFO");
// mMsg->setAttribute(Qt::WA_DeleteOnClose);
mMsg->setWindowModality(Qt::NonModal);
mMsg->setModal(false);
QString info ="Pinging client... Pls wait!";
mMsg->setText(info);
mMsg->show();
}
connectThreadPingToGui()
{
connect(mpWorkerThread, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(mpThreadPing, SIGNAL(started()), mpWorkerThread, SLOT(testPing()));
connect(mpWorkerThread, SIGNAL(finished()), mpThreadPing, SLOT(quit()));
connect(mpWorkerThread, SIGNAL(finished()), mpWorkerThread, SLOT(deleteLater()));
connect(mpThreadPing, SIGNAL(finished()), mpThreadPing, SLOT(deleteLater()));
connect(mpWorkerThread,SIGNAL(listClientPingChecked(QList<ClientDataObj>)),this,SLOT(updateListClientOnline(QList<ClientDataObj>)));
}
updateListClientOnline(QList<ClientDataObj> list)
{
mListClientOnline = list;
mPingDone = true;
if (NULL==mMsg) {
return;
}
else{
mMsg->hide();
}
if(mpDialogWaitPing==NULL){
return;
}
else{
mpDialogWaitPing->hide();
}
launchClientListTable();
}
You create a new thread, but you don't move any objects to that thread. So your new thread does nothing. I assume you wan't mpWorkerThread to be moved to the new thread. In that case you're missing mpWorkerThread->moveToThread(mpThreadPing);

QTcpSocket handling in another QThread

I need to handle incoming tcp connections on individual QThread's.
After successful client authentication, the according socket should be stored in an QList object.
[simplified main/server-side application]
class Server : public QObject
{
Q_OBJECT
public:
Server();
private:
QList<QTcpSocket*> m_connections;
QTcpServer m_server;
void handleIncomingConnection();
void handleWaiterThread();
private slots:
void treatFinishedWaiterThread();
}
[according function definitions]
handleIncomingConnection() slot is connected with the server object's (m_server) newConnection() signal.
void Server::handleIncomingConnection()
{
QThread *waiter = new QThread();
connect(waiter, SIGNAL(started()), this, SLOT(handleWaiterThread()));
connect(waiter, SIGNAL(finished()), this, SLOT(treatFinishedWaiterThread()));
moveToThread(waiter);
waiter->start();
}
void Server::handleWaiterThread()
{
// fetch requesting socket
QTcpSocket *socket = m_server->nextPendingConnection();
// HANDLE PASSWORD AUTHENTICATION HERE ...
// IF SUCCESSFUL, CONTINUE
connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
// add to list
m_connections.append(socket);
}
void Server::treatFinishedWaiterThread()
{
QThread *caller = qobject_cast<QThread*>(sender());
caller->deleteLater();
}
If I try to run this, the threads get created but no SIGNAL is emitted when they're done, so I can't delete threads afterwards. Additionally I get this message:
QObject::moveToThread: Widgets cannot be moved to a new thread
How to fix this?
[01.06.2016]
According to QTcpServer::nextPendingConnection() it says:
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 in the end I have to create another class that inherits from QTcpServer.
[01.07.2016 #1]
I revised my code and added a custom server and waiter thread class.
[custom server class]
class CustomServer : public QTcpServer
{
Q_OBJECT
public:
WServer(QObject* = nullptr) : QTcpServer(parent) {}
signals:
void connectionRequest(qintptr);
protected:
void incomingConnection(qintptr socketDescriptor)
{
emit connectionRequest(socketDescriptor);
}
};
[custom thread class]
class Waiter : public QThread
{
Q_OBJECT
public:
Waiter(qintptr socketDescriptor, QObject *parent = nullptr)
: QThread(parent)
{
// Create socket
m_socket = new QTcpSocket(this);
m_socket->setSocketDescriptor(socketDescriptor);
}
signals:
void newSocket(QTcpSocket*);
protected:
void run()
{
// DO STUFF HERE
msleep(2500);
emit newSocket(m_socket);
}
private:
QTcpSocket *m_socket;
};
[new main class]
class ServerGUI : public QWidget
{
Q_OBJECT
public:
Server(QObject*);
private:
QList<QTcpSocket*> m_connections;
CustomServer m_server;
private slots:
void handleConnectionRequest(qintptr);
void handleNewSocket(QTcpSocket*);
}
void CustomServer::handleConnectionRequest(qintptr socketDescriptor)
{
Waiter *nextWaiter = new Waiter(socketDescriptor, this);
connect(nextWaiter, SIGNAL(newSocket(QTcpSocket*)), this, SLOT(handleNewSocket(QTcpSocket*)));
connect(nextWaiter, SIGNAL(finished()), this, SLOT(deleteLater()));
nextWaiter->start();
}
void CustomServer::handleNewSocket(QTcpSocket *socket)
{
// DO STUFF HERE ...
connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
// FINALLY ADD TO ACTIVE-CLIENT LIST ...
}
Signal & Slot specific settings:
Since CustomServer is defined as a class member (m_server) within my main widget class (that handles GUI; called ServerGUI),
connectionRequest(qintptr) signal of m_server gets connected with handleConnectionRequest(qintptr) slot of ServerGUI instance.
But now my application is crashing immediately after startup, showing following message in debug window:
HEAP[qtapp.exe]: Invalid address specified to RtlValidateHeap( 000002204F430000, 0000006E0090F4C0 )
What may be the cause of this?
[01.10.2016 #2]
I adapted my code according to user2014561's answer.
for CustomServer class
class CustomServer : public QTcpServer
{
Q_OBJECT
public:
WServer(QHostAddress, quint16, quint16, QObject* = nullptr);
~WServer();
void kickAll();
void kickClient(qintptr);
QHostAddress localAddress() const;
quint16 serverPort() const;
bool isReady() const;
bool alreadyConnected(qintptr) const;
bool clientLimitExhausted() const;
signals:
void clientConnected(qintptr);
void clientDisconnected(qintptr);
private slots:
void destroyedfunc(QObject*);
// JUST FOR TESTING PURPOSES
void waiterFinished();
private:
QList<ServerPeer*> m_connections;
quint16 m_maxAllowedClients;
bool m_readyState;
void incomingConnection(qintptr);
};
for kickAll():
void WServer::kickAll()
{
while (!m_connections.isEmpty())
{
ServerPeer *peer = m_connections.first();
QEventLoop loop;
connect(peer->thread(), SIGNAL(destroyed()), &loop, SLOT(quit())); // ### PROBLEM ENCOUNTERED HERE
QMetaObject::invokeMethod(peer, "deleteLater", Qt::QueuedConnection);
loop.exec();
}
}
for kickClient(qintptr):
void WServer::kickClient(qintptr client_id)
{
foreach (ServerPeer *peer, m_connections)
{
bool peerState;
QMetaObject::invokeMethod(peer, "hasSocket", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, peerState), Q_ARG(qintptr, client_id));
if (peerState)
{
QEventLoop loop;
connect(peer->thread(), SIGNAL(destroyed()), &loop, SLOT(quit()));
QMetaObject::invokeMethod(peer, "deleteLater", Qt::QueuedConnection);
loop.exec();
break;
}
}
}
for destroyedfunc(QObject*):
void CustomServer::destroyedfunc(QObject *obj)
{
ServerPeer *peer = static_cast<ServerPeer*>(obj);
m_connections.removeAll(peer);
}
for incomingConnection(qintptr):
void WServer::incomingConnection(qintptr handle)
{
ServerPeer *peer = new ServerPeer();
QThread *waiter = new QThread();
m_connections.append(peer); // add to list
peer->moveToThread(waiter);
// notify about client connect
connect(peer, SIGNAL(connected(qintptr)), this, SIGNAL(clientConnected(qintptr)));
// stop waiter thread by indirectly raising finished() signal
connect(peer, SIGNAL(finished()), waiter, SLOT(quit()));
// notify about client disconnect
connect(peer, SIGNAL(disconnected(qintptr)), this, SIGNAL(clientDisconnected(qintptr)));
// remove client from list
connect(peer, SIGNAL(destroyed(QObject*)), this, SLOT(destroyedfunc(QObject*)));
// notify about finished waiter thread; only for debug purposes
connect(waiter, SIGNAL(finished()), this, SLOT(waiterFinished()));
// remove waiter thread when finished
connect(waiter, SIGNAL(finished()), waiter, SLOT(deleteLater()));
QMetaObject::invokeMethod(peer, "start", Qt::QueuedConnection,
Q_ARG(qintptr, handle));
waiter->start();
}
for ServerPeer class
class ServerPeer : public QObject
{
Q_OBJECT
public:
ServerPeer(QObject* = nullptr);
~ServerPeer();
bool hasSocket(qintptr) const;
signals:
void connected(qintptr);
void disconnected(qintptr);
void finished();
public slots:
void start(qintptr);
void disconnect();
private slots :
void notifyConnect();
void notifyDisconnect();
private:
QTcpSocket *m_peer;
qintptr m_id;
};
for ServerPeer(QObject*):
ServerPeer::ServerPeer(QObject *parent) : QObject(parent), m_peer(nullptr)
{
}
for ~ServerPeer():
ServerPeer::~ServerPeer()
{
disconnect();
}
for start(qintptr):
void ServerPeer::start(qintptr handle)
{
qDebug() << "New waiter thread has been started.";
m_peer = new QTcpSocket(this);
if (!m_peer->setSocketDescriptor(handle))
{
this->deleteLater();
return;
}
if (true /*verification here*/)
{
connect(m_peer, SIGNAL(disconnected()), this, SLOT(notifyDisconnect()));
connect(m_peer, SIGNAL(disconnected()), this, SLOT(deleteLater()));
// manually do connected notification
QTimer::singleShot(0, this, SLOT(notifyConnect()));
}
else
{
this->deleteLater();
}
emit finished();
}
for disconnect():
void ServerPeer::disconnect()
{
if (m_peer != nullptr)
{
if (m_peer->state() != QAbstractSocket::SocketState::ClosingState
&& m_peer->state() != QAbstractSocket::SocketState::UnconnectedState)
m_peer->abort();
delete m_peer;
m_peer = nullptr;
}
}
for notifyConnect():
void ServerPeer::notifyConnect()
{
emit connected(m_peer);
}
for notifyDisconnect():
void ServerPeer::notifyDisconnect()
{
emit disconnected(m_peer);
}
for ServerGUI class
class ServerGUI : public QWidget
{
Q_OBJECT
public:
ServerGUI(QWidget* = nullptr);
private:
Ui::ServerWindow ui;
CustomServer *m_server;
private slots:
// For further handling, e.g. updating client view
void handleNewClient(qintptr);
void handleRemovedClient(qintptr);
}
for ServerGUI(QWidget*):
ServerGUI::ServerGUI(QWidget *parent) : QWidget(parent)
{
// initialize gui elements;
// GENERATED WITH ACCORDING *.ui FILE
ui.setupUi(this);
m_server = new WServer(QHostAddress::LocalHost, 1234, 2, this);
if (!m_server->isReady())
{
qDebug() << "Server could not start!";
delete m_server;
m_server = nullptr;
return;
}
connect(m_server, SIGNAL(clientConnected(qintptr)), this, SLOT(handleNewClient(qintptr)));
connect(m_server, SIGNAL(clientDisconnected(qintptr)), this, SLOT(handleRemovedClient(qintptr)));
}
And here my main function:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ServerGUI w;
w.show();
return a.exec();
}
With given code following message pops up if I try to kick a (selected) client:
QMetaObject::invokeMethod: No such method ServerPeer::hasSocket(qintptr)
QObject::connect: Cannot connect (null)::destroyed() to QEventLoop::quit()
How to fix this?
If I understood correctly, you want to run each peer of your server on a separate thread, if so, then the following may help you:
create a subclass of QTcpServer
reimplement incomingConnection() method
create an instance (without parent) of QThread and of ServerPeer and start the thread
do the SIGNAL - SLOT connections to remove the peer from the list and delete the thread and the peers instances
add ServerPeer to your QList
once time started, do the credentials verification; if you reject them, abort the connection
Edit, considerations:
You haven't got the connected SIGNAL because when you set a socketDescriptor to the socket was already connected, so you can simple assume after setSocketDescriptor that the socket is connected and do what you want.
About the error when closing, it's happens because you not releasing properly the threads, see my edit how can you solve that.
Finally QTcpSocket must not be accessed by diferent threads, if you need to call ServerPeer from another thread use QMetaObject::invokeMethod with QueuedConnection or BlockingQueuedConnection and SIGNAL SLOT mechanism.
Edit 2:
Now the server and it's peers will be deleted on MainWindow::closeEvent and that way you can see the disconnected function being called. I guess the problem happens depending of the order that the classes will be deleted.
You can interact with the socket including send data through it, but I believe that will be painless use the Qt methods for cross-thread calls already mentioned. In my example you can easily write to a specific peer or to all peers.
customserver.h:
//Step 1
#include <QtCore>
#include <QtNetwork>
#include "serverpeer.h"
class CustomServer : public QTcpServer
{
Q_OBJECT
public:
explicit CustomServer(const QHostAddress &host, quint16 port, quint16 maxconnections, QObject *parent = nullptr);
~CustomServer();
void kickAll();
void kickClient(qintptr id);
void writeData(const QByteArray &data, qintptr id);
void writeData(const QByteArray &data);
QHostAddress localAddress();
quint16 serverPort();
bool isReady();
signals:
void clientConnected(qintptr);
void clientDisconnected(qintptr);
private slots:
void destroyedfunc(QObject *obj);
private:
void incomingConnection(qintptr handle);
QList<ServerPeer*> m_connections;
int m_maxAllowedClients;
};
customserver.cpp:
#include "customserver.h"
CustomServer::CustomServer(const QHostAddress &host, quint16 port, quint16 maxconnections, QObject *parent) :
m_maxAllowedClients(maxconnections), QTcpServer(parent)
{
listen(host, port);
}
CustomServer::~CustomServer()
{
kickAll();
}
//Step 2
void CustomServer::incomingConnection(qintptr handle)
{
// handle client limit
if (m_connections.size() >= m_maxAllowedClients)
{
qDebug() << "Can't allow new connection: client limit reached!";
QTcpSocket *socket = new QTcpSocket();
socket->setSocketDescriptor(handle);
socket->abort();
delete socket;
return;
}
//Step 3
ServerPeer *peer = new ServerPeer();
QThread *waiter = new QThread();
peer->moveToThread(waiter);
//Step 4
connect(peer, SIGNAL(connected(qintptr)), this, SIGNAL(clientConnected(qintptr)));
connect(peer, SIGNAL(disconnected(qintptr)), this, SIGNAL(clientDisconnected(qintptr)));
connect(peer, SIGNAL(destroyed()), waiter, SLOT(quit()));
connect(peer, SIGNAL(destroyed(QObject*)), this, SLOT(destroyedfunc(QObject*)));
connect(waiter, SIGNAL(finished()), waiter, SLOT(deleteLater()));
QMetaObject::invokeMethod(peer, "start", Qt::QueuedConnection, Q_ARG(qintptr, handle));
waiter->start();
//Step 5
m_connections.append(peer);
}
void CustomServer::kickAll()
{
while (!m_connections.isEmpty())
{
ServerPeer *peer = m_connections.first();
QEventLoop loop;
connect(peer->thread(), SIGNAL(destroyed()), &loop, SLOT(quit()));
QMetaObject::invokeMethod(peer, "deleteLater", Qt::QueuedConnection);
loop.exec();
}
}
void CustomServer::kickClient(qintptr id)
{
foreach (ServerPeer *peer, m_connections)
{
ServerPeer::State hassocket;
QMetaObject::invokeMethod(peer, "hasSocket", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ServerPeer::State, hassocket), Q_ARG(qintptr, id));
if (hassocket == ServerPeer::MyTRUE)
{
QEventLoop loop;
connect(peer->thread(), SIGNAL(destroyed()), &loop, SLOT(quit()));
QMetaObject::invokeMethod(peer, "deleteLater", Qt::QueuedConnection);
loop.exec();
break;
}
}
}
void CustomServer::writeData(const QByteArray &data)
{
foreach (ServerPeer *peer, m_connections)
QMetaObject::invokeMethod(peer, "writeData", Qt::QueuedConnection, Q_ARG(QByteArray, data));
}
void CustomServer::writeData(const QByteArray &data, qintptr id)
{
foreach (ServerPeer *peer, m_connections)
{
ServerPeer::State hassocket;
QMetaObject::invokeMethod(peer, "hasSocket", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ServerPeer::State, hassocket), Q_ARG(qintptr, id));
if (hassocket == ServerPeer::MyTRUE)
{
QMetaObject::invokeMethod(peer, "writeData", Qt::QueuedConnection, Q_ARG(QByteArray, data));
break;
}
}
}
QHostAddress CustomServer::localAddress()
{
return QTcpServer::serverAddress();
}
quint16 CustomServer::serverPort()
{
return QTcpServer::serverPort();
}
bool CustomServer::isReady()
{
return QTcpServer::isListening();
}
void CustomServer::destroyedfunc(QObject *obj)
{
ServerPeer *peer = static_cast<ServerPeer*>(obj);
m_connections.removeAll(peer);
}
serverpeer.h:
#include <QtCore>
#include <QtNetwork>
class ServerPeer : public QObject
{
Q_OBJECT
public:
explicit ServerPeer(QObject *parent = nullptr);
~ServerPeer();
enum State
{
MyTRUE,
MyFALSE
};
signals:
void connected(qintptr id);
void disconnected(qintptr id);
public slots:
ServerPeer::State hasSocket(qintptr id);
void start(qintptr handle);
void writeData(const QByteArray &data);
private slots:
void readyRead();
void notifyConnect();
void notifyDisconnect();
private:
QTcpSocket *m_peer;
qintptr m_id;
};
serverpeer.cpp:
#include "serverpeer.h"
ServerPeer::ServerPeer(QObject *parent) : QObject(parent), m_peer(nullptr)
{
}
ServerPeer::~ServerPeer()
{
if (m_peer)
m_peer->abort();
}
ServerPeer::State ServerPeer::hasSocket(qintptr id)
{
if (m_id == id)
return MyTRUE;
else
return MyFALSE;
}
void ServerPeer::start(qintptr handle)
{
m_peer = new QTcpSocket(this);
m_peer->setSocketDescriptor(handle);
//Step 6
if (true /*verification here*/)
{
m_id = handle;
QTimer::singleShot(0, this, SLOT(notifyConnect()));
connect(m_peer, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(m_peer, SIGNAL(disconnected()), this, SLOT(notifyDisconnect()));
connect(m_peer, SIGNAL(disconnected()), this, SLOT(deleteLater()));
}
else
{
m_peer->abort();
this->deleteLater();
}
}
void ServerPeer::readyRead()
{
qDebug() << m_peer->readAll() << QThread::currentThread();
}
void ServerPeer::writeData(const QByteArray &data)
{
m_peer->write(data);
m_peer->flush();
}
void ServerPeer::notifyConnect()
{
emit connected(m_id);
}
void ServerPeer::notifyDisconnect()
{
emit disconnected(m_id);
}
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qRegisterMetaType<qintptr>("qintptr");
m_server = new CustomServer(QHostAddress::LocalHost, 1024, 2, this);
if (!m_server->isReady())
{
qDebug() << "Server could not start!";
delete m_server;
m_server = nullptr;
return;
}
connect(m_server, SIGNAL(clientConnected(qintptr)), this, SLOT(handleNewClient(qintptr)));
connect(m_server, SIGNAL(clientDisconnected(qintptr)), this, SLOT(handleRemovedClient(qintptr)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *)
{
if (m_server)
{
delete m_server;
m_server = nullptr;
}
}
void MainWindow::handleNewClient(qintptr id)
{
qDebug() << __FUNCTION__ << id;
m_server->writeData(QString("Hello client id: %0\r\n").arg(id).toLatin1(), id);
m_server->writeData(QString("New client id: %0\r\n").arg(id).toLatin1());
}
void MainWindow::handleRemovedClient(qintptr id)
{
qDebug() << __FUNCTION__ << id;
}

Resources