Add ping retry feature and provide it to the monitor

This commit is contained in:
Simon Stürz 2022-06-03 16:38:07 +02:00
parent d8c86c5767
commit 2dbd8c47ba
10 changed files with 127 additions and 19 deletions

View File

@ -201,10 +201,10 @@ void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *network
unregisterMonitor(MacAddress(networkDeviceMonitor->networkDeviceInfo().macAddress()));
}
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address)
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint retries)
{
// Note: we use any ping used trough this method also for the monitor evaluation
PingReply *reply = m_ping->ping(address);
PingReply *reply = m_ping->ping(address, retries);
connect(reply, &PingReply::finished, this, [=](){
// Search cache for mac address and update last seen
@ -304,7 +304,8 @@ void NetworkDeviceDiscoveryImpl::pingAllNetworkDevices()
if (targetAddress == entry.ip())
continue;
PingReply *reply = ping(targetAddress);
// Retry only once to ping a device
PingReply *reply = ping(targetAddress, 1);
m_runningPingRepies.append(reply);
connect(reply, &PingReply::finished, this, [=](){
m_runningPingRepies.removeAll(reply);
@ -333,7 +334,7 @@ void NetworkDeviceDiscoveryImpl::processMonitorPingResult(PingReply *reply, Netw
monitor->setLastSeen(QDateTime::currentDateTime());
monitor->setReachable(true);
} else {
qCDebug(dcNetworkDeviceDiscovery()) << "Failed to ping device from" << monitor << reply->error();
qCDebug(dcNetworkDeviceDiscovery()) << "Failed to ping device from" << monitor << "retrying" << reply->retries() << "times:" << reply->error();
monitor->setReachable(false);
}
}
@ -452,12 +453,23 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monit
if (monitor->networkDeviceInfo().address().isNull())
return;
if (monitor->currentPingReply())
return;
// Try to ping first
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->networkDeviceInfo().address().toString();
PingReply *reply = m_ping->ping(monitor->networkDeviceInfo().address(), monitor->pingRetries());
monitor->setCurrentPingReply(reply);
monitor->setLastConnectionAttempt(currentDateTime);
PingReply *reply = m_ping->ping(monitor->networkDeviceInfo().address());
connect(reply, &PingReply::retry, monitor, [=](PingReply::Error error, uint retryCount){
Q_UNUSED(error)
Q_UNUSED(retryCount)
monitor->setLastConnectionAttempt(QDateTime::currentDateTime());
});
connect(reply, &PingReply::finished, monitor, [=](){
monitor->setCurrentPingReply(nullptr);
processMonitorPingResult(reply, monitor);
});
}

View File

@ -71,7 +71,7 @@ public:
void unregisterMonitor(const MacAddress &macAddress) override;
void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) override;
PingReply *ping(const QHostAddress &address) override;
PingReply *ping(const QHostAddress &address, uint retries = 3) override;
MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) override;
MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) override;

View File

@ -29,6 +29,9 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "networkdevicemonitorimpl.h"
#include "loggingcategories.h"
Q_DECLARE_LOGGING_CATEGORY(dcNetworkDeviceDiscovery)
namespace nymeaserver {
@ -73,6 +76,7 @@ void NetworkDeviceMonitorImpl::setReachable(bool reachable)
if (m_reachable == reachable)
return;
qCDebug(dcNetworkDeviceDiscovery()) << this << (reachable ? "is now reachable" : "is not reachable any more.");
m_reachable = reachable;
emit reachableChanged(m_reachable);
}
@ -92,6 +96,26 @@ void NetworkDeviceMonitorImpl::setLastSeen(const QDateTime &lastSeen)
emit lastSeenChanged(m_lastSeen);
}
uint NetworkDeviceMonitorImpl::pingRetries() const
{
return m_pingRetries;
}
void NetworkDeviceMonitorImpl::setPingRetries(uint pingRetries)
{
m_pingRetries = pingRetries;
}
PingReply *NetworkDeviceMonitorImpl::currentPingReply() const
{
return m_currentPingReply;
}
void NetworkDeviceMonitorImpl::setCurrentPingReply(PingReply *reply)
{
m_currentPingReply = reply;
}
QDateTime NetworkDeviceMonitorImpl::lastConnectionAttempt() const
{
return m_lastConnectionAttempt;

View File

@ -35,6 +35,7 @@
#include <QDateTime>
#include "network/networkdevicemonitor.h"
#include "network/pingreply.h"
namespace nymeaserver {
@ -57,16 +58,24 @@ public:
QDateTime lastSeen() const override;
void setLastSeen(const QDateTime &lastSeen);
uint pingRetries() const override;
void setPingRetries(uint pingRetries) override;
PingReply *currentPingReply() const;
void setCurrentPingReply(PingReply *reply);
QDateTime lastConnectionAttempt() const;
void setLastConnectionAttempt(const QDateTime &lastConnectionAttempt);
private:
NetworkDeviceInfo m_networkDeviceInfo;
MacAddress m_macAddress;
bool m_reachable = false;
QDateTime m_lastSeen;
QDateTime m_lastConnectionAttempt;
uint m_pingRetries = 5;
PingReply *m_currentPingReply = nullptr;
};
}

View File

@ -60,7 +60,7 @@ public:
virtual void unregisterMonitor(const MacAddress &macAddress) = 0;
virtual void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) = 0;
virtual PingReply *ping(const QHostAddress &address) = 0;
virtual PingReply *ping(const QHostAddress &address, uint retries = 3) = 0;
virtual MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) = 0;
virtual MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) = 0;

View File

@ -53,6 +53,9 @@ public:
virtual bool reachable() const = 0;
virtual QDateTime lastSeen() const = 0;
virtual uint pingRetries() const = 0;
virtual void setPingRetries(uint pingRetries) = 0;
signals:
void reachableChanged(bool reachable);
void lastSeenChanged(const QDateTime &lastSeen);

View File

@ -111,11 +111,21 @@ PingReply::Error Ping::error() const
return m_error;
}
PingReply *Ping::ping(const QHostAddress &hostAddress)
PingReply *Ping::ping(const QHostAddress &hostAddress, uint retries)
{
PingReply *reply = new PingReply(this);
reply->m_targetHostAddress = hostAddress;
reply->m_networkInterface = NetworkUtils::getInterfaceForHostaddress(hostAddress);
reply->m_reties = retries;
connect(reply, &PingReply::timeout, this, [=](){
// Note: this is not the ICMP timeout, here we actually got nothing from nobody...
finishReply(reply, PingReply::ErrorTimeout);
});
connect(reply, &PingReply::aborted, this, [=](){
finishReply(reply, PingReply::ErrorAborted);
});
// Perform the reply in the next event loop to give the user time to do the reply connects
m_replyQueue.enqueue(reply);
@ -206,10 +216,7 @@ void Ping::performPing(PingReply *reply)
// Start reply timer and handle timeout
m_pendingReplies.insert(reply->requestId(), reply);
reply->m_timer->start(8000);
connect(reply, &PingReply::timeout, this, [=](){
finishReply(reply, PingReply::ErrorTimeout);
});
reply->m_timer->start(m_timeoutDuration);
}
void Ping::verifyErrno(int error)
@ -289,10 +296,34 @@ quint16 Ping::calculateRequestId()
void Ping::finishReply(PingReply *reply, PingReply::Error error)
{
reply->m_error = error;
m_pendingReplies.remove(reply->requestId());
emit reply->finished();
reply->deleteLater();
// Check if we should retry
if (reply->m_retryCount >= reply->m_reties ||
error == PingReply::ErrorNoError ||
error == PingReply::ErrorAborted ||
error == PingReply::ErrorInvalidHostAddress ||
error == PingReply::ErrorPermissionDenied) {
// No retry, we are done
reply->m_error = error;
reply->m_timer->stop();
m_pendingReplies.remove(reply->requestId());
emit reply->finished();
reply->deleteLater();
} else {
m_pendingReplies.remove(reply->requestId());
reply->m_error = error;
reply->m_retryCount++;
reply->m_sequenceNumber++;
qCDebug(dcPing()) << "Ping finished with error" << error << "Retry ping" << reply->targetHostAddress().toString() << reply->m_retryCount << "/" << reply->m_reties;
emit reply->retry(error, reply->retryCount());
// Note: will be restarted once actually sent trough the network
reply->m_timer->stop();
// Re-Enqueu the reply
m_replyQueue.enqueue(reply);
sendNextReply();
}
}
void Ping::onSocketReadyRead(int socketDescriptor)
@ -360,7 +391,7 @@ void Ping::onSocketReadyRead(int socketDescriptor)
int lookupId = QHostInfo::lookupHost(senderAddress.toString(), this, SLOT(onHostLookupFinished(QHostInfo)));
m_pendingHostLookups.insert(lookupId, reply);
qCDebug(dcPingTraffic()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
qCDebug(dcPing()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
<< "ID:" << QString("0x%1").arg(responsePacket->icmp_id, 4, 16, QChar('0'))
<< "Sequence:" << htons(responsePacket->icmp_seq)
<< "Time:" << reply->duration() << "[ms]";

View File

@ -62,7 +62,7 @@ public:
PingReply::Error error() const;
PingReply *ping(const QHostAddress &hostAddress);
PingReply *ping(const QHostAddress &hostAddress, uint retries = 3);
signals:
void availableChanged(bool available);
@ -76,6 +76,7 @@ private:
// Config
QByteArray m_payload = "ping from nymea";
PingReply::Error m_error = PingReply::ErrorNoError;
uint m_timeoutDuration = 5000;
// Socket
QSocketNotifier *m_socketNotifier = nullptr;

View File

@ -63,6 +63,16 @@ QNetworkInterface PingReply::networkInterface() const
return m_networkInterface;
}
uint PingReply::retries() const
{
return m_reties;
}
uint PingReply::retryCount() const
{
return m_retryCount;
}
double PingReply::duration() const
{
return m_duration;
@ -72,3 +82,10 @@ PingReply::Error PingReply::error() const
{
return m_error;
}
void PingReply::abort()
{
m_timer->stop();
m_error = ErrorAborted;
emit aborted();
}

View File

@ -51,6 +51,7 @@ class LIBNYMEA_EXPORT PingReply : public QObject
public:
enum Error {
ErrorNoError,
ErrorAborted,
ErrorInvalidResponse,
ErrorNetworkDown,
ErrorNetworkUnreachable,
@ -70,13 +71,21 @@ public:
QString hostName() const;
QNetworkInterface networkInterface() const;
uint retries() const;
uint retryCount() const;
double duration() const;
Error error() const;
public slots:
void abort();
signals:
void finished();
void timeout();
void retry(Error error, uint retryCount);
void aborted();
private:
QTimer *m_timer = nullptr;
@ -86,6 +95,8 @@ private:
QString m_hostName;
QNetworkInterface m_networkInterface;
uint m_reties = 0;
uint m_retryCount = 0;
uint m_timeout = 3;
double m_duration = 0;
Error m_error = ErrorNoError;