How to deal with signals emitted before slots connected - c++

I have a somewhat more complicated version of the following code:
void Foo::makeConnection(...) {
QTcpSocket * socket = new QTcpSocket(this);
// ...
socket->disconnect(this);
emit connectionAppeared(socket);
}
void Bar::baz() {
// ...
connect(foo, SIGNAL(connectionAppeared(QTcpSocket*)), this, SLOT(onConnectionAppeared(QTcpSocket*)));
foo->makeConnection(...);
}
void Bar::onConnectionAppeared(QTcpSocket * socket) {
if (!socket) { std::terminate(); }
socket->setParent(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(readData()));
connect(socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
}
The Bar::baz asks Foo to make it a connected QTcpSocket, and after it is made, the socket's signals are connected to Bar's slots. But there is a problem with timing: the readyRead signal may very well be emitted befor it is connected to any slots, and thus is effectively lost. We "fixed" it like this:
void Bar::onConnectionAppeared(QTcpSocket * socket) {
// ... same code as before plus the following line:
emit socket->readyRead(); // Kick it to get started!
}
void Bar::readData() {
while (mSocket->bytesAvailable()) {
// ...
}
}
The problem of losing disconnected() signals is still there, but it doesn't manifests very often. Technically, I could emit disconnected() manually, and check socket->state() in the socketClosed() slot... but I just feel it's not the right way. I effectively just manually examine the current state instead of reacting to state transitions which I was the whole point of signal/slot machinery.
Are there nice ways to fix this kludge? I thought of passing signal/slot pairs to Foo::makeConnection(...) somehow, so they can be connected before socket->connectToHost() is called, but I don't know how, and I am not sure it's the good idea anyway.

Related

QT client doesn't download file when action is placed on button

Code in main.cpp
Client c;
c.start("ip-address", port, "final folder/file");
It works and client downloads file I need.
But if I place same code in mainwindow.cpp on button click action
void MainWindow::on_btn_connect_clicked()
{
Client c;
c.start("ip-address", port, "final folder/file");
}
it doesn't work. Why is that? I'm newbie at Qt and network coding. Some source code
void Client::start(QString address, qint16 port, QString file)
{
qDebug() << "client started";
QHostAddress addr(address);
filename = file;
client->connectToHost(addr, port);
qDebug() << client->socketDescriptor();
}
I have readyRead() but programm doesn't get there.
Client::Client(QObject *parent) :
QObject(parent)
{
client = new QTcpSocket(this);
client->abort();
connect(client, SIGNAL(readyRead()), this, SLOT(ReadData()));
connect(client, SIGNAL(disconnected()), this, SLOT(Completed()));
}
Did you connect the buttons clicked signal to the on_btn_connect slot?
When you create the Client like this:
void MainWindow::on_btn_connect_clicked()
{
Client c;
c.start("ip-address", port, "final folder/file");
}
The client object is created, start is called, then it is immediately destroyed one execution leaves the on_btn_conenct_clicked() function. This will never work unless the "start" function is completely finished doing whatever you want it to do by the time you leave it, which does not appear to be the case by the code you posted.
For this to work, you'd have to make client a member of the MainWindow class so it sticks around after the slot is called. Another alternative would be to make it a pointer, create it in the on_btn_connect_clicked() slot, then connect whatever signal gets emitted when its all done with its work to its own deleteLater slot. Like so:
void MainWindow::on_btn_connect_clicked()
{
Client* c = new Client(this);
connect(c, &Client::downloadComplete, c, &Client::deleteLater);
c->start("ip-address", port, "final folder/file");
}
The downloadComplete signal is something that you have to make sure you emit yourself whenever the action you are trying to accomplish is completed, otherwise you'll have a memory leak and the client object will not get deleted until the entire MainWindow is deleted.

How to properly create QUdpSocket on non-gui thread ? Readyread not emitted

I'm writing a network library that wraps the QUdpSocket:
QAbstractSocket *UdpNetworkStreamer::addConnection()
{
QUdpSocket *udpSocket = new QUdpSocket(this);
udpSocket->bind(connection.port, QUdpSocket::ShareAddress);
bool ret = udpSocket->joinMulticastGroup(QHostAddress(connection.ip));
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::QueuedConnection);
return udpSocket;
}
create a new QUdpSocket.
connect to its readyRead signal.
call readDatagram when readyRead is raised.
All is working fine when I use the library from a Qt GUI application.
The problem starts when another user includes the library used outside of a Qt GUI application.
He calls the addConnection (which creates the socket and calls connect on the readyRead)
The thread on which the addConnection is called is non-Qt.
The addConnection seems to end successfully but the readyRead is never emitted.
Calling read (even though no readyRead was emitted) leads to a successful datagram read.
Fixes that did not work :
moving the the UDP socket thread to the this->thread
QUdpSocket *udpSocket = new QUdpSocket();
udpSocket->moveToThread(this->thread());
udpSocket->setParent(this);
I tried to simulate the problem by calling:void
MainWindow::on__btnOpenMulticastReceiver_clicked()
{
QFuture<void> future = QtConcurrent::run(this,
&MainWindow::CreateMulticastConnection, testHandle);
}
This also led to same symptoms as the one the user had with my library, meaning the readyRead wasn't emitted.
QSignalSpy - I've activated a spy on the readyRead signal; the counter kept on being zero although I could read data directly from the socket. The spy gave valid results (i.e. progressed) when used the socket was initialized on the main thread.
My Questions:
What am I missing and doing wrong ?
What is the simplest way of having the readyRead emitted even though it is not created on the main GUI thread - I couldn't find any sample that works with no GUI or outside Qt threads.
I ended up solving the problem this way :
void MainWindow::OpenConnection()
{
QThread *t = new QThread();
t->start();
SocketWrapper *w= new SocketWrapper();
w->moveToThread(t);
w->metaObject()->invokeMethod(w, "CreateSocket", Qt::QueuedConnection);
}
You must call invokeMethod() with the thread the socket wrapper was movedTo() upon creation of the socket, so that the thread that creates the socket will have a running event loop.
In addition to that, the CreateSocket() needs to be a slot in the SocketWrapper, something like that :
class SocketWrapper : public QObject
{
Q_OBJECT
public:
explicit SocketWrapper(QObject *parent = 0);
signals:
public slots:
void readyRead();
void CreateSocket();
private:
QUdpSocket *_socket;
};
My guess is that you need to add Q_OBJECT macro to the beginning of the class from where you need to emit the signal. Unless you do this, the signal-slot mechanism will not function properly.

Why would deleting a Qt (QSslSocket) object cause a crash

I am really stumped and hoping somebody out there knows something about my problem.
I have a very simple SSL client and server. The connection is fine. Communication is fine. The problem arises when the client disconnects from the server. This fires a signal on the server which is handled in the SLOT error_handler(QAbstractSocket::SocketError in_error). In that function is where the sslSocket object has to be deleted, I would imagine.
However doing this causes the server to seg fault. I don’t understand what’s going on. I expected this to be really straightforward but apparently I am missing some Qt (or other) concept.
Can anybody help out?
Essential server code:
void SSLServer::incomingConnection(int sd)
{
sslSocket = new SSLSocket(this);
if( sslSocket->setSocketDescriptor(sd))
{
QFile sslkeyfile(privKey_);
sslSocket->setPrivateKey(QSslKey(sslkeyfile.readAll(),QSsl::Rsa));
QFile cliCertFile(serverCert_);
sslSocket->setLocalCertificate(QSslCertificate(cliCertFile.readAll()));
QFile certFile(caCert_);
sslSocket->addCaCertificate(QSslCertificate(certFile.readAll()));
sslSocket->setPeerVerifyMode(QSslSocket::VerifyPeer);
sslSocket->setProtocol(QSsl::SslV3);
connect(sslSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(error_handler(QAbstractSocket::SocketError)));
connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(ssl_error_handler(QList<QSslError>)));
connect(sslSocket, SIGNAL(encrypted()), this,
SLOT(ready()));
connect(sslSocket, SIGNAL(readyRead()), this,
SLOT(read_data_from_client()));
sslSocket->startServerEncryption();
if(!sslSocket->waitForEncrypted())
{
qDebug() << "failed to perform SSL handshake with client";
return;
}
}
}
void SSLServer::read_data_from_client()
{
QByteArray qstrbytes = sslSocket->readAll();
qDebug() << Q_FUNC_INFO << qstrbytes;
}
void SSLServer::ready()
{
QSslCertificate clientCert = sslSocket->peerCertificate();
qDebug() << clientCert.isValid();
}
void SSLServer::error_handler(QAbstractSocket::SocketError in_error)
{
qDebug() << Q_FUNC_INFO << in_error;
if(in_error == QAbstractSocket::RemoteHostClosedError)
{
delete sslSocket; //// line causes crash !!!!!!
}
}
Use QObject::deleteLater() instead of delete since QSslSocket inherits QObject. You may still receive messages on the socket which is causing the crash when you just delete the object.
sslSocket->deleteLater();
When you call deleteLater(), Qt automatically disconnects all slots and signals and calls the object destructor after there are no pending events being delivered to the object. See QObject::~QObject() for more information.
If you think how a QObject class such as SSLSocket class may be written, it could be something like this:-
class SSLSocket : public QObject
{
signals:
void sslErrors(QList<QSslError>);
void SomeFunction()
{
// something went wrong, emit error
emit sslErrors(errorList);
Cleanup(); // If a slot connected to sslErrors deleted this, what happens now?!
}
}
When the signal sslErrors is triggered, your slot function is called. As you can see, after emitting the signal, the class may have more work to do. If you immediately delete the object in your slot, this is going to crash, which is why you should always use deleteLater() for deleting QObject instances in slot functions.
The deleteLater function will ensure that the slot function has finished executing and the call stack restored, so it will be deleted at the appropriate time.
Note that the code above is not actually what SSLSocket does, but just an example.
QSslSocket is a QObject. Never just delete a QObject. For sure don't do this in a slot. Always use deleteLater().
Here's Qt example code using QSslSocket:
http://qt-project.org/doc/qt-4.8/network-securesocketclient-sslclient-cpp.html
As the other posters mentioned, use deleteLater(), and the error notification is not the only place to do so.

Wait for a SLOT to finish

I use QNetworkAccessManager to do form POST.
I have connected signals and slots as:
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(readCookies(QNetworkReply*)));
Now, I make a request by doing:
manager->post(request,postData);
Now readCookies(QNetworkReply *) will be run as soon as SIGNAL is emitted. Now, using the Cookies which I get in this slot, I have to make one more POST..
As signals & slots are asynchronous, I want to wait till I get the cookies from my first POST and then I again want to do another post using the cookies I got in first POST like
//Setting new request, headers etc...
manager->post(request2,postData2);
I want the later to always be executed after first one has executed (so that I get proper cookies value).
What is the way to go? I am new to all these SIGNALS & SLOTS so please bear with me.
You can do the post in your readCookies() slot:
void readCookies( QNetworkReply* reply ) {
if ( ...error? ) {
report error...
return;
}
...
manager->post(request2,postData2);
}
I will be called when the cookies is read, and you can then continue with your post. Connect that to a second slot, and so on.
Managing multiple, possibly parallely running asynchronous operations like this can become errorprone though, if you manage many of them in a single object. I would suggest to use the Command Pattern - here I described why I find it extremely useful in exactly this context. The sequence of request and asnychronous operations is encapsulated in a single object (abbreviated, with some pseudo-code):
class PostStuffOperation : public QObject {
Q_OBJECT
public:
enum Error {
NoError=0,
Error=1,
...
};
Error error() const; //operation successful or not?
QString errorString() const; //human-readable error description
... setters for all the information the operation needs
...
void start() {
...start your first request and connect it to cookiesRead
}
public Q_SLOTS:
void cookiesRead( QNetworkReply * ) {
if ( error ) {
// set error and errorString...
emit finished( this ); //couldn't read cookies, so the operation fails
return;
}
... do post
}
void postFinished( QNetworkReply* ) {
if ( error ) {
// set error and errorString...
}
emit finished( this ); //post finished - that means the whole operation finished
}
Q_SIGNALS:
void finished( PostStuffOperation* );
};
To start the operation, you do
PostStuffOperation op* = new PostStuffOperation( this );
... pass data like server, port etc. to the operation
connect( op, SIGNAL(finished()), this, SLOT(postOperationFinished()) );
op->start();
void postOperationFinished( PostStuffOperation* op ) {
if ( op->error != PostStuffOperation::NoError ) {
//handle error, e.g. show message box
}
}
It makes sense to have a common baseclass for such operations, see e.g. KDE's KJob.
You can connect a signal from this to a slot from your manager and emit the signal after reading the cookies. By example:
connect(this, SIGNAL(cookiesRead()), manager, SLOT(PostAgain());
So your readCookies function will be:
{
// Read cookies
emit cookiesRead();
}
Of course you can send all data you want form signal to slot.
Hope that helps
You can send a second signal connected to another slot (the resend slot), if you have finished the evaluation of your first cookie. You can do that directly in the slot. You can also call slots like a normal member function.

Binding arguments to signals/slots

I basically have multiple events signals which I want to connect to the same slot. What I want to know is how can I pass string based parameters to that same slot so that the slot knows which is this signal coming from. One alternative is to make as many slots as there are signals and then connect them in a 1:1 manner, but this is efficient, considering that the code for all the processing is very similar. I tried doing this but I'm getting some errors:
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString)));
connect(button1,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button1")));
connect(button2,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button2")));
The error is related to the parameters I'm passing in the last 2 commands .. And backgroundTypeChoiceMade is declared like this:
void backgroundTypeChoiceMade(QString);
Can someone tell me what the error is in the above code ?
You can use QSignalMapper. Although the QSignalMapper is the answer to your question, I think jon hanson's answer is the way you should take. You get much more cleaner code that way.
Four methods. One doesn't suck.
QSignalMapper. Works, but makes for messy code.
Named slots. Messy for any significant number of senders, and doesn't work for dynamically-generated senders (e.g., buttons in a list).
sender()-compare. Can handle dynamic senders, but is still kinda ugly.
Subclass the sender. Doesn't suck. Gives you what you really wanted all along: parameterized signals.
Especially when you're using a small number of signals and sender types and when the senders are dynamically generated, subclassing the sender is the cleanest way. This lets you overload the existing signals to contain whatever parameters you need.
And now, wiring up the signals and slots just works:
Keypad::Keypad(QWidget *parent) : QWidget(parent)
{
for (int i = 0; i < 10; ++i)
{
// KeypadButton keeps track of the identifier you give it
buttons[i] = new KeypadButton(i, this);
// And passes it as a signal parameter. Booyah.
connect(buttons[i], SIGNAL(clicked(int)), this, SIGNAL(digitClicked(int)));
}
createLayout();
}
void Keypad::digitClicked(int digit)
{
// The slot can find the clicked button with ease:
dial(button[i]); // or whatever
//...
}
and the extra code is out-of-sight in a subclass you'll never have to touch again.
See http://doc.qt.digia.com/qq/qq10-signalmapper.html#thesubclassapproach for an example implementation of subclassing QPushButton to emit clicked(int) signals. Also discusses all four methods - named slots ("the trivial solution"), sender(), subclassing, and signal mapper.
Caveat: Obviously works best for small numbers of sender types. But that's usually the case. And in that case, it's worth it.
What is inefficient about using separate slots? If there's commonality in the slot handlers then move that into a function, e.g. extending ereOn's example:
void YourClass::YourClass() :
m_button1(new QPushButton()),
m_button2(new QPushButton())
{
connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot1()));
connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot2()));
}
void YourClass::common(int n)
{
}
void YourClass::yourSlot1()
{
common (1);
}
void YourClass::yourSlot2()
{
common (2);
}
You can't pass constants to connect() because the effective parameters are deduced at execution time, not compile time.
However, while this is against the OO principle, you can use QObject::sender() which gives a pointer to the emitter QObject.
Example below:
void YourClass::YourClass() :
m_button1(new QPushButton()),
m_button2(new QPushButton())
{
connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot()));
connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot()));
}
void YourClass::yourSlot()
{
if ((QPushButton* button = dynamic_cast<QPushButton*>(sender()))
{
// Now button points to a QPushButton* that you can compare with the pointers you already have
if (button == m_button1)
{
// Whatever
} else
if (button == m_button2)
{
// Whatever
}
}
}
If you have many buttons, you may also use a QSignalMapper by providing an identifier for each button.
You can now really bind a value when connecting. Qt5 added support for that.
Example:
connect(sender, &Sender::valueChanged,
tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1));
See more info.
NB: you can of course use std::bind or boost::bind instead of tr1::bind.
If you really don't want to use QSignalMapper, you could do something like this:
class SignalForwarderWithString: public QObject
{
Q_OBJECT
public:
SignalForwarderWithString(QString data = "", QObject *parent = 0) : QObject(parent), _data(data) {}
QString _data;
signals:
void forward(QString);
public slots:
void receive() { emit forward(_data); }
};
...
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString)));
SignalForwarderWithString *sfws;
sfws = new SignalForwarderWithString("button1", this);
connect(button1,SIGNAL(clicked()), sfws, SLOT(receive(QString)));
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString)));
sfws = new SignalForwarderWithString("button2", this);
connect(button2,SIGNAL(clicked()), sfws, SLOT(receive(QString)));
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString)));
but QSignalMapper is just as easy...
QSignalMapper *mapper = new QSignalMapper(this);
connect(button1, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(button1, "button 1");
connect(button2, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(button2, "button 2");
// you might have to tweak the argument type for your slot...
connect(mapper, SIGNAL(mapped(const QString &), this, SLOT(backgroundTypeChoiceMade(QString)));