Share websocket connection between two windows in QT - c++

I've created a class called CSocket:
CSocket.h
#ifndef CSOCKET_H
#define CSOCKET_H
#include <QtCore/QObject>
#include <QtWebSockets/QWebSocket>
class CSocket : public QObject
{
Q_OBJECT
public:
explicit CSocket(QObject *parent = nullptr);
void onConnect(const QUrl &url);
void onSendMesssage(QString message);
signals:
void closed();
private slots:
void onConnected();
void onTextMessageReceived(QString message);
private:
QWebSocket m_webSocket;
QUrl m_url;
};
#endif // CSOCKET_H
CSocket.cpp
#include "csocket.h"
#include <QtCore/QDebug>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMessageBox>
QT_USE_NAMESPACE
CSocket::CSocket(QObject *parent) : QObject(parent)
{
}
void CSocket::onConnect(const QUrl &url)
{
m_url = url;
connect(&m_webSocket, &QWebSocket::connected, this, &CSocket::onConnected);
connect(&m_webSocket, &QWebSocket::disconnected, this, &CSocket::closed);
m_webSocket.open(QUrl(url));
}
void CSocket::onConnected()
{
connect(&m_webSocket, &QWebSocket::textMessageReceived, this, &CSocket::onTextMessageReceived);
}
void CSocket::onTextMessageReceived(QString message)
{
QMessageBox::information(nullptr, "Answer", message, QMessageBox::Ok);
}
void CSocket::onSendMesssage(QString message)
{
m_webSocket.sendTextMessage(message);
}
In main window (QWidget) i create a connection:
CSocket *socket = new CSocket;
socket->onConnect(QUrl(QStringLiteral("ws://localhost:8080")));
Now is the question: how can i share the connection to another QWidget or QDialog? I just don't want to reconnect in the new window. Does someone know how to do it?

Assuming that within your entire application you only want a connection as indicated, an appropriate pattern would be the singleton:
csocket.h
#ifndef CSOCKET_H
#define CSOCKET_H
#include <QObject>
#include <QWebSocket>
class CSocket : public QObject
{
Q_OBJECT
public:
static CSocket *instance();
void onConnect(const QUrl &url);
void onSendMesssage(QString message);
signals:
void closed();
private slots:
void onConnected();
void onTextMessageReceived(QString message);
private:
static CSocket* m_instance;
explicit CSocket(QObject *parent = nullptr);
QWebSocket m_webSocket;
QUrl m_url;
};
#endif // CSOCKET_H
csocket.cpp
#include "csocket.h"
#include <QMessageBox>
CSocket* CSocket::m_instance = nullptr;
CSocket::CSocket(QObject *parent) : QObject(parent)
{
}
CSocket *CSocket::instance()
{
if (m_instance == nullptr)
m_instance = new CSocket;
return m_instance;
}
void CSocket::onConnect(const QUrl &url)
{
m_url = url;
connect(&m_webSocket, &QWebSocket::connected, this, &CSocket::onConnected);
connect(&m_webSocket, &QWebSocket::disconnected, this, &CSocket::closed);
m_webSocket.open(QUrl(url));
}
void CSocket::onConnected()
{
connect(&m_webSocket, &QWebSocket::textMessageReceived, this, &CSocket::onTextMessageReceived);
}
void CSocket::onTextMessageReceived(QString message)
{
QMessageBox::information(nullptr, "Answer", message, QMessageBox::Ok);
}
void CSocket::onSendMesssage(QString message)
{
m_webSocket.sendTextMessage(message);
}
So instead of using the constructor you should use the instance() method:
//mainwindow
CSocket *socket = CSocket::instance();
socket->onConnect(QUrl(QStringLiteral("ws://localhost:8080")));
// another window
CSocket *socket = CSocket::instance();
As you can see there will only be one CSocket that is shared by all the windows.

Related

QT request "QObject::connect: No such slot BackEnd::RequestReceived"

