I am trying to create two test applications based on QTcpSocket, a server that streams (uncompressed) images and a client that receives them.
My code is mostly taken from Qts Fortune Client Example and Fortune Server Example. I have cut out all gui. Instead of opening a connection, sending a fortune and then immediately closing it, my server keeps it open and continuously streams my image. The client just reads the images from the QTcpSocket and then discards them.
The image I'm sending is 800x600 RGB (=1440000 bytes), I am sending it as often as I am allowed. The image is read from file each time before it is sent and I am not using any compression.
The server seems to be sending images just as it should. BUT the client receives them too slowly, 1-4 frames per second, and it seems to not be receiving any data at times, which in turn causes my server to use alot of memory (because the client isn't reading as fast as the server is writing).
I've tried running my server and client on different machines and both on one machine, both setups produce the same problem.
When running my applications on a Linux machine, the client receives images at a much higher rate (think it was 14 frames per second). The client seems to be able to read as fast as the server writes.
Can anyone help shed some light on this problem?
How can I speed up the data transfere? (without using compression)
How can I get the client to read as fast as the server is writing?
How can I get this to be stable on my Windows machine? (no sudden pauses...) Clicking in the console window of the client sometimes seem to "wake" the application btw. ^^
Here is my code:
Server:
main.cpp
#include <iostream>
#include <QCoreApplication>
#include "Server.h"
int main(int argc, char *argv[]){
QCoreApplication app(argc, argv);
QString ipaddress = QString(argv[1]);
int port = atoi(argv[2]);
Server* server = new Server(ipaddress, port);
int retVal = app.exec();
return retVal;
}
Server.h
#ifndef SERVER_H_
#define SERVER_H_
#include <QObject>
QT_BEGIN_NAMESPACE
class QTcpServer;
class QNetworkSession;
class QTcpSocket;
class QTimer;
QT_END_NAMESPACE
class Server : public QObject
{
Q_OBJECT
public:
Server(QString ipAddress, int port, QObject *parent = 0);
private slots:
void newConnectionSlot();
void sendSlot();
private:
QTcpServer *mTcpServer;
QTcpSocket *mTcpSocket;
QTimer *mSendTimer;
};
#endif /* SERVER_H_ */
Server.cpp
#include "Server.h"
#include <iostream>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <QDateTime>
#include <QSettings>
#include <highgui.h>
Server::Server(QString ipAddress, int port, QObject *parent) :
QObject(parent), mTcpServer(0), mTcpSocket(0)
{
mTcpServer = new QTcpServer(this);
connect(mTcpServer, SIGNAL(newConnection()), this, SLOT(newConnectionSlot()));
if (!mTcpServer->listen(QHostAddress(ipAddress), port)) {
std::cout << "Unable to start the server: " << mTcpServer->errorString().toStdString() << std::endl;
return;
}
std::cout << "The server is running on\n\nIP: "<< ipAddress.toStdString()
<< "\nport: " << mTcpServer->serverPort() << "\n\nRun the Client now.\n" << std::endl;
}
void Server::newConnectionSlot()
{
mTcpSocket = mTcpServer->nextPendingConnection();
connect(mTcpSocket, SIGNAL(disconnected()),
mTcpSocket, SLOT(deleteLater()));
// setup timer to send data at a given interval
mSendTimer = new QTimer(this);
connect(mSendTimer, SIGNAL(timeout()),
this, SLOT(sendSlot()));
mSendTimer->start(40);
}
void Server::sendSlot()
{
if(!mTcpSocket)
return;
//know that the image is this big
int width = 800;
int height = 600;
int nChannels = 3;
int depth = 8;
qint64 blockSize = 1440000; //in bytes
qint64 imagesInQue = mTcpSocket->bytesToWrite()/blockSize;
int maxPendingImages = 25;
if(imagesInQue > maxPendingImages)
{
std::cout << "Dumping." << std::endl;
return;
}
//load image
IplImage* img = cvLoadImage("pic1_24bit.bmp");
if(!img)
std::cout << "Error loading image " << std::endl;;
//send data
quint64 written = mTcpSocket->write(img->imageData, img->imageSize);
//clean up
cvReleaseImage( &img );
}
Client:
main.cpp
#include <iostream>
#include <QCoreApplication>
#include "Client.h"
int main(int argc, char *argv[]){
QCoreApplication app(argc, argv);
QString ipaddress = QString(argv[1]);
int port = atoi(argv[2]);
Client* client = new Client(ipaddress, port);
int retVal = app.exec();
}
Client.h
#ifndef CLIENT_H_
#define CLIENT_H_
#include <QObject>
#include <QAbstractSocket>
QT_BEGIN_NAMESPACE
class QTcpSocket;
QT_END_NAMESPACE
class Client : public QObject
{
Q_OBJECT
public:
Client(QString ipAddress, int port, QObject *parent=0);
private slots:
void readSlot();
void displayErrorSlot(QAbstractSocket::SocketError);
private:
QTcpSocket *mTcpSocket;
QString mIpAddress;
int mPort;
};
#endif /* CLIENT_H_ */
Client.cpp
#include "Client.h"
#include <iostream>
#include <QTcpSocket>
#include <QSettings>
#include <QDateTime>
Client::Client(QString ipAddress, int port, QObject *parent):
QObject(parent), mTcpSocket(0), mIpAddress(ipAddress), mPort(port)
{
mTcpSocket = new QTcpSocket(this);
connect(mTcpSocket, SIGNAL(readyRead()),
this, SLOT(readSlot()));
connect(mTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(displayErrorSlot(QAbstractSocket::SocketError)));
std::cout << "Connecting to ip: " << mIpAddress.toStdString() << " port: " << mPort << std::endl;
mTcpSocket->connectToHost(mIpAddress, mPort);
}
void Client::readSlot()
{
static qint64 starttime = QDateTime::currentMSecsSinceEpoch();
static int frames = 0;
//know that the image is this big
int width = 800;
int height = 600;
int nChannels = 3;
int depth = 8;
qint64 blockSize = 1440000; //in bytes
if (mTcpSocket->bytesAvailable() < blockSize)
{
return;
}
frames++;
char* data = (char*) malloc(blockSize+100);
qint64 bytesRead = mTcpSocket->read(data, blockSize);
free(data);
//FPS
if(frames % 100 == 0){
float fps = frames/(QDateTime::currentMSecsSinceEpoch() - starttime);
std::cout << "FPS: " << fps << std::endl;
}
}
void Client::displayErrorSlot(QAbstractSocket::SocketError socketError)
{
switch (socketError) {
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
std::cout << "The host was not found. Please check the "
"host name and port settings."<< std::endl;
break;
case QAbstractSocket::ConnectionRefusedError:
std::cout << "The connection was refused by the peer. "
"Make sure the fortune server is running, "
"and check that the host name and port "
"settings are correct."<< std::endl;
break;
default:
std::cout << "The following error occurred: " << mTcpSocket->errorString().toStdString() << std::endl;
break;
}
}
You are miscounting your frames. You must increment the frame counter after the mTcpSocket->bytesAvailable() < blockSize test! That's the source of your non-problem: it works OK, but you miscount the frames. That's also why it "works better" on Linux: the network stack there is buffering the data differently, giving you less signals.
You must limit the amount of memory on the wire (buffered) on the server end. Otherwise, as you correctly noticed, you'll run out of memory. See my other answer for an example of how to do it.
You're never freeing the memory you malloc() in the receiver. Be aware that you should expect to consume 35 megabytes of RAM per second if the server really sends at 40ms intervals. The receiver may well get bogged down very quickly by that memory leak. That's probably the source of the trouble.
What is that QDataStream for? You're not using a datastream to send the image, and there's no use for it on the receiving end either.
Related
To develop my program first without connecting two physical machines on serial port, I downloaded and used this program to simulate COM ports:
https://sourceforge.net/projects/com0com/
I connected virtual COM4 to virtual COM5. It works fine.
Using Br#y's Terminal program, I tested if I connect to COM4 in one Terminal instance, and to COM5 in another instance on the same computer, the data that I send on one terminal arrives in the other terminal, and vice versa.
Terminal program: https://sites.google.com/site/terminalbpp/
Now let's see the problem:
I used SerialPortReader class from this official Qt sample code for async serial read: https://code.qt.io/cgit/qt/qtserialport.git/tree/examples/serialport/creaderasync
It connects to COM5 and sets baud rate to 9600 successfully, but no data arrives if I send something via Terminal to COM4, so: SerialPortReader runs through with no error, but after then, no matter what message I send on my Terminal instance, handleReadyRead, handleError, and handleTimeout never get called.
(If I have already a terminal emulator connected to the virtual COM5 port, then connection in my C++ program fails, so indeed the open() check works fine.
Also, if I try to send more than one messages to my program via the virtual COM4 port, Terminal freezes, which is a clear sign of that the previous message has not yet been read on the other side(COM5).)
I have googled a lot, but have not yet found any solutions. Someone here said that it is/was a bug Qt Serial Port Errors - Data not getting read and that the problem is in qserialport_win.cpp, but even if I change that and compile my program again, nothing happens.
I use the following code to create the class, but the class' content is unchanged, I use it as I found in the sample program:
// Serial comm init
QSerialPort serialPort;
QString serialPortName = "COM5";
serialPort.setPortName(serialPortName);
int serialPortBaudRate = 9600;
if (serialPort.open(QIODevice::ReadOnly)) {
if(serialPort.setBaudRate(serialPortBaudRate) &&
serialPort.setDataBits(QSerialPort::Data8) &&
serialPort.setParity(QSerialPort::NoParity) &&
serialPort.setStopBits(QSerialPort::OneStop) &&
serialPort.setFlowControl(QSerialPort::NoFlowControl)) {
//SerialPortReader serialPortReader(&serialPort);
SerialPortReader serialPortReader(&serialPort, this);
} else {
std::cout << "Failed to set COM connection properties " << serialPortName.toStdString() << serialPort.errorString().toStdString() << std::endl;
}
} else {
std::cout << "Failed to open port " << serialPortName.toStdString() << serialPort.errorString().toStdString() << std::endl;
}
I would appreciate any help. Thanks!
Today I figured out a sketchy but working version:
SerialPortReader.h
#pragma once
#include <QtCore/QObject>
#include <QByteArray>
#include <QSerialPort>
#include <QTextStream>
#include <QTimer>
class SerialPortReader : public QObject {
Q_OBJECT
public:
explicit SerialPortReader(QObject *parent = 0);
~SerialPortReader() override;
void close();
private:
QSerialPort *serialPort = nullptr;
QByteArray m_readData;
QTimer m_timer;
public slots:
void handleReadyRead();
//void handleTimeout();
//void handleError(QSerialPort::SerialPortError error);
};
SerialPortReader.cpp
#include <iostream>
#include "SerialPortReader.h"
SerialPortReader::SerialPortReader(QObject *parent) : QObject(parent)
{
serialPort = new QSerialPort(this);
const QString serialPortName = "COM4"; //argumentList.at(1);
serialPort->setPortName(serialPortName);
const int serialPortBaudRate = QSerialPort::Baud9600;
serialPort->setBaudRate(serialPortBaudRate);
if (!serialPort->open(QIODevice::ReadOnly)) {
std::cout << "Failed to open port" << std::endl;
//return 1;
}
std::cout << "SerialPortReader(QSerialPort *serialPort, QObject *parent)" << std::endl;
connect(serialPort, SIGNAL(readyRead()), this, SLOT(handleReadyRead()), Qt::QueuedConnection);
// connect(serialPort, &QSerialPort::readyRead, this, &SerialPortReader::handleReadyRead);
//connect(serialPort, &QSerialPort::errorOccurred, this, &SerialPortReader::handleError);
//connect(&m_timer, &QTimer::timeout, this, &SerialPortReader::handleTimeout);
//m_timer.start(5000);
}
void SerialPortReader::handleReadyRead()
{
std::cout << "handleReadyRead()" << std::endl;
m_readData.append(serialPort->readAll());
if (!m_timer.isActive())
m_timer.start(5000);
}
/*
void SerialPortReader::handleTimeout()
{
std::cout << "handleTimeout()" << std::endl;
if (m_readData.isEmpty()) {
std::cout << "No data was currently available for reading" << std::endl;
} else {
std::cout << "Data successfully received" << std::endl;
//m_standardOutput << m_readData << Qt::endl;
}
//QCoreApplication::quit();
}
void SerialPortReader::handleError(QSerialPort::SerialPortError serialPortError)
{
std::cout << "handleError()" << std::endl;
if (serialPortError == QSerialPort::ReadError) {
std::cout << "An I/O error occurred while reading" << std::endl;
//QCoreApplication::exit(1);
}
}
*/
SerialPortReader::~SerialPortReader() {
close();
}
// Close the files, filestreams, etc
void SerialPortReader::close() {
// ...
}
... and in my QApplication code you just need to include the .h and write this to instantiate the serial listener:
SerialPortReader *serialPortReader = new SerialPortReader(this);
I am trying to create an Application that lets the user input an integer value and then sends it via tcp to my esp32.
I have set up my esp32 as a tcp server which connects to my wifi router and than shows its ipv4 adress via serial Monitor. The esp32 is connected to 2 stepperdriver and shall later control them with the data from the Application.
So my Application is working when the data that has to be sent is known before runtime for example i could create a const char* variable with the desired data and then simply send it with socket->write(foo);.
But thats not working for me because i need to get input from the console at runtime and then pass it to the function socket->write(). The problem there is that the QTcpSocket Object has to be in another thread. So i am stuck at figuring out how to pass data in between threads.
I tried to use the signal and slot mechanism to do so which if i correctly understood is meant to be used for that.
Especially this line gives me a huge headache. (the line is in the main function)
emit IO->doSendData(IO->getInput());
I try to emit the signal doSendData and pass the input from the console to the connected slot sendData() which is a method from the network class which object is in the threadNET and the object thats getting the input from the console lives in the main thread i guess thats the problem here but i have no glue how to fix it.
I dont get any error messages in QTcreator.
Thanks in advance for taking your time to help me.
If something is unclear feel free to ask me anything. Thats my first post on Stack overflow and i would appreciate any feedback on how to increase the quality of my question.
Complete code
main.cpp
//MAIN
#include <QCoreApplication>
#include <network.h>
#include <userio.h>
#include <QThread>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread* threadNET = new QThread();
network* net = new network();
userIO* IO = new userIO();
net->moveToThread(threadNET);
QObject::connect(threadNET,
&QThread::started,
net,
&network::Connect);
QObject::connect(IO,
&userIO::doSendData,
net,
&network::sendData);
threadNET->start();
while(true)
{
emit IO->doSendData(IO->getInput());
}
return a.exec();
}
network.h
//NETWORK HEADER
#ifndef NETWORK_H
#define NETWORK_H
#include <QObject>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QString>
#include <QDebug>
#include <iostream>
#include <string>
class network : public QObject
{
Q_OBJECT
public:
explicit network(QObject *parent = nullptr);
~network();
signals:
void finished(QString ffs);
void error(QString err);
public slots:
void Connect();
void sendData(QString dataToSend);
private:
QTcpSocket *socket;
};
#endif // NETWORK_H
userIO.h
//USERIO HEADER
#ifndef USERIO_H
#define USERIO_H
#include <QObject>
#include <QString>
#include <iostream>
#include <QDebug>
#include <limits>
#include <string>
class userIO : public QObject
{
Q_OBJECT
public:
explicit userIO(QObject *parent = nullptr);
QString getInput();
signals:
void doSendData(QString dataToSend);
public slots:
};
#endif // USERIO_H
network.cpp
//NETWORK SOURCE
#include "network.h"
network::network(QObject *parent) : QObject(parent)
{
}
network::~network()
{
}
void network::Connect()
{
socket = new QTcpSocket(this);
socket->connectToHost("192.168.179.167", 80);
if(socket->waitForConnected(5000))
{
std::cout << "Connected to TcpServer";
}
else
{
qDebug() << "Error: " << socket->errorString();
}
emit finished("send help");
}
void network::sendData(QString dataToSend)
{
qDebug() << "sendData" << dataToSend << "\n";
std::string convert = dataToSend.toStdString();
const char* formattedData = convert.c_str();
socket->write(formattedData);
}
userIO.cpp
//USERIO SOURCE
#include "userio.h"
userIO::userIO(QObject *parent) : QObject(parent)
{
}
QString userIO::getInput()
{
std::string rawInput;
qDebug() << "Enter the amount of steps to go\n";
std::cin >> rawInput;
while(std::cin.fail())
{
qDebug() << "Invalid input only Integer numbers are allowed\n";
qDebug() << "try again...\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin >> rawInput;
}
QString convert;
convert.fromStdString(rawInput);
return convert;
}
QString::fromStdString(const std::string &str)
is a static member that returns a copy of the str string.
calling convert.fromStdString(rawInput); does not store copied string in convert !!
you need to write :
convert = QString::fromStdString(rawInput)
OR
direct initialization:
...
QString convert(QString::fromStdString(rawInput));
return convert;
Here is a short UDP server example in Qt below which does work but what I don't like is that I'm polling to see if new data is available. I've come across some examples of a readyRead() but they all seem to introduce a qt class. Do I need to use a qt class in order to take advantage of the readyRead() signal?
Here is the working but simple UDP server implemented entirely in main:
#include <QDebug>
#include <QUdpSocket>
#include <QThread>
int main(int argc, char *argv[])
{
QUdpSocket *socket = new QUdpSocket();
u_int16_t port = 7777;
bool bindSuccess = socket->bind(QHostAddress::AnyIPv4, port);
if (!bindSuccess) {
qDebug() << "Error binding to port " << port << " on local IPs";
return a.exec();
}
qDebug() << "Started UDP Server on " << port << endl;
QHostAddress sender;
while (true) {
while (socket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(socket->pendingDatagramSize());
socket->readDatagram(datagram.data(),datagram.size(),&sender,&port);
qDebug() << "Message From :: " << sender.toString();
qDebug() << "Port From :: "<< port;
qDebug() << "Message :: " << datagram.data();
}
QThread::msleep(20);
}
return 0;
}
Here is an example of the readyRead() signal:
https://www.bogotobogo.com/Qt/Qt5_QUdpSocket.php
I haven't really figured out how to get this to work yet. I must be doing something wrong. Here is the UDP connection code i'm trying:
#include "myudp.h"
MyUDP::MyUDP(QObject *parent) : QObject(parent) {
}
void MyUDP::initSocket(u_int16_t p) {
port = p;
udpSocket = new QUdpSocket(this);
bool bindSuccess = udpSocket->bind(QHostAddress::LocalHost, port);
if (!bindSuccess) {
qDebug() << "Error binding to port " << port << " on local IPs";
return;
}
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}
void MyUDP::readPendingDatagrams() {
QHostAddress sender;
while (udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &port);
qDebug() << "Message From :: " << sender.toString();
qDebug() << "Port From :: " << port;
qDebug() << "Message :: " << datagram.data();
}
}
myudp.h
#include <QObject>
#include <QUdpSocket>
class MyUDP : public QObject
{
Q_OBJECT
public:
explicit MyUDP(QObject *parent);
void initSocket(u_int16_t p);
u_int16_t port;
QUdpSocket *udpSocket;
signals:
public slots:
void readPendingDatagrams();
};
new main.cpp
int main(int argc, char *argv[])
{
MyUDP *myUDP = new MyUDP(0);
myUDP->initSocket(port);
while (true) {
usleep(1000);
}
return 0;
}
I am testing with:
netcat 127.0.0.1 -u 7777
{"cid"="0x1234123412341", "fill_level"=3245 }<cr>
What you're doing wrong is that you're not letting Qt's event loop run. i.e. this is incorrect:
int main(int argc, char *argv[])
{
MyUDP *myUDP = new MyUDP(0);
myUDP->initSocket(port);
while (true) {
usleep(1000);
}
return 0;
}
... instead, you should have something like this:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// connect needs to occur after QCoreApplication declaration
MyUDP *myUDP = new MyUDP(0);
myUDP->initSocket(port);
return app.exec();
}
... it is inside the app.exec() call where a Qt application spends most of its time (app.exec() won't return until Qt wants to quit), and there is where Qt will handle your UDP socket's I/O and signaling needs.
Please modify your processPendingDatagrams like this, to let newer incoming data be processed:
void MyUDP::readPendingDatagrams() {
QHostAddress sender;
uint16_t port;
QByteArray datagram; // moved here
while (udpSocket->hasPendingDatagrams()) {
//QByteArray datagram; // you don't need this here
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &port);
qDebug() << "Message From :: " << sender.toString();
qDebug() << "Port From :: " << port;
qDebug() << "Message :: " << datagram.data();
}
// God knows why, there is always one more "dummy" readDatagram call to make,
// otherwise no new readyRead() will be emitted, and this function would never be called again
datagram.resize(udpSocket->pendingDatagramSize());
socket->readDatagram(datagram.data(),datagram.size(),&sender,&port);
}
****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
I try to write a little client-server program. A client should detect all servers running in the same LAN. I tried to implement a UDP-Broadcast with qt but my read function returns -1 all the time. If I call the dataAvailable()-function on the socket, it says that 4 bytes are available to read, but for some reason it fails to read them.
For now I'm trying to receive the broadcast on the same machine and in the same program
Here's my main:
#include <iostream>
#include <QString>
#include <QThread>
#include "../include/network.h"
using namespace std;
using namespace network;
int main () {
Network *n = new Network ();
n->broadcast("test");
if (n->dataAvailable()) {
cout << "Data available: ";
cout << n->getData().toStdString() << std::endl;
} else {
cout << "No data" << endl;
}
delete n;
}
and my Network-class:
#ifndef NETWORK
#define NETWORK
#include <QObject>
#include <QUdpSocket>
#include <iostream>
namespace network {
class Network : public QObject {
Q_OBJECT
public:
const static int BROADCAST_PORT = 52433;
explicit Network(QObject *parent = 0) {
socket = new QUdpSocket ();
socket->bind (QHostAddress::Any, BROADCAST_PORT);
}
~Network () {
delete socket;
}
bool dataAvailable () {
return socket->hasPendingDatagrams();
}
QString getData () {
if (!dataAvailable()) {
return "";
}
char *data = 0;
std::cout << socket->pendingDatagramSize() << std::endl;
std::cout << QString("recv: %1")
.arg(socket->readDatagram(data,
socket->pendingDatagramSize())).toStdString();
return QString(data);
}
void broadcast(QString data) {
QUdpSocket *broadcast = new QUdpSocket ();
broadcast->connectToHost(QHostAddress::Broadcast, BROADCAST_PORT);
std::cout << broadcast->write(data.toStdString().c_str())
<< std::endl << std::endl;
delete broadcast;
}
private:
QUdpSocket *socket;
};
}
#endif // NETWORK
the Output is the following:
4
Data available: 4
recv -1
Which means
4 Bytes are sent from the broadcast
the socket detects that 4 bytes are available
the attempt to read the 4 bytes fails
the QString returned by getData() is empty
Unfortunately I could not find any possibility to get more information on the error
You definitely need to allocate memory for reading datagram. Now you try to write it to null pointer.
// char *data = 0;
std::cout << socket->pendingDatagramSize() << std::endl;
QVector<char> buffer(socket->pendingDatagramSize()); // create buffer
std::cout << QString("recv: %1")
.arg(socket->readDatagram(buffer.data(),
socket->pendingDatagramSize())).toStdString();