This following code retrieves a JSON file from a NGROK url, which leads to the directory listing of the game files, where the JSON file is stored:
//header file
#ifndef CLIENT_H
#define CLIENT_H
#include <QWidget>
#include <QtNetwork>
#include <QFile>
#include "board.h"
class Client : public QObject
{
public:
void startRequest();
void processRequest();
private:
Board b;
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
};
#endif // CLIENT_H
//cpp file
#include "client.h"
#include "board.h"
//go to the NGROK url and retrieve the json file containing the game data
void Client::startRequest()
{
QUrl json = QUrl::fromUserInput("URL goes here");
QNetworkRequest request;
request.setUrl(json);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply *reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, &Client::processRequest);
}
//retrieve the data from the json file
void Client::processRequest()
{
QNetworkRequest request;
QJsonObject data;
QNetworkReply *reply = manager->post(request, QJsonDocument(data).toJson());
if(reply->error() == QNetworkReply::NoError)
{
QByteArray response = reply->readAll();
qDebug() << response;
QJsonDocument doc = QJsonDocument::fromJson(response);
QJsonObject data = doc.object();
}
else
{
qDebug() << "File failed to load --" << reply->errorString();
manager->clearAccessCache();
}
reply->deleteLater();
}
It executes successfully, but qDebug() returns an empty byte array, resulting in the QJSON document and objects being empty as well. What could be causing this?
Related
I want to write data from another thread in txt file.
Text files will be named currentTime() and created periodically every minute.
So, Txt file made very well every minute using QTimer, but In Txt file, content is null.
Of course this result is correct. Because i didn't connect data from another Thread.
How to connect or send data from another thread to QTimer make the txt file every minute?
For example, another thread get data every second and QTimer make txt file every minute.
Ideally, another thread get 60 data in minute and one text file made in minute.
I want to write 60 data to txt file one cycle.
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QTcpSocket>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(qintptr ID, QObject *parent = 0);
void run();
signals:
void error(QTcpSocket::SocketError socketerror);
void sendValue(QString strValue);
void close(QString disconnectId);
private slots:
void readyRead();
void disconnected();
void InitThreadObjects();
void sendData();
private:
QTcpSocket *socket;
qintptr socketDescriptor;
QByteArray Data;
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QThread>
#include <QTimer>
#include <QTime>
#include <QFile>
#include <QString>
#include <QTextStream>
MyThread::MyThread(qintptr ID, QObject *parent) :
QThread(parent)
{
this->socketDescriptor = ID;
}
int count = 0;
void MyThread::run()
{
// thread starts here
qDebug() << " Thread started";
socket = new QTcpSocket();
// set the ID
if(!socket->setSocketDescriptor(this->socketDescriptor))
{
// something's wrong, we just emit a signal
emit error(socket->error());
return;
}
// connect socket and signal
// note - Qt::DirectConnection is used because it's multithreaded
// This makes the slot to be invoked immediately, when the signal is emitted.
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(this, &MyThread::started, this, &MyThread::InitThreadObjects);
// We'll have multiple clients, we want to know which is which
qDebug() << socketDescriptor << "Client connected";
QString strValue = QString::number(socketDescriptor);
emit sendValue(strValue);
// make this thread a loop,
// thread will stay alive so that signal/slot to function properly
// not dropped out in the middle when thread dies
exec();
}
void MyThread::readyRead()
{
// get the information
Data = socket->readAll();
// will write on server side window
qDebug() << socketDescriptor << " Data in: " << Data;
socket->write(Data);
}
void MyThread::disconnected()
{
qDebug() << socketDescriptor << " Disconnected";
QString disconnectId = QString::number(socketDescriptor);
emit close(disconnectId);
socket->deleteLater();
exit(0);
}
void MyThread::InitThreadObjects(){
QTimer *timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(sendData()));
timer->start(60000);
}
void MyThread::sendData(){
QTime currentTime = QTime::currentTime();
QString time = currentTime.toString("hhmmss");
QString filename = "/home/pi/"+time+".txt";
qDebug() << filename;
QFile file(filename);
if(!file.open(QFile::WriteOnly | QFile::Text))
{
qDebug() << "Could not open file for writing";
return;
}
QTextStream out(&file);
out << Data;
file.flush();
file.close();
}
You need to create QTimer in another thread
public slots:
void InitThreadObjects();
and in constructor add initialization
connect(this, &Thread::started, this, &Thread::InitThreadObjects);
void Thread::InitThreadObjects(){
m_Timer = new QTimer(this);
connect(m_Timer, &QTimer::timeout, this, &Thread::TimeWriteData);
m_Timer->start(60 * 1000);
}
record the collected data
void Thread::TimeWriteData(){
// Write data to file
}
I have a problem with Qt QNetworkAccessManager. I want to use it to interact with my web server which propose an POST api.
In my main function, I can use QNetworkAccessManager::post(). My server receive the data from the client.
But if I move the post function in another thread, my server receive anything.
Here an example of my code :
Worked code :
#include <iostream>
#include <QNetworkRequest>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QCoreApplication>
#include <thread>
#include <QThread>
#include <QObject>
#include <QNetworkReply>
class PostClass: public QObject {
Q_OBJECT
public:
PostClass() {
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onfinish(QNetworkReply*)));
};
QNetworkAccessManager* manager = nullptr;
public slots:
void post() {
const QUrl url("http://[ip:port]/users");
QNetworkRequest req(url);
manager->get(req);
QNetworkRequest req1(QUrl("[ip:port]/event"));
req1.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
qDebug() << "Send new request";
QByteArray data(R"({"data": "QtGroupHEllo"})");
manager->post(req1, data);
};
void onfinish(QNetworkReply* rep) {
qDebug() << "reply delete!";
rep->deleteLater();
qDebug() << "https post_request done!";
};
};
class ThreadTmp: public QThread {
Q_OBJECT
public:
ThreadTmp() = default;
public slots:
void onfinish(QNetworkReply* rep) {
rep->deleteLater();
qDebug() << "reply delete!";
qDebug() << "https post_request done!";
};
signals:
void postRequest();
protected:
void run() override {
while (1) {
emit postRequest();
sleep(1);
}
}
};
int main(int argc, char* argv[]) {
QCoreApplication app (argc, argv);
std::cout << "Hello, World!" << std::endl;
PostClass postObj;
ThreadTmp tmp();
QObject::connect(&tmp, SIGNAL(postRequest()), &postObj, SLOT(post()));
tmp.start();
return app.exec();
}
#include "main.moc"
But if I move the PostClass instance in my ThreadTmp class, in the run function, only get works and post doesn't work because in my server, I only received the get request and not the post request :
#include <iostream>
#include <QNetworkRequest>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QCoreApplication>
#include <thread>
#include <QThread>
#include <QObject>
#include <QNetworkReply>
class PostClass: public QObject {
Q_OBJECT
public:
PostClass() {
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onfinish(QNetworkReply*)));
// connect(manager, SIGNAL(finished(QNetworkReply*)), manager, SLOT(deleteLater()));
};
QNetworkAccessManager* manager = nullptr;
public slots:
void post() {
const QUrl url("http://[ip:port]/users");
QNetworkRequest req(url);
manager->get(req);
QNetworkRequest req1(QUrl("http://[ip:port]/event"));
req1.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
qDebug() << "Send new request";
QByteArray data(R"({"data": "QtGroupHEllo"})");
manager->post(req1, data);
};
void onfinish(QNetworkReply* rep) {
qDebug() << "reply delete!";
rep->deleteLater();
qDebug() << "https post_request done!";
};
};
class ThreadTmp: public QThread {
Q_OBJECT
public:
ThreadTmp() = default;
public slots:
void onfinish(QNetworkReply* rep) {
rep->deleteLater();
qDebug() << "reply delete!";
qDebug() << "https post_request done!";
};
signals:
void postRequest();
protected:
void run() override {
PostClass postObj;
connect(this, SIGNAL(postRequest()), &postObj, SLOT(post()));
while (1) {
emit postRequest();
sleep(1);
}
}
};
int main(int argc, char* argv[]) {
QCoreApplication app (argc, argv);
std::cout << "Hello, World!" << std::endl;
ThreadTmp tmp();
tmp.start();
return app.exec();
}
#include "main.moc"
I don't know why I have this behavior ...
You need to use a blocking .exec() in order to have an event loop in a thread. Just overriding run(), causes the thread to fire off that code and then terminate. When you post via the QNetworkAccessManager, that actually occurs asynchronously, in the background, driven by the QEventLoop out of the box. It should not typically be necessary to use another thread, if you are interacting with a simple REST api, or even if using a get() to download a large file. Qt handles for you, what you are probably trying to avoid with another thread, i.e. clogging up the main thread and hindering performance with network traffic. The best reason to use another thread would be to process the response from the server in the event that's going to take a long time. To do that, just hook up the signals/slots across threads, while leaving the post operation / network manager in your main one. (Generally speaking of course...)
I'm new in QT development and I've to make a non-gui application that reads a token from a POST request and then launches some json requests using that token. My problem is what to do when the finished signal is launched. I've tried to pass the reply.readAll() to a QByteArray parameter of the object, but when I do this the value is always empty (""). My code was done based on this.
.h
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QObject>
#include <QNetworkAccessManager>
class QNetworkReply;
class Downloader : public QObject
{
Q_OBJECT
public:
Downloader(QObject* parent=0);
void test();
bool finished = false;
QByteArray data;
public slots:
void handleReply(QNetworkReply* reply);
protected:
QNetworkAccessManager m_manager;
};
#endif // DOWNLOADER_H
.cpp
#include "downloader.h"
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QVariant>
#include <QDebug>
Downloader::Downloader(QObject *parent) :
QObject(parent)
{
connect(&m_manager, SIGNAL(finished(QNetworkReply*)), SLOT(handleReply(QNetworkReply*)));
}
void Downloader::test() {
QNetworkRequest request;
QUrl url("http://192.168.25.25:8080/path/to/token");
QUrl postData;
postData.addQueryItem("client_id", "foo");
postData.addQueryItem("username", "bar");
postData.addQueryItem("password", "12345");
postData.addQueryItem("grant_type", "password");
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
QNetworkReply* rep = m_manager.post(request,postData.encodedQuery());
rep->setProperty("url", QVariant(url));
qDebug() << "Post " << rep << rep->url();
}
void Downloader::handleReply(QNetworkReply *reply) {
qDebug() << "Handle" << reply << reply->url();
qDebug() << "Dynamic property" << reply->property("url").isValid() << reply->property("url");
qDebug() << "ReadAll " << reply->readAll();
finished = true;
data =reply->readAll();
reply->deleteLater();
}
In main, the call is:
Downloader down;
down.test();
while (!down.finished)
{
usleep(3*1000*1100);//3,3s
cout << "no finalizado";
}
What I'm trying to do is to use the reply to fill a parameter and use this parameter from the main, when the finished bool is true. I know it's not correct, but I don't know how to manage the asynchronous nature of the request. What I need it's some guide to understand what I'm doing, since I've been searching in qt page, stackoverflow and others without success. Thanks in advance.
Update: my main function
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Downloader down;
down.test();
return a.exec();
}
In class Downloader, declare a signal to emit the data to a slot of another QObject derived class. For example, in Downloader::handleReply you call emit dataReady(data). In the main function add below code:
Downloader down;
JsonSender obj;
connect(&down, &Downloader::dataReady, &obj, &JsonSender::dataReady);
down.test();
return a.exec();
In the slot dataReady of class JsonSender you can parse the token and send the request.
I need a very simple console application to FTP into the server and get some file , I try with QNetworkAccessManager , and it seems like not working and getting nothing.
Here is my code ,
ftp.h
#ifndef FTP_H
#define FTP_H
#include <string>
#include <vector>
#include <qstring.h>
#include <qfile.h>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include "qdebug.h"
#include <QCoreApplication>
#include <QDebug>
#include <string>
#include <iostream>
#include <sstream>
#include <QTextStream>
#include <QObject>
class ftp
{
// Q_OBJECT
public:
ftp();
private:
QNetworkAccessManager *manager;
QNetworkReply *reply;
private slots:
void slotError(QNetworkReply::NetworkError code);
void replyFinished(QNetworkReply *reply);
};
#endif // FTP_H
ftp.cpp
#include "ftp.h"
#include "QDebug"
#include <qstring.h>
#include <qfile.h>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include "qdebug.h"
#include <QCoreApplication>
#include <QDebug>
#include <string>
#include <iostream>
#include <sstream>
#include <QTextStream>
#include <QObject>
using namespace std;
ftp::ftp()
{
qDebug() << "ZSS Server Program Running Now ...";
QTextStream cin(stdin);
QString username;
QString password;
QString ip;
QString port;
cout << "Username : ";
username = cin.readLine();
cout << "Password : ";
password = cin.readLine();
cout << "IP Address : ";
ip = cin.readLine();
cout << "Port Number : ";
port = cin.readLine();
QUrl url;
url.setScheme("ftp");
url.setHost("10.228.7.74");
url.setPath("/try/hello.txt");
url.setPort(21);
url.setUserName("ftpTest");
url.setPassword("ftpTest");
QNetworkRequest request(url);
//request.setUrl(url);
qDebug() << url;
//cout << "Downloading File...";
manager = new QNetworkAccessManager;
reply = manager->get(request);
QByteArray buffer = reply->readAll();
qDebug() << buffer;
// connect(reply , SIGNAL(error(QNetworkReply::NetworkError)), this,SLOT(slotError(QNetworkReply::NetworkError)));
// connect(manager, SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));
}
void ftp::slotError(QNetworkReply::NetworkError code)
{
qDebug() << reply->errorString();
}
void ftp::replyFinished(QNetworkReply *reply)
{
// QTextCodec *codec = QTextCodec::codecForName("utf8");
// QString all = codectoUniCode(reply->readAll());
// cout >> all;
// reply->deleteLater();
}
After I run my code , inside my folder , I can't see any file is there. So I qDebug() out all the response and buffer , I found that the buffer and response is empty , is there any part I was missing or doing other things wrong ? Can I get a simple example for this ??
Here is the console output screenshots,
I qDebug() the buffer , but the buffer doesn't have anythings on it , I create this ftp local server by using 3CDaemon , and I was point to the correct folder and my ftp server is running , I can use command prompt to ftp inside and get the file , just can't make it by using Qt.
Please help me , this have been trouble me for couple of days..Thanks..
QNetworkAccessManager::get is an asynchronous function. You can use the QNetworkReply it returns to monitor the progress of the download, but isn't ready to read immediately. You have to wait until the finished signal is emitted (either by the QNetworkAccessManager or the QNetworkReply) before you can read data from it.
If you really want the download to be synchronous, you can spin your own local event loop until the download finishes. For example
QEventLoop loop;
QNetworkAccessManager mgr;
connect(&mgr, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
QNetworkReply *reply = mgr.get(...);
loop.exec();
qDebug() << reply.readAll();
****UPDATED: I noticed that I get the segfault only on Windows, on Linux it's fine. On Windows I use QT 5.5 and MinGW32. I still want to know why.
**** Initial Question:
Nothing tricky here, I create a basic Console Application. I have a QNetworkAccessManager sending a Post() request. When I close the console, there is a segfault.
Note that the request is sent and received successfully, my question is only about that segfault.
If no Post() request are sent, no crash on closing the console. There is not much help from the stack.
Stack
0 ntdll!RtlFreeHeap 0x77b5e041
1 ucrtbase!free 0x5e4c5eab
2 LIBEAY32!CRYPTO_free 0x5e5a123e
Main.cpp
#include <QCoreApplication>
#include "CNetworkHandleTest.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CNetworkHandleTest net;
net.start();
return a.exec();
}
CNetworkHandleTest.cpp
#include "CNetworkHandleTest.h"
CNetworkHandleTest::CNetworkHandleTest()
{
m_Manager = new QNetworkAccessManager(this);
// Connect the network manager so we can handle the reply
connect(m_Manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*)));
m_nTotalBytes = 0;
}
CNetworkHandleTest::~CNetworkHandleTest()
{
disconnect();
m_Manager->deleteLater();
}
void CNetworkHandleTest::onFinished(QNetworkReply* reply)
{
// Look at reply error
// Called when all the data is receivedqDebug() << "Error code:" << reply->error();
qDebug() << "Error string:" << reply->errorString();
reply->close();
reply->deleteLater();
}
void CNetworkHandleTest::start()
{
// Configure the URL string and then set the URL
QString sUrl(BASE_URL);
sUrl.append("/console/5555/upload");
m_Url.setUrl(sUrl);
// Make the request object based on our URL
QNetworkRequest request(m_Url);
// Set request header (not sure how or why this works, but it works)
// \todo investigate
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlenconded");
// Make file object associated with our DB file
QFile file("/tx_database.db");
if(!file.open(QIODevice::ReadOnly))
{
qDebug() << "Failed to open file";
}
// Read the entire file as a binary blob
QByteArray data(file.readAll());
// Set our request to our request object
// Note: there should probably be a flag so that when start is called it does not do
// any processing in case we are already in the middle of processing a request
m_Request = request;
// Send it
m_Reply = m_Manager->post(m_Request, data);
// Need to connect the signals and slots to the new reply object (manager makes a new
// reply object every post; may need to investigate if memory should be freed when
// done processing a response)
connect(m_Reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(m_Manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
this, SLOT(onAuthenticationRequired(QNetworkReply*, QAuthenticator*)));
}
void CNetworkHandleTest::onReadyRead()
{
// Whenever data becomes available, this slot is called. It is called every time data
// is available, not when all the data has been received. It is our responsibility to
// keep track of how much we have received if we want to show progress or whatever
// but we do not need to keep track if we have received all the data. The slot
// OnFinished will be called when the all the data has been received.
qDebug() << "Bytes available:" << m_Reply->bytesAvailable();
m_nTotalBytes += m_Reply->bytesAvailable();
qDebug() << "Bytes thus far:" << m_nTotalBytes;
QByteArray responseData = m_Reply->readAll();
qDebug() << "Response" << responseData;
m_Reply->size();
}
void CNetworkHandleTest::onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator)
{
}
CNetworkHandleTest.h
#ifndef CNETWORKHANDLETEST_H
#define CNETWORKHANDLETEST_H
// Required packages
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QByteArray>
#include <QUrlQuery>
#include <QHostInfo>
#include <QObject>
#include <QUrl>
// Helper packages
#include <QCoreApplication>
#include <QFile>
#include <QDir>
// Our packages
#include <iostream>
#include <fstream>
#include <cstdlib>
#define BASE_URL "localhost:5000"
#define BOUNDARY "123456787654321"
class CNetworkHandleTest : public QObject
{
Q_OBJECT
public:
CNetworkHandleTest();
~CNetworkHandleTest();
void start();
protected Q_SLOTS:
void onFinished(QNetworkReply* reply);
void onReadyRead();
void onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator);
private:
QNetworkAccessManager* m_Manager;
QNetworkRequest m_Request;
QNetworkReply* m_Reply;
QUrl m_Url;
int m_nTotalBytes;
};
#endif // CNETWORKHANDLETEST_H
When you close the console, your program dies in a most ungraceful manner. You need to write some code to make it graceful instead:
The below is a complete test case:
// https://github.com/KubaO/stackoverflown/tree/master/questions/network-cleanup-40695076
#include <QtNetwork>
#include <windows.h>
extern "C" BOOL WINAPI handler(DWORD)
{
qDebug() << "bye world";
qApp->quit();
return TRUE;
}
int main(int argc, char *argv[])
{
SetConsoleCtrlHandler(&handler, TRUE);
QCoreApplication a(argc, argv);
QNetworkAccessManager mgr;
int totalBytes = 0;
QObject::connect(&mgr, &QNetworkAccessManager::finished, [](QNetworkReply *reply){
qDebug() << "Error string:" << reply->errorString();
});
QNetworkRequest request(QUrl{"http://www.google.com"});
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlenconded");
auto reply = mgr.post(request, QByteArray{"abcdefgh"});
QObject::connect(reply, &QIODevice::readyRead, [&]{
qDebug() << "Bytes available:" << reply->bytesAvailable();
totalBytes += reply->bytesAvailable();
qDebug() << "Bytes thus far:" << totalBytes;
reply->readAll();
});
QObject::connect(reply, &QObject::destroyed, []{
qDebug() << "reply gone";
});
QObject::connect(&mgr, &QObject::destroyed, []{
qDebug() << "manager gone";
});
return a.exec();
}
If you press Ctrl-C or click [x] on the console window, the shutdown is orderly, the output being:
[...]
bye world
reply gone
manager gone