Im trying to make a request to get some data with QT.
my backend.h
#ifndef BACKEND_H
#define BACKEND_H
#include <QNetworkAccessManager>
#include <QObject>
#include <QString>
#include <QNetworkReply>
class BackEnd : public QObject
{
Q_OBJECT
Q_PROPERTY(QString userName READ userName WRITE setUserName NOTIFY userNameChanged)
public:
explicit BackEnd(QObject *parent = nullptr);
QString userName();
void setUserName(const QString &userName);
signals:
void userNameChanged();
private:
QString m_userName;
QNetworkAccessManager *manager;
//also tried: void RequestReceived(QNetworkReply* reply); << without space after QNetworkReply
void RequestReceived(QNetworkReply * reply);
};
#endif // BACKEND_H
my .cpp
#include "backend.h"
#include <string>
#include <iostream>
#include <QtNetwork>
BackEnd::BackEnd(QObject *parent) :
QObject(parent)
{
manager = new QNetworkAccessManager(this);
}
void BackEnd::RequestReceived(QNetworkReply * reply){
QByteArray rawData = reply->readAll();
QString textData(rawData);
qDebug() << textData;
}
QString BackEnd::userName()
{
return m_userName;
}
void BackEnd::setUserName(const QString &userName)
{
if (userName == m_userName)
return;
m_userName = userName;
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(RequestReceived(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("https://google.com")));
emit userNameChanged();
}
I found similair questions but almost all answers say Q_OBJECT should be added (which I have). Im very new to qt but if I understand correctly the error indicates that I do not have a
BackEnd::RequestReceived(QNetworkReply*)
method, which I do have. Any help is welcome.
You need to mark void RequestReceived(QNetworkReply * reply); as a slot:
Q_SLOT void RequestReceived(QNetworkReply * reply);

QObject: Cannot create children for a parent that is in a different thread.(Parent is QTcpSocket)

I am a beginner of qt.I adopted the way recommended by QThread Class in qt documentation.
The method used in documentation is as follows.
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString &parameter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
So I have Class SocketThreadCtrler and Class SocketWorker respectively.
Concrete code is as follows.
socketthreadctrler.h is as follows.
#ifndef SOCKETTHREADCTRLER_H
#define SOCKETTHREADCTRLER_H
#include <QObject>
#include <QThread>
#include "socketworker.h"
class SocketThreadCtrler : public QObject
{
Q_OBJECT
private:
QThread workThread;
public:
explicit SocketThreadCtrler(QObject *parent = 0);
~SocketThreadCtrler();
private:
void connSigAngSlots();
public slots:
void login(QString acnt, QString passwd);
void EmitSigCheckLogin(bool loginStatus);
signals:
void SIG_CONECTSERVER();
void SIG_WRITEMSG(QByteArray&);
void SIG_CHECKLOGIN(bool);
private:
SocketWorker* worker;
// MainUI* mainUI;
};
#endif // SOCKETTHREADCTRLER_H
socketThreadCtrler.cpp
#include "header/backend/communication/socketthreadctrler.h"
#include "header/backend/communication/smessage1.h"
SocketThreadCtrler::SocketThreadCtrler(QObject *parent) : QObject(parent)//, worker(NULL)
{
// SocketWorker* worker = new SocketWorker();
worker=new SocketWorker;
worker->moveToThread(&workThread);
connSigAngSlots();
workThread.start();
emit SIG_CONECTSERVER();
}
SocketThreadCtrler::~SocketThreadCtrler()
{
workThread.quit();
workThread.wait();
}
void SocketThreadCtrler::connSigAngSlots()
{
connect(&workThread,SIGNAL(finished()),worker,SLOT(deleteLater()));
connect(this,SIGNAL(SIG_CONECTSERVER()),worker,SLOT(connectServer()),Qt::QueuedConnection);
connect(this,SIGNAL(SIG_WRITEMSG(QByteArray&)),worker,SLOT(writeMsg(QByteArray&)));
connect(worker,SIGNAL(Sig_LoginStatus(bool)),this,SLOT(EmitSigCheckLogin(bool)));
}
void SocketThreadCtrler::login(QString acnt, QString passwd)
{
//如何给login专门开一个线程
SMessage1 msg1(acnt, passwd);
emit SIG_WRITEMSG(msg1.getMsg());
}
void SocketThreadCtrler::EmitSigCheckLogin(bool loginStatus)
{
emit SIG_CHECKLOGIN(loginStatus);
}
socketWorker.h
#ifndef SOCKETWORKER_H
#define SOCKETWORKER_H
#include <QObject>
#include <QTcpSocket>
#include "messagefactory.h"
#include "rmessage.h"
#include "message.h"
#include "auxfuncset.h"
#include "smessage.h"
class SocketWorker : public QObject
{
Q_OBJECT
private:
static const quint16 serverPort=12345;
static const QString serverAddr;
QTcpSocket sock;
public:
explicit SocketWorker(QObject *parent = 0);
void WorkerEmitSigLogin(bool loginStatus);
private:
bool isServerMsg();
quint32 getMsgLen();
void getMsg(quint32 msgLen, QByteArray& msgBytes);
signals:
void Sig_LoginStatus(bool loginStatus);
public slots:
void connectServer();
void writeMsg(QByteArray&Msg);
void procMsg();
//private:
};
#endif // SOCKETWORKER_H
socketWorker.cpp
#include "header/backend/communication/socketworker.h"
const QString SocketWorker::serverAddr("127.0.0.1");
SocketWorker::SocketWorker(QObject *parent) : QObject(parent)
{
}
void SocketWorker::connectServer()
{
QString tmp=serverAddr;
sock.connectToHost(tmp,serverPort);
connect(&sock,SIGNAL(readyRead()) ,this, SLOT(procMsg()));
}
void SocketWorker::writeMsg(QByteArray& Msg)
{
sock.write(Msg);
// QAbstractSocket will start sending data automatically
// once control goes back to the event loop
sock.flush();
}
void SocketWorker::procMsg()
{
if(!isServerMsg()) return;
quint32 msgLen=0;
msgLen=getMsgLen();
if(0 == msgLen) return;
QByteArray msgBytes;
getMsg(msgLen,msgBytes);
RMessage* msg = MessageFactory::createMessage(msgBytes);
msg->process(this);
delete msg;
}
bool SocketWorker::isServerMsg()
{
//MSGHEADER
quint32 msgHead=0;
while(AuxFuncSet::readQuint32(&sock, msgHead))
{
if(msgHead == Message::MSGHEADER)
{
return true;
}
}
return false;
}
quint32 SocketWorker::getMsgLen()
{
while(sock.bytesAvailable()<sizeof(quint32));
quint32 msgLen=0;
AuxFuncSet::readQuint32(&sock,msgLen);
return msgLen;
}
void SocketWorker::getMsg(quint32 msgLen, QByteArray& msgBytes)
{
while(sock.bytesAvailable()<msgLen);
msgBytes = sock.read(msgLen);
}
void SocketWorker::WorkerEmitSigLogin(bool loginStatus)
{
emit Sig_LoginStatus(loginStatus);
}
The error message provided by compiler is
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0xb988c8), parent's thread is QThread(0xb93528), current thread is QThread(0x8dfe2c)
From my debugging, I find the key section is signal-slot connect part that causes this problem.
Specifically,there are four connection statements.
i.e.,
connect(&workThread,SIGNAL(finished()),worker,SLOT(deleteLater()));
connect(this,SIGNAL(SIG_CONECTSERVER()),worker,SLOT(connectServer()),Qt::QueuedConnection);
connect(this,SIGNAL(SIG_WRITEMSG(QByteArray&)),worker,SLOT(writeMsg(QByteArray&)));
connect(worker,SIGNAL(Sig_LoginStatus(bool)),this,SLOT(EmitSigCheckLogin(bool)));
But only the middle two statements causes this error.Concretely,if I delete the middle two statements,i.e.,
connect(this,SIGNAL(SIG_CONECTSERVER()),worker,SLOT(connectServer()),Qt::QueuedConnection);
connect(this,SIGNAL(SIG_WRITEMSG(QByteArray&)),worker,SLOT(writeMsg(QByteArray&)));
then there are no errors.
So I am confused.After one and a half hour,I still don't find a effective solution and a reasonable explanation.So I think I need help.Thanks in advance.

Adding code example to QT C++

I'd like to add some QT example code to my simple project. The sample code is here: https://wiki.qt.io/Download_Data_from_URL
It consists of filedownloader.cpp and filedownloader.h -- this code downloads a graphic from a supplied URL.
I've added these files to my project and get a clean compile. I think I understand the code ok (I'm mainly a c coder, not c++) but I don't understand how I can pass the QUrl created by my project to filedownloader.cpp
The "project" is just a simple main.cpp/mainwindow.cpp/mainwindow.ui that offers a button to be pressed. Pressing the button calls the routine below:
void MainWindow::on_pushButton_clicked()
{
// pass to filedownloader to process
QUrl fileloc("http://www.test.com/test.jpg");
}
How do I feed the QUrl fileloc to filedownload.cpp?
You have to add a new method to FileDownloader, that accepts QUrl and starts the download.
filedownloader.h:
#ifndef FILEDOWNLOADER_H
#define FILEDOWNLOADER_H
#include <QObject>
#include <QByteArray>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
class FileDownloader : public QObject
{
Q_OBJECT
public:
explicit FileDownloader(QUrl imageUrl, QObject *parent = 0);
virtual ~FileDownloader();
QByteArray downloadedData() const;
signals:
void downloaded();
public slots:
void download(QUrl url); // <------ Here it is
private slots:
void fileDownloaded(QNetworkReply* pReply);
private:
QNetworkAccessManager m_WebCtrl;
QByteArray m_DownloadedData;
};
#endif // FILEDOWNLOADER_H
filedownloader.cpp:
#include "filedownloader.h"
FileDownloader::FileDownloader(QObject *parent) :
QObject(parent)
{
connect(
&m_WebCtrl, SIGNAL (finished(QNetworkReply*)),
this, SLOT (fileDownloaded(QNetworkReply*))
);
// <------ Notice, that i've removed downloading code from here
}
FileDownloader::~FileDownloader() { }
void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
m_DownloadedData = pReply->readAll();
//emit a signal
pReply->deleteLater();
emit downloaded();
}
void FileDownloader::download(QUrl url) { // <------ And its definition
QNetworkRequest request(url);
m_WebCtrl.get(request);
}
QByteArray FileDownloader::downloadedData() const {
return m_DownloadedData;
}
And then your on_pushButton_clicked will look like this:
void MainWindow::on_pushButton_clicked()
{
// pass to filedownloader to process
QUrl fileloc("http://www.test.com/test.jpg");
m_filedownloader.download(fileloc);
}

Connect qnetworkaccessmanager to slot

Now I have this code:
requester.h
#ifndef REQUESTER_H
#define REQUESTER_H
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtCore/QtCore>
#include <QVector>
#include <QObject>
class Requester
{
Q_OBJECT
public:
Requester();
~Requester();
QString get_last_reply();
void send_request();
private:
QNetworkAccessManager *manager;
QVector<QString> replies;
public slots:
void get_reply(QNetworkReply *reply);
};
#endif // REQUESTER_H
requester.cpp
#include "requester.h"
Requester::Requester()
{
manager = new QNetworkAccessManager;
}
Requester::~Requester() {
delete manager;
}
void Requester::get_reply(QNetworkReply *reply) {
QByteArray res = reply->readAll();
QString data = res.data();
replies.push_back(data);
}
QString Requester::get_last_reply() {
QString res = replies.back();
replies.pop_back();
return res;
}
void Requester::send_request() {
QObject::connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(get_reply(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://google.com")));
}
And this error:
no matching function for call to 'QObject::connect(QNetworkReply*&, const char*, Requester* const, const char*)'
What's wrong? I've tried to use just connect instead of QObject::connect, but there was an error about the impossibility of converting QNetworkAccessmanager to socket.
The problem is that you are not inheriting QObject, so naturally: you cannot get slots handled in that class.
You should write something like this:
requester.h
class Requester : public QObject
{
Q_OBJECT
public:
explicit Requester(QObject *parent);
...
requester.cpp
#include "requester.h"
Requester::Requester(QObject *p)
: QObject(p)
, manager(new QNetworkAccessManager)
{
}
...
Also, there is little to no point in this case to construct the QNetworkAccessManager on the heap as opposed to the stack. You could just have a QNetworkAccessManager m_networkAccessManager; member without the allocation, construction, and deletion, but this is just an additional information for the future.

circular dependency QThread

I have a task to create a chess game with support for network play.
For development I've been using Qt.
The problem is as follows:
I have class "MyServer":
Header file->
//Header file "MyServer.h"
#ifndef MYSERVER_H
#define MYSERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <mythread.h>
#include <QDebug>
class MyServer : public QTcpServer
{
Q_OBJECT
public:
explicit MyServer(QObject *parent = 0);
void startServer();
QList<QString> *usersOnline;
QList<QTcpSocket*> *connections;
signals:
public slots:
protected:
void incomingConnection(int socketDescriptor);
private:
QTcpServer* server;
//QTcpSocket* socket;
//QByteArray* bytes;
//QString* str;
};
#endif // MYSERVER_H
cpp.file
#include "myserver.h"
MyServer::MyServer(QObject *parent) :
QTcpServer(parent)
{
}
void MyServer::startServer()
{
this->listen(QHostAddress::Any,1234);
usersOnline=new QList<QString>;
}
void MyServer::incomingConnection(int socketDescriptor)
{
MyThread* thread=new MyThread(socketDescriptor,this,this);
thread->run();
}
As you can see, this class with a new connection creates a new thread.
Class "MyThread".
Header file
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
#include <QDataStream>
#include <QObject>
#include <myserver.h>
class MyServer;
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(int ID,MyServer* s,QObject *parent = 0);
void run();
signals:
public slots:
void readyRead();
private:
QTcpSocket* socket;
int socketDescriptor;
};
#endif // MYTHREAD_H
cpp.file
#include "mythread.h"
MyThread::MyThread(int ID,MyServer* s,QObject *parent) :
QThread(parent)
{
//this->mainserver=parent;
//parent=new MyServer();
//qDebug()<<s->usersOnline;
this->socketDescriptor=ID;
}
void MyThread::run()
{
qDebug()<<"Starts thread";
socket=new QTcpSocket();
socket->setSocketDescriptor(this->socketDescriptor);
connect(socket,SIGNAL(readyRead()),this,SLOT(readyRead()),Qt::DirectConnection);
exec();
}
void MyThread::readyRead()
{
QDataStream in(this->socket);
quint32 n;
in>>n;
qDebug()<<n;
QByteArray bytes;
QDataStream out(&bytes,QIODevice::WriteOnly);;
QString str;
switch (n) {
case 1:
in>>str;
qDebug()<<str;
//usersOnline->append(str);
//qDebug()<<*(usersOnline);
//out(&bytes,QIODevice::WriteOnly);
//out<<(*usersOnline);
//for(int i=0;i<this->connections->length();i++)
//{
//connections->at(i)->is
//connections->at(i)->write(bytes);
//connections->at(i)->waitForBytesWritten(2000);
// }
break;
case 2:
in>>str;
qDebug()<<str;
break;
}
}
In "MyThread" constructor i pass pointer to "MyServer" class to use the fields "usersOnline","connections" and "MyServer" method.
Thus, i have in my architecture "circular dependency" with "MyServer" and "MyThread" class.
How to change architecture?
Thanks a lot.
Remove #include <myserver.h> from your thread header. You already have class MyServer; forward definition there, it will be enough.