7

Writing a cross platform app using Qt (including Windows with MinGW). For reading data from SSL socket, I am creating a separate thread. This thread is there for historical reason, because earlier the app was written using C socket/ssl/crypto libraries. Now all these are being replaced with Qt Network library.

For blocking thread, waitForReadyRead(milliseconds) seems a better choice. Now according to Qt hierarchy:

QIODevice
   |
QAbstractSocket
   |
QTcpSocket
   |
QSslSocket

Documentation of QAbscractSocket::waitForReadyRead() suggests:

Note: This function may fail randomly on Windows. Consider using the event loop and the readyRead() signal if your software will run on Windows.

But the similar warning is Not mentioned in the QIODevice::waitForReadyRead().

Question: Is QSslSocket::waitForReadyRead() consistently usable for all the platforms?


Why am I not using readyRead() signal?
For some strange reason, if I slot some method with readyRead() then it's not getting called. Moreover, the QSslSocket::write() also doesn't work, which works otherwise with above approach. Due to complexity of my code, I am unable to present it here.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • You state that you're using threads. If the `readyRead` emitter and receiver are on different threads are you certain the receiver's thread has an active event loop? As an aside, I can't help feeling that using `waitForReadyRead` is only going to temporarily mask the symptoms of the real underlying problem. – G.M. Jun 14 '17 at 12:35
  • @G.M. initially There is only 1 thread which makes a TLS connection & exchange couple of custom messages with server (read & write). Only if this suceeds, I start a new loop for exclusive read purpose. For `readyRead()`, I am certain that there has to be coding/understanding mistake from my side to not let it work. However if I have to use `waitFor...()` methods then my current design works on its own. The only worry is it's Windows implementation. Another smaller issue is that, sometimes Qt `readyRead()` is emitted after multiple consecutive data messages. `socket-select()` was quicker here. – iammilind Jun 14 '17 at 13:27

4 Answers4

10

To your question: yes you can use QSslSocket::waitForReadyRead() but on Widows it can timeout even when the data came to the socket. So if timeout occurs you have to check if it is timeout or the method failed. The check is simple just if QAbstractSocket::bytesAvailable() > 0 then data are ready for read otherwise it's timeout.

This approach is ok when you use small timeout and your application isn't sensitive on delay (e.g. communication between temperature sensor and cloud with temperature history). But if any unnecessary delay is not acceptable for you then you should use the signal/slot interface.

For more information you can look at the bug report on the Qt's bug tracker.

Qeek
  • 1,910
  • 22
  • 28
1

according to your question. The implementation of QIODevice does nothing but return false. So there is no need for the hint of sometimes failing. The implementation of QAbstractSocket calls something called "nativeSelect" internally, which is then directed to the corresponding method, depending on the OS you are running on. For Windows the select implementation sometimes seem to return a negative false. But this should not harm you, because you should get the hint for available data from your next call to waitForReadyRead(). QSslSocket's waitForReadyRead() internaly uses QAbstactSocket's implementation appart from some SSL checks.

Regarding your problem with the signals and slots. A mistake, I made when I was new to Qt was, that I tried to signals before I started a MainLoop by calling QApplication::exec() or something else. The signal slot mechanism does not work without a run loop.

Hope you can get some hints from this.

Regards

M0rk
  • 56
  • 1
1

Problem might be use resources.

When you will use waitForReady* you creating constraint one socket per one thread (otherwise you will have strange bugs). Now question is how many sockets do you have? If it depends it on run time data, you may do not know that. Some embedded systems have limit on number of treads what can impact your application and IMO this is only limit which can impact such implementation.

This part of your question:

Why am I not using readyRead() signal? For some strange reason, if I slot some method with readyRead() then it's not getting called. Moreover, the QSslSocket::write() also doesn't work, which works otherwise with above approach. Due to complexity of my code, I am unable to present it here.

Looks suspicious.
I never seen someone had similar problem. Maybe some parts of your code is blocking an event loop?

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • I have edited that suspicious part as it was my coding mistake. Currently I am using only 1 socket. May be the Qn is now more specific to the `wait..()` method. – iammilind Jun 26 '17 at 03:16
0

Though it's not an exact answer to the Qn, I am posting a possible implementation of waitForReadyRead(), which can be used under a local event loop.

class MySslSocket : QSslSocket
{
  Q_OBJECT 
public:
  virtual
  bool
  waitForReadyRead (int milliseconds) override final
  {
    QEventLoop eventLoop;
    QTimer timer;
    connect(this, SIGNAL(readyRead()), &eventLoop, SLOT(quit()));
    connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
    timer.setSingleShot(true);
    timer.start(milliseconds);

    eventLoop.exec();
    return timer.isActive();
  }
};

This can be either use exclusively for Windows, or it can be used in general for all platforms.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • This is certainly not a good solution to go for. You can refer to this [question](https://stackoverflow.com/q/35561182/2666212) for more details. – Mike Jun 23 '17 at 15:14