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.
Related
I am trying to reuse a library as a module in Qt, therefore, I want be make less modifications/patches to it as possible. The goal is to be notified by the Q_EMIT that is emitted by a class member. My example code is as follows:
myclass.cpp
public:
MyClass::MyClass(QObject* parent) : QObject(parent)
{
this->manager = new QOfonoManager(this);
}
public slots:
void MyClass::manager_available()
{
qDebug() << "Manager available";
QStringList modems = this->manager->modems();
qDebug() << "Modems:" << modems << "-" ;
}
public:
void MyClass::test()
{
QStringList modems = this->manager->modems(); //Starts getting available modems
connect (this->manager,SIGNAL(availableChanged()),this,SLOT(manager_available()));
}
qofonomanager.cpp
void QOfonoManager::onGetModemsFinished(QDBusPendingCallWatcher* watcher)
{
......
Q_EMIT availableChanged(true);
}
QOfonoManager::QOfonoManager(QObject *parent) :
QObject(parent),
d_ptr(new Private)
{
.....
}
When I call MyClass::test, I expect the member class to signal availableChanged to MyClass::available_changed. What am I missing here, can I make use of the Q_EMIT availableChanged(true) without having to modify QOfonoManager code.
Any help is greately appreciated.
I think the underlying issue is that your connect statement doesn't match the signal spec for QOfonoManager::availableChanged. The signal is emitted with a bool parameter...
Q_EMIT availableChanged(true);
But your connect statement states it to be a signal with no parameters...
connect(this->manager, SIGNAL(availableChanged()), this, SLOT(manager_available()));
(Do you not get an error message at the console? Something along the lines of "QObject::connect: No such signal ..." ?)
Changing the connect statement to the following should help to fix the problem...
connect(this->manager, SIGNAL(availableChanged(bool)), this, SLOT(manager_available()));
Or, better still, use the new signal/slot syntax if you're using Qt5...
connect(this->manager, &QOfonoManager::availableChanged, this, &MyClass::manager_available);
In addition to the above you should heed the advice of #scopchanov & #ixSci regarding the placement of your connect statements. It's vital that connections are established before signals are emitted otherwise the notifications will be missed.
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.
Hi there i got problem with signal and slots in qt.
In main i have created object of mainwindow.
in mainwindow.cpp i creating object of another class(modbus_tcp).
i also creating connection here
void MainWindow::on_ConnectB_clicked()
{
modbus_tcp appts;
appts.slave();
connect(&appts,SIGNAL(msgSended(QString)),this,SLOT(msgEdit(QString)));
}
between slot declared in mainwindow.cpp/h
public slots:
void msgEdit(QString m);
void MainWindow::msgEdit(QString m)
{
ui->sendEdit->setText(m);
ui->recvEdit->setText(m);
//QMessageBox::information(0,"bad", "nope nope nope");
}
and signal declared in modbus_tcp.h
signals:
void msgSended(QString);
next i emiting signal in modbus_tcp.cpp
emit msgSended("asdasd");
and nothing happen
when i trying to emit in mainwindow.cpp its working
any ideas ?
void MainWindow::on_ConnectB_clicked()
{
modbus_tcp appts;
appts.slave();
connect(&appts,SIGNAL(msgSended(QString)),this,SLOT(msgEdit(QString)));
}
appts was created in stack, so it will be deleted at the end of slot execution. Try to create it in the heap(try to use pointer).
void MainWindow::on_ConnectB_clicked()
{
modbus_tcp *appts = new modbus_tcp;
connect(appts,SIGNAL(msgSended(QString)),this,SLOT(msgEdit(QString)));//first!
appts->slave();//now you can call it
}
Use pointers, but first of all connect, and after this call slave. You emit signal in slave, but there is no connection in this time. You should do connection firstly and after that, you will be able to catch signals.
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.
I'm trying to move a connection handler (containing a QTCPSocket object) to a separate thread from the QMainWindow object.
So far it works, but if I close the QMainWindow, It crashes and I receive the following:
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 1680d0. Receiver '' (of type 'QAbstractSocket') was created in thread 34fe0c"
Here is my code:
in the QMainWindow constructor (subclassed):
QThread m_commsThread;
m_pICommsHandler = new CommsHandlerIP();
m_pICommsHandler->moveToThread(&m_commsThread);
// when it starts, we start the connection
connect(&m_commsThread,SIGNAL(started()),m_pICommsHandler,SLOT(connectToHost()));
m_commsThread.start();
in the QMainWindow destructor:
m_commsThread.quit();
m_commsThread.wait();
delete m_pICommsHandler;
About CommsHandlerIP.cpp
CommsHandlerIP::CommsHandlerIP() {
m_TCPSocket = new QTcpSocket(this);
}
CommsHandlerIP::~CommsHandlerIP(void)
{
m_TCPSocket->deleteLater(); // It's going to crash a bit later if I have this line
}
m_TCPSocket should be in the right thread to be deleted but I feel like by closing the main thread, the affinity has become messy.
What should I do?
I think there might be two things happening:
As far as i remember, there were always some issues with Qt4 when calling moveToThread() on a QAbstractSocket.
When you call deleteLater() on your socket, the thread in which it lives is already stopped.
A possible solution could be to subclass your CommsHandlerIP from QThread and override run():
CommsHandlerIP::CommsHandlerIP() :
QThread(),
m_TCPSocket(0)
{
moveToThread(this);
}
void CommsHandlerIP::run()
{
m_TCPSocket = new QTcpSocket(this);
connectToHost();
exec();
m_TCPSocket->close();
delete m_TCPSocket;
}