When to check for error when using QIODevice's blocking interface (QTcpSocket and QFile) - c++

For learning purposes I made application that sends file across network (which work very well for me). Here I will post main part of code, the code that actually sends bytes, I think that is enough.
My primary question is: When, where, why and how should I check for errors? (looks like more than one question :) )
As you can see, I checked for errors by checking return values of every function that can warn me that way (I marked every check with number to make easier to those who want to help to answer and explain).
Is this necessary? Because it can expand code significantly.
Secondary question:
Is this what I made OK, is there a better way to do it?
while(!file->atEnd()){
if(isCancelled())//this is in a thread, and there is mechanism to cancel it
return;
if((readed = file->read(inter_buffer,BUFLEN)) == -1){ //1 <- marking check with "1"
emit errorOccurred(tr("Error while reading file."));
return;
}
if(socket->write(inter_buffer,readed) == -1){//2 QTcpSocket::write
emit errorOccurred(tr("Unable to send data. Probably the other side cancelled or there are connection problems."));
qDebug() << socket->error();
return;
}
rsofar += readed;
if(!socket->flush()){//3
emit errorOccurred(tr("Unable to send data. Probably the other side cancelled or there are connection problems."));
return;
}
//emit signal to inform GUI thread about progress
emit updateProgress((int)(((double)rsofar)/(double)filesize * 100.0));
if(!socket->waitForBytesWritten()){//4
//maybe this is not the right message, but that is not important now
emit errorOccurred(tr("Unable to send data. Probably the other side cancelled or there are connection problems."));
return;
}
}
Đ¢ertiary question is: In Java I would rely on Exceptions to handle this kind of problems. Why Qt functions does not throw exceptions? Is it because it is considered slow for C++ (because of stack unrolling), or just bad habit when programming in C++, or because it does not work well with signals and slots, or something else?

Exceptions can add memory and runtime overhead on some C++ implementations. It's not a problem on modern, well maintained C++ implementations - but Qt has to run and compile on some really obsolete or awkward platforms. Not only that - Qt (at least the core) has to compile and run properly with compiler's exception support disabled.
Your error code checking is almost correct. In your case, if write returns any size other than readed, it should be treated as an error. Grammar nitpick: the correct form is "read", not "readed". Yes, you have "written" but simply "read". English is weird like that ;)
There is no need to use flush(). Just waitForBytesWritten and then check how many bytes still remain to be written and report progress based on that. You're making things run slower since your approach can't amortize the latency of disk file access: you don't do network sending and file reading in parallel.
So, what you're doing is somewhat convoluted. You don't need to use blocking waitForX functions at all. You're running in a thread, so let's just use signals provided by QIODevice and use the default event loop that QThread's run() method is spinning. That way you can process multiple files in the same worker thread. Your implementation requires a dedicated thread for each file processed in parallel.
The code below should work. Simply use moveToThread to move it to a worker QThread - don't derive from QThread. To start sending, invoke the start() slot. To cancel sending, all you need to do is to call sender->deleteLater().
#include <QTcpSocket>
#include <QByteArray>
class Sender : public QObject {
Q_OBJECT
QIODevice * m_src;
QAbstractSocket * m_dst;
QByteArray m_buf;
qint64 m_hasRead;
qint64 m_hasWritten;
qint64 m_srcSize;
bool m_doneSignaled;
bool signalDone() {
if (!m_doneSignaled &&
((m_srcSize && m_hasWritten == m_srcSize) || m_src->atEnd())) {
emit done();
m_doneSignaled = true;
}
return m_doneSignaled;
}
Q_SLOT void dstBytesWritten(qint64 len) {
if (m_dst->bytesToWrite() < m_buf.size() / 2) {
// the transmit buffer is running low, refill
send();
}
m_hasWritten += len;
emit progressed((m_hasWritten * 100) / m_srcSize);
signalDone();
}
Q_SLOT void dstError() {
emit errorOccurred(tr("Unable to send data. Probably the other side"
"cancelled or there are connection problems."));
qDebug() << m_dst->error();
}
void send() {
if (signalDone()) return;
qint64 read = m_src->read(m_buf.data(), m_buf.size());
if (read == -1) {
emit errorOccurred(tr("Error while reading file."));
return;
}
m_hasRead += read;
qint64 written = m_dst->write(m_buf.constData(), read);
if (written == -1) {
emit errorOccurred(tr("Unable to send data. Probably the other side "
"cancelled or there are connection problems."));
qDebug() << m_dst->error();
return;
}
if (written != read) {
emit errorOccurred(tr("Internal error while filling write buffer."));
qDebug() << m_dst->error();
return;
}
}
public:
/*! Requires a source device open for reading, and a destination socket open
for writing. */
Sender(QIODevice * src, QAbstractSocket * dst, QObject * parent = 0) :
QObject(parent), m_src(src), m_dst(dst), m_buf(8192, Qt::Uninitialized),
m_hasRead(0), m_hasWritten(0), m_doneSignaled(false)
{
Q_ASSERT(m_src->isReadable());
Q_ASSERT(m_dst->isWritable());
connect(m_dst, SIGNAL(bytesWritten(qint64)), SLOT(dstBytesWritten(qint64)));
connect(m_dst, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(dstError()));
m_srcSize = m_src->size();
}
Q_SLOT void start() { send(); }
Q_SIGNAL void done();
Q_SIGNAL void errorOccurred(const QString &);
Q_SIGNAL void progressed(int percent);
};

Related

How to make blocking tcp socket with Qt?

I work with QTcpSocket. I need any write/read calls to the socket to be synchronous (blocking).
I know there is waitForReadyRead() and waitForBytesWritten(), but those two methods are marked in Qt documentation as they can fail randomly under Windows. I cannot affort this.
The blocking read is the most important (as reading comes always after writting a command to the other peer, so I know that if data reaches the other peer, it will answer).
I have tried 2 approaches.
First:
QByteArray readBytes(qint64 count)
{
int sleepIterations = 0;
QByteArray resultBytes;
while (resultBytes.size() < count && sleepIterations < 100)
{
if (socket->bytesAvailable() == 0)
{
sleepIterations++;
QThread::msleep(100);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
continue;
}
resultBytes += socket->read(qMin(count, socket->bytesAvailable()));
}
return resultBytes;
}
This should wait for bytes to be available for reading on the socket, processing the event loop in the mean time, so the socket is doing it's necessary internal stuff.
Unfortunately - for unknown to me reason - the bytesAvailable() sometimes returns correct number of bytes, but sometimes it never returns anything greater than 0.
I know in fact that there was data to be read, because it used to work with the second approach (but it has it's own problems).
Second:
I have a kind of signal "blocker", which blocks current context and processes event loop, until certain signal is emitted. This is the "blocker":
SignalWait.h:
class SignalWait : public QObject
{
Q_OBJECT
public:
SignalWait(QObject *object, const char *signal);
bool wait(int msTimeout);
private:
bool called = false;
private slots:
void handleSignal();
};
SignalWait.cpp:
SignalWait::SignalWait(QObject* object, const char* signal) :
QObject()
{
connect(object, signal, this, SLOT(handleSignal()));
}
bool SignalWait::wait(int msTimeout)
{
QTime timer(0, 0, 0, msTimeout);
timer.start();
while (!called && timer.elapsed() < msTimeout)
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return called;
}
void SignalWait::handleSignal()
{
called = true;
}
and then I used it like this:
SignalWait signalWait(socket, SIGNAL(readyRead()));
// ...
// socket->write(...);
// ...
if (!signalWait.wait(30000))
{
// error
return;
}
bytes = socket->read(size);
This approach seems to be working better, but it also fails from time to time. I don't know why. It's like the readyRead() signal was never emitted and the SignalWait keeps waiting, until it times out.
I'm out of ideas. What is the proper way to deal with it?
I would suggest to use the asynchronous approach but if you really want to go with the synchronous way, then a better way is to use a local event loop:
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
loop.connect(socket, SIGNAL(readyRead()), SLOT(quit()));
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
while (resultBytes.size() < count)
{
timer.start(msTimeout);
loop.exec();
if(timer.isActive())
resultBytes += socket->read(qMin(count, socket->bytesAvailable()));
else
break;
}
Here it waits until count bytes are read or the the timeout reaches.

qt waitforreadyread() inconsistent?

Under Qt 4.7.1, OS X 10.6.8
(have to use this -- later versions
of Qt and/or OS X introduce severe
incompatibilities for my users)
The following works. Sometimes. Then sometimes not.
When it doesn't work, it returns "Unknown Error"
hst is good in all cases, qDebug returns same correct
data for hst every time.
The idea is, use ->get to pull a CGI URL; the CGI
returns some data, which I can ignore in this case.
Then I'm done.
hst is a well formed URL,
http://yadda.com/cgi-bin/whatever.py
QString hst;
QNetworkReply *qnr;
QNetworkAccessManager *qqnap = NULL;
qqnap = new(std::nothrow) QNetworkAccessManager(tmw);
if (qqnap != NULL)
{
hst = loaduphst(); // get qstring to send
qnr = qqnap->get(QNetworkRequest(QUrl(hst))); // report in and fetch update info
if (qnr->waitForReadyRead(3000) == FALSE)
{
qDebug() << "waitForReadyRead() returned FALSE -- error or timeout:" << qnr->errorString();
}
}
else
{
qDebug() << "qqnap is NULL";
}
yadda.com is up; the target script is dead simple
and works fine from browser or cmd line every time.
This is running within the context of
MainWindow::closeEvent(QCloseEvent *ce)
before I emit ce->accept() GUI is still up,
etc.
Hints? Tips? Abuse? Thanks!
waitForReadyRead is not implemented in QNetworkReply. The default implementation does nothing:
bool QIODevice::waitForReadyRead(int msecs)
{
Q_UNUSED(msecs);
return false;
}
Use the readyRead signal to find out when there is data available to be read.
More-or-less synchronous use of async networking is very problematic in the context of the main GUI loop. Signals that don't appear (finished OR readyRead), URLs that sometimes send and sometimes don't... and of course, as the kind person above pointed out, unimplemented functions. Zebras!
What we can do, though, is fire up an event loop and a timer on our own, and this will in a more-or-less friendly way act synchronous.
Perhaps some poor soul will need to poke a website CGI as I do; here's the code. It works. At least under Qt 4.7.1 it does!
So anyway, here it is:
QNetworkReply *qnr;
QNetworkAccessManager *qqnap;
QNetworkRequest qnwr;
QEventLoop w;
QTimer arf;
if ((qqnap = new(std::nothrow) QNetworkAccessManager(this)))
{
qnwr.setUrl(myUrl()); // Build web goodness
qnwr.setRawHeader("User-Agent", myUserAgent());
arf.setSingleShot(true);
if (connect(&arf, SIGNAL(timeout()), // timer firing blows...
&w, SLOT(quit()) // ...out event loop
) == FALSE)
{ return(BAD_CONNECT_TOUT); }
if (connect(qqnap, SIGNAL(finished(QNetworkReply*)), // notify we finished...
this, SLOT(qqnapReplyQ(QNetworkReply*)) // ...cuz I need to know
) == FALSE)
{ return(BAD_CONNECT_FINISHED_NOTIFY); }
if (connect(qqnap, SIGNAL(finished(QNetworkReply*)), // finishing blows out...
&w, SLOT(quit()) // ...event loop
) == FALSE)
{ return(BAD_CONNECT_FINISHED_ELOOP); }
if ((qnr = qqnap->get(qnwr))) // Go if qnr is good
{
arf.start(6000); // timeout in ms // Watchdog timer on
w.exec(); // handle all that
if (arf.isActive()) { arf.stop(); } // kill timer if needed
}
else { return(BAD_WWWGET); } // FAIL
}
else
{
return(BAD_NWAM); // FAIL
}
return(ZEN_NETWORKING);

Pausing execution until a specific message on a socket is received?

Is there some way to make a function pause it's execution until the socket receives a specific message? Using Signals + QEventLoop to wait doesn't work because while it can wait for signals, there isn't any way to get the data the signal emitted (or is there?).
You could connect to the following signal:
void QIODevice::readyRead() [signal]
Then, you would basically read the data and if it is the one you are looking for, you could set a boolean variable to true that is initially false. Your function would continue the execution only when the variable is true.
Make sure that the function paused is not sleeping in a sync manner too much, etc, without having a dedicated thread.
So, this would be one way of solving your task:
MySocketManager::MySocketManager(QObject *parent): QObject(parent)
{
...
connect(m_mySocket, SIGNAL(readyRead()), SLOT(handleReadyRead()));
...
}
void MySocketManager::handleReadyRead()
{
if (m_mySocket.readAll() == "myMessage")
continue = true;
}
...
void myFunction()
{
...
continue = false;
qDebug() << "Pause";
while (!continue) { ... }
qDebug() << "Continue";
...
}
This is a tad simplication of the issue, but since you have not shown much effort other than asking for solution, this should get you started.

Qt C++ Console Server, Wait for socket connection & accept input at same time?

I am writing a server as a Qt console application. I have the server set up to wait for a socket connection, but I also need to allow a user to input commands into the server for managing it. Both are working independently. However, the problem I ran into is that when I'm in a while loop accepting and processing input commands, the server doesn't accept connections.
I have a Socket class, and in its constructor, I have:
connect(server,SIGNAL(newConnection()),this, SLOT(newConnection()));
Right under that in the constructor, I call a function that has a more in-depth version of this for getting commands from the user:
QTextStream qin(stdin, QIODevice::ReadOnly);
QString usrCmd;
while(usrCmd != "exit" && usrCmd != "EXIT") {
//Get command input and process here
}
Inside newConnection(), I just accept the next connection and then use the socket.
QTcpSocket *serverSocket = server->nextPendingConnection();
How can I make it so the socket can wait for connections and wait for user-inputed commands at the same time?
Problem with your code is because you are blocking event loop with your while loop. So, the solution to your problem is to read from stdin asynchronously. On Linux (and on Mac, I guess), you can use QSocketNotifier to notify when the data is arrived on stdin, and to read it manually), as per various internet sources.
As I am using Windows, I would suggest you to do it in this way (which should work on all platforms):
Open the thread for reading data from stdin
Once you get some data (perhaps line?) you can use Qt signal-slot mechanism to pass the data to main thread for processing without blocking the event loop.
So, this is the pseudocode. MainAppClass should your existing server class, just edit the constructor to create new thread, and add new slot for processing the data.
class Reader: public QThread
{
Q_OBJECT
public:
Reader(QObject * parent = 0 ): QThread(parent){}
void run(void)
{
forever{
std::string data;
std::getline (std::cin, data);
if(data == "exit")
{
emit exitServer();
return;
}
emit dataReady(QString::fromStdString(data));
}
}
signals:
void dataReady(QString data);
void exitServer();
};
class MainAppClass: public QObject
{
Q_OBJECT
public:
MainAppClass()
{
Reader * tr = new Reader(this);
connect(tr, SIGNAL(dataReady(QString)), this, SLOT(processData(QString)));
connect(tr, SIGNAL(exitServer()), this, SLOT(exitServer()));
tr->start();
}
public slots:
void processData(QString data)
{
std::cout << "Command: " << data.toStdString() << std::endl;
}
void exitServer()
{
std::cout << "Exiting..." << std::endl;
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainAppClass myapp; //your server
app.exec();
return 0;
}
Since I wrote simple guidelines how to use QTcpSocket, here is the brief
When you get client QTcpSocket, connect readyRead() signal to some slot, and read data from sender() object. You don't need to read anything in the constructor.
For reading you can use standard QIODevice functions.
Note: this is pseudo code, and you may need to change few things (check the state of the stream on reading, save pointer to sockets in some list, subscribe to disconnected() signal, call listen() in constructor, check if QTcpServer is listening, etc).
So, you need to have slot onReadyRead() in your class which will have the following code:
void Server::readyReadSlot()
{
QTcpSocket *client = (QTcpSocket*)sender(); // get socket which emited the signal
while(client->canReadLine()) // read all lines!
// If there is not any lines received (you may not always receive
// whole line as TCP is stream based protocol),
// you will not leave data in the buffer for later processing.
{
QString line = client->readLine();
processLine(line); // or emit new signal if you like
}
}
Inside newConnection() you need to connect readyRead() signal with your slot.
void Server::newConnection()
{
QTcpSocket *clientSocket = server->nextPendingConnection();
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
}

Unable to read correctly from socket

I have a server application which sends some xor encrypted strings. I am reading them from my QT client application. Sometimes, the server is slower and I am not able to receive the entire string. I have tried something like below but it gets stuck ( see the comment below). How can I wait until I have the entire data. I tried bytesAviable() but then again i get stuck (infinite loop)
QTcpSocket * sock = static_cast<QTcpSocket*>(this->sender());
if (key == 0)
{
QString recv(sock->readLine());
key = recv.toInt();
qDebug() << "Cheia este " << key;
char * response = enc_dec("#AUTH|admin|admin",strlen("#AUTH|admin|admin"),key);
sock->write(response);
}
else
{
busy = true;
while (sock->bytesAvailable() > 0)
{
unsigned short word;
sock->read((char*)(&word),2);
qDebug()<<word;
//Sleep(100); if i do this than it works great!
QByteArray bts = sock->read(word);
while (bts.length() < word)
{
char bit; //here get's stuck
if (sock->read(&bit,1) > 0)
bts.append(bit);
sock->flush();
}
char * decodat = enc_dec((char*)bts.data(),bts.length() - 2,key);
qDebug() << decodat;
}
}
I don't know what the meaning of key == 0 is, but you are almost certainly misusing available(), like almost everybody else who has ever called it, including me. It tells you how much data can be read without blocking. It has nothing to do with how much data may eventually be delivered down the connection, and the reason is that there are TCP APIs that can tell you the former, but not the latter. Indeed the latter doesn't have any real meaning, considering that the peer could keep writing from now until Doomsday. You should just block and loop until you have read the amount of data you need for the next piece of work.
I offer you to do the following:
QObject::connect(this->m_TCPSocket, SIGNAL(readyRead()), this, SLOT(processRecivedDatagrams()));
Some explanation:
It is convinient to create a class instance of which will manage network;
One has the member which is pointer on TCPSocket;
In constructor implement connection of signal from socket readyRead() which is emmited when needed data was delivered with SLOT(processRecivedDatagrams()). which is responsible for processing recived datagrams/ in this case it is processRecivedDatagrams(), also implement this slot
Mind that class which manages network has to inherit from QObject and also in its declaration include macrosQ_OBject` for MOC.
update:
i also offer you to store recived data in container like stack or queue this will allow you to synhronize sender and reciver (container in this case acts like buffer)
// SLOT:
void Network::processRecivedDatagrams(void)
{
if (!this->m_flagLocked) // use analog of mutex
{
this->m_flagLocked = true; // lock resource
QByteArray datagram;
do
{
datagram.resize(m_TCPSocket->pendingDatagramSize());
m_TCPSocket->readDatagram(datagram.data(), datagram.size());
}
Qt::String YourString; // actualy I don`t remember how to declare Qt string
while (m_TCPSocket->hasPendingDatagrams());
QDataStream in (&datagram, QIODevice::ReadOnly);
in >> YourString
--numberOfDatagrams;
}
this->m_flagLocked = false; // unlock resource
}
}