QLocalSocket - QTimer and Lambda - qt

I have a strange behavior with Lambda and timer on Qt 5.7.1. Probably a mistake from me.
I start a connection with a socket and set a timer to check whether it was connected or not after a certain amount of time.
The signal connected of the socket will stop the time.
However, with the following implementation, the timer does not stop even if the connected signal is called.
constructor:
m_connectTimeout.setInterval(5000);
connect(&m_socket, &QLocalSocket::connected, [&]()
{
// this is called first and should stop the timer.
m_connectTimeout.stop();
});
connect(&m_connectTimeout, &QTimer::timeout, [&](){
// this is still called
});
Here is a minimum example with problem reproducible on Qt5.7.1 and Windows 10.
#include <QtCore>
#include <QtNetwork>
#define PIPENAME "testbug"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer timer;
QLocalSocket socketClient, *socketServer;
QLocalServer server;
timer.setInterval(2000);
QObject::connect(&timer, &QTimer::timeout, [&]
{
qDebug() << "client connection timed out";
timer.stop();
});
QObject::connect(&socketClient, &QLocalSocket::connected, [&]
{
qDebug() << "client connected";
timer.stop();
});
QObject::connect(&server, &QLocalServer::newConnection, [&]
{
qDebug() << "server got connection";
socketServer = server.nextPendingConnection();
});
server.setSocketOptions(QLocalServer::WorldAccessOption);
server.listen(PIPENAME);
qDebug() << "client connecting. . .";
socketClient.connectToServer(PIPENAME, QLocalSocket::ReadWrite);
timer.start();
return a.exec();
}
Output of the program:
client connecting. . .
client connected
server got connection
client connection timed out
I also noticed it's not always reproducible and seems somehow random.

Actually it seems the code works, it's just that the connection is so fast, that the timer.stop is called before the timer.start.
Starting the timer before calling connect to server seems to solve the issue
m_timer.start();
m_socketClient.connectToServer(PIPENAME, QLocalSocket::ReadWrite);
This would mean that connectToServer does some calls on the event loop in the background, allowing the slots to be called, even before the next line is executed.

Related

Make Peer-to-Peer communication on Qt Remote Objects

I want to make simple communication example on Qt Remote Objects. I want to make the communication peer-to-peer, therefore I'm trying to merge both Source and Replica of the same remote object functionality in one application (REPC_MERGED tool used to generate Source and Replica base classes).
#include <QtCore/QCoreApplication>
#include "MyPeerHost.h"
#include "Client.h"
#include <QDebug>
static QString peer_node_name(int number)
{
QString ret = QString("peer_%1").arg(number);
return ret;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyPeerHost peerHost; // just inherits auto-generated MyPeerSimpleSource
QUrl thisAddress = "local:" + peer_node_name(0);
QRemoteObjectHost sourceNode(thisAddress);
if(sourceNode.enableRemoting(&peerHost))
{
qInfo() << "Source remoting enabled successfully" << thisAddress;
QUrl remoteAddress = "local:" + peer_node_name(1);
QSharedPointer<MyPeerReplica> replica;
QRemoteObjectNode replicaNode;
if(replicaNode.connectToNode(remoteAddress))
{
qInfo() << "Replica connected to the address" << remoteAddress << "successfully";
replica.reset(replicaNode.acquire<MyPeerReplica>());
QString sourceClassName = peerHost.staticMetaObject.className();
qDebug() << "Replica wait for Source" << sourceClassName << "...";
if(replica->waitForSource(1000))
{
qInfo() << "Replica object completely initialized";
Client client;
client.setReplicaObject(replica);
client.sendMessage("AAA");
}
else
{
qCritical() << "Replica wait for Source" << sourceClassName << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Replica connect to the address" << remoteAddress << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Source remoting enable FAILED" << sourceNode.lastError();
}
return a.exec();
}
Application output:
Source remoting enabled successfully QUrl("local:peer_0")
Replica connected to the address QUrl("local:peer_1") successfully
Replica wait for Source "MyPeerHost" ...
Replica wait for Source "MyPeerHost" FAILED QRemoteObjectNode::NoError
As you see, replicaNode successfully connected to the non-existent node QUrl("local:peer_1").
What I am doing wrong?
You don't have valid Qt code.
Qt relies on an event loop to handle asynchronous behavior, which is started by the a.exec() at the end of your main() routine. Qt Remote Objects, in turn, relies on the event loop for all of its communication.
In your code, you create your objects on the stack, but in code blocks that go out of scope before you start the event loop. They will therefore be destructed before the event loop is kicked off.
I'd recommend starting with some of the examples, make sure they work, then grow what you are trying to do from there.

QEventLoop wait for only local events not main loop events

I'm trying to execute a network request and wait for the response before moving on. Whereas using a QEventLoop works to wait for the request, the problem is if there are other signals fired in other parts of the program they will execute first.
Here is an example of the code re-producing the problem. You can see the network request (I'm using http://example.com here just for example) is clearly going to be issued before the second singleShot timer will fire.
#include <QApplication>
#include <QTimer>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
void longFunction()
{
qDebug() << "Starting Long Function";
QEventLoop localEventLoop;
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("http://example.com")));
QObject::connect(reply, &QNetworkReply::finished, &localEventLoop, &QEventLoop::quit);
// I wish to block here
// - do not want to pass this point and run
// other events if fired from the main event loop
localEventLoop.exec();
qDebug() << "Finishing Long Function";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer::singleShot(0, &longFunction);
QTimer::singleShot(1, []() {
qDebug() << " -- Some other function that was issued after";
qDebug() << " -- 'longFunction' started but before 'longFunction' ended";
});
return a.exec();
}
Here is the output:
Here is the desired output:
Edit
To add some more info. The code above is obviously an over simplification of the problem as I can't easily post all of the code. Basically whats happening is there is code somewhere else in my project that will emit a signal that is connected to the longFunction() above. Obviously I want to wait for a response from the REST call before continuing because the response will dictate the elements later in that function. The issue is that at some point later (potentially before the REST call finishes), some other parts of my code might trigger some other signal and I don't want those other signals to be processed. I just want to wait for the ONE connection on the localEventLoop to be processed.
Any help would be greatly appreciated
QTimer::singleShot(0, &longFunction);
is an asynchronous function. It returns before void longFunction() executed the QEventLoop. The result is that your second QTimer::singleShot(1, []() { will trigger immediately. The solution is to move QEventLoop outside your function, before QTimer::singleShot(1, []() {

Using QEventLoop to block connected to multiple signals

Using a QEventLoop to block execution in a Qt program, how can you connect 10 individual signals to the one loop in such a way that it won't unblock until all 10 signals are received?
Please avoid using nested loops when possible. But if you are absolutely sure that you have no way around, you need to have a way to store which of the signals have fired and which of them haven't, and quit the event loop (i.e. maybe by emitting a signal connected to your event loop's QEventLoop::quit) only when all signals have fired.
Here is a minimum example that uses 10 QTimers with different intervals, and waits for all of them to fire before quitting a nested event loop:
#include <QtCore>
#include <algorithm>
int main(int argc, char* argv[]) {
QCoreApplication a(argc, argv);
const int n = 10;
//10 timers to emit timeout signals on different intervals
QTimer timers[n];
//an array that stores whether each timer has fired or not
bool timerFired[n]= {};
QEventLoop loop;
//setup and connect timer signals
for(int i=0; i<n; i++) {
timers[i].setSingleShot(true);
QObject::connect(&timers[i], &QTimer::timeout, [i, &timerFired, &loop]{
qDebug() << "timer " << i << " fired";
timerFired[i]=true;
//if all timers have fired
if(std::all_of(std::begin(timerFired), std::end(timerFired),
[](bool b){ return b; }))
loop.quit(); //quit event loop
});
timers[i].start(i*i*100);
}
qDebug() << "executing loop";
loop.exec();
qDebug() << "loop finished";
QTimer::singleShot(0, &a, &QCoreApplication::quit);
return a.exec();
}

Qt5 WebSockets test app not connecting to test service

I wanted to open a qt websocket to the test service in ws://echo.websocket.org but I got the error QAbstractSocket::RemoteHostClosedError
I am connecting the signal error(QAbstractSocket::SocketError socketError) to a slot in my code in order to read the error number then look for it in here
My code looks like this
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Controller w;
w.initializeWebSocket("ws://echo.websocket.org", true);
w.show();
return a.exec();
}
Controller::Controller(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
}
void Controller::initializeWebSocket(QString url, bool debug)
{
m_webSocketURL = url;
m_webSocketDebug = debug;
if(m_webSocketDebug)
std::cout << "WebSocket server: " << m_webSocketURL.toStdString() << std::endl;
QObject::connect(&m_webSocket, SIGNAL(connected()), this, SLOT(onConnected()));
QObject::connect(&m_webSocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
QObject::connect(&m_webSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
QObject::connect(&m_webSocket, SIGNAL(textMessageReceived(QString)), this, SLOT(onTextMessageReceived(QString)));
m_webSocket.open(QUrl(m_webSocketURL));
}
void Controller::onConnected()
{
if (m_webSocketDebug)
std::cout << "WebSocket connected" << std::endl;
m_webSocket.sendTextMessage(QStringLiteral("Rock it with HTML5 WebSocket"));
}
void Controller::onDisconnected()
{
if (m_webSocketDebug)
std::cout << "WebSocket disconnected" << std::endl;
}
void Controller::onError(QAbstractSocket::SocketError error)
{
std::cout << error << std::endl;
}
void Controller::onTextMessageReceived(QString message)
{
if (m_webSocketDebug)
std::cout << "Message received:" << message.toStdString() << std::endl;
m_webSocket.close();
}
Im new to websockets so I have no idea where could the problem be. Can anyone give advise?
Opening websocket at "ws://echo.websocket.org" works for me just fine.
These handlers are sufficient in my project:
connect(&webSocket, SIGNAL(connected()), this, SLOT(onConnected()));
connect(&webSocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
connect(&webSocket, SIGNAL(textMessageReceived(const QString&)), this, SLOT(onTextMessageReceived(const QString&)));
I also just realized that I don't connect error() signal yet the program code is quite reliable for more than a year already and in case of disconnect there is a connection restore kick in. Maybe I should connect error() signal as well for infrequent strange cases.
The error QAbstractSocket::RemoteHostClosedError can be just a correct thing to get. Try to get the echo within reasonable time. The websocket farm we use in our project is holding the connection for up to 50 minutes so we do ping-pong between the client and the server to keep the connection live before this period expires.
// you can try that immediately after opening the web socket and also using some QTimer
m_webSocket.sendTextMessage("Pong!");
Try that and see the text reply as long as you are playing some public echo service.
Well, I verified your code and it seems to work fine. The error you give indicates a host related issue. It may be due to firewall, isp or other blocks/issues.
WebSocket server: ws://echo.websocket.org
WebSocket connected
Message received:Rock it with HTML5 WebSocket
WebSocket disconnected
I do like to point out that it's preferred to keep a pointer to a QWebSocket 'object'. It's very convenient to declare m_webSocket as QWebSocket *, and add m_webSocket = new QWebSocket(this). It's good practice to treat objects as objects. You don't want to accidentally try to 'copy' an QWebSocket directly. Also, due to the internals of Qt, you may eventually run into problems if this "Controller" object is destroyed while the QWebSocket is still attached to other objects (although I think Qt is prepared for it).

Launch and write to terminal in Qt

I am coding in linux using Qt. I understand that with popen or QProcess I can launch terminal from my program, but how do I write into to it? I google around people are suggesting fork() and pipe().
My purpose is to do an ICMP ping with the terminal, and stop when ping successfully. I made it with popen, but I couldn't stop the ping process thus my program won't run.
You don't write anything to terminal because there's no terminal. You pass name of a program to run and its arguments as arguments of the QProcess::start method. If you only need to know if ping was successful or not it's enough to check the exit code of the process which you started earlier using QProcess::start; you don't have to read its output.
from ping(8) - Linux man page
If ping does not receive any reply
packets at all it will exit with code
1. If a packet count and deadline are both specified, and fewer than count
packets are received by the time the
deadline has arrived, it will also
exit with code 1. On other error it
exits with code 2. Otherwise it exits
with code 0. This makes it possible to
use the exit code to see if a host is
alive or not.
By default ping under Linux runs until you stop it. You can however use -c X option to send only X packets and -w X option to set timeout of the whole process to X seconds. This way you can limit the time ping will take to run.
Below is a working example of using QProcess to run ping program on Windows. For Linux you have to change ping options accordingly (for example -n to -c). In the example, ping is run up to X times, where X is the option you give to Ping class constructor. As soon as any of these executions returns with exit code 0 (meaning success) the result signal is emitted with value true. If no execution is successful the result signal is emitted with value false.
#include <QCoreApplication>
#include <QObject>
#include <QProcess>
#include <QTimer>
#include <QDebug>
class Ping : public QObject {
Q_OBJECT
public:
Ping(int count)
: QObject(), count_(count) {
arguments_ << "-n" << "1" << "example.com";
QObject::connect(&process_,
SIGNAL(finished(int, QProcess::ExitStatus)),
this,
SLOT(handlePingOutput(int, QProcess::ExitStatus)));
};
public slots:
void handlePingOutput(int exitCode, QProcess::ExitStatus exitStatus) {
qDebug() << exitCode;
qDebug() << exitStatus;
qDebug() << static_cast<QIODevice*>(QObject::sender())->readAll();
if (!exitCode) {
emit result(true);
} else {
if (--count_) {
QTimer::singleShot(1000, this, SLOT(ping()));
} else {
emit result(false);
}
}
}
void ping() {
process_.start("ping", arguments_);
}
signals:
void result(bool res);
private:
QProcess process_;
QStringList arguments_;
int count_;
};
class Test : public QObject {
Q_OBJECT
public:
Test() : QObject() {};
public slots:
void handle(bool result) {
if (result)
qDebug() << "Ping suceeded";
else
qDebug() << "Ping failed";
}
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Test test;
Ping ping(3);
QObject::connect(&ping,
SIGNAL(result(bool)),
&test,
SLOT(handle(bool)));
ping.ping();
app.exec();
}
#include "main.moc"

Resources