From c3a1946675855b299a2b36550cdf2c2f69d153aa Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Mon, 17 Sep 2012 23:03:03 +0200 Subject: initial import --- distfoldd/discoverer.cc | 195 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 distfoldd/discoverer.cc (limited to 'distfoldd/discoverer.cc') diff --git a/distfoldd/discoverer.cc b/distfoldd/discoverer.cc new file mode 100644 index 0000000..629a955 --- /dev/null +++ b/distfoldd/discoverer.cc @@ -0,0 +1,195 @@ +#include + +#include "discoverer.h" + +Discoverer::Discoverer(const QUuid &uuid, uint port, const QString &serviceName, QObject *parent) : + QObject(parent), _folderUuid(uuid), _hostUuid(QUuid::createUuid()), + _serviceName(serviceName), _port(port), + _mtime(), _mtimeChanged(false), + _receiver(new QUdpSocket(this)), _sender(new QUdpSocket(this)), + _netConfig(new QNetworkConfigurationManager(this)), + _netInfo(new QSystemNetworkInfo(this)), + _timer(new QSystemAlignedTimer(this)), _fallbackTimer(new QTimer(this)) +{ + connect(_receiver, SIGNAL(readyRead()), SLOT(handleDataAvailable())); + if (!_receiver->bind(servicePort, QUdpSocket::ShareAddress)) { + qWarning() << "Failed to bind to discoverer port" << servicePort; + } + connect(_netConfig, SIGNAL(onlineStateChanged(bool)), SLOT(handleOnlineStateChanged(bool))); + connect(_timer, SIGNAL(timeout()), SLOT(handleTimerTimeout())); + connect(_fallbackTimer, SIGNAL(timeout()), SLOT(handleTimerTimeout())); + connect(_timer, SIGNAL(error(QSystemAlignedTimer::AlignedTimerError)), SLOT(handleTimerError())); + _timer->setSingleShot(true); + _fallbackTimer->setSingleShot(true); + if (_netConfig->isOnline() || + _netConfig->allConfigurations(QNetworkConfiguration::Defined).empty()) { + // Either only, or _netConfig has no configs so it is useless. + qDebug() << "Start timer"; + _timer->start(0, activeBroadcastInterval); + if (_timer && _timer->lastError() != QSystemAlignedTimer::NoError) { + qDebug() << "QSystemAlignedTimer broken at start"; + delete _timer; + _timer = 0; + _fallbackTimer->start(activeBroadcastInterval * 1000); + } + } else { + qDebug() << "I am NOT online"; + } +} + +void Discoverer::setLastModifiedTime(const QDateTime& dateTime) +{ + _mtime = dateTime; + if (_knownHosts.empty()) { + // Idle mode: do nothing. + } else { + // Active mode + switchToActiveMode(); + } + _mtimeChanged = true; +} + +QByteArray Discoverer::encodeMessage() const +{ + QByteArray ba; + QByteArray hostUuid = _hostUuid.toString().toAscii(); + QByteArray folderUuid = _folderUuid.toString().toAscii(); + QByteArray serviceNameUtf8 = _serviceName.toUtf8(); + Message msg; + + Q_ASSERT(hostUuid.length() == sizeof(msg.hostUuid)); + Q_ASSERT(folderUuid.length() == sizeof(msg.folderUuid)); + + ba.reserve(sizeof(msg) + serviceNameUtf8.size()); + + msg.mtime = _mtime.toTime_t(); + strncpy(msg.hostUuid, hostUuid.data(), sizeof(msg.hostUuid)); + msg.port = _port; + strncpy(msg.folderUuid, folderUuid.data(), sizeof(msg.folderUuid)); + + ba.append(reinterpret_cast(&msg), sizeof(msg)); + ba.append(serviceNameUtf8); + return ba; +} + +void Discoverer::switchToActiveMode() +{ + if (_timer) { + if (!_timer->isActive() || _timer->maximumInterval() > activeBroadcastInterval) { + if (_timer->isActive()) _timer->wokeUp(); + _timer->start(0, activeBroadcastInterval); + } + } else if (!_fallbackTimer->isActive() || _fallbackTimer->interval() > activeBroadcastInterval * 1000) { + _fallbackTimer->start(activeBroadcastInterval * 1000); + } +} + +void Discoverer::broadcastMessage() +{ + _sender->writeDatagram(encodeMessage(), QHostAddress::Broadcast, servicePort); + _lastBroadcast = QDateTime::currentDateTime(); + _mtimeChanged = false; + qDebug() << "Broadcast message at" << _lastBroadcast; +} + +void Discoverer::handleDataAvailable() +{ + while (_receiver->hasPendingDatagrams()) { + QByteArray data; + QHostAddress sender; + data.resize(_receiver->pendingDatagramSize()); + _receiver->readDatagram(data.data(), data.size(), &sender); + + handleBroadcastMessage(sender, data); + } +} + +void Discoverer::handleBroadcastMessage(const QHostAddress& sender, const QByteArray &data) +{ + const char *dataPtr = data.data(); + const Message *msg = reinterpret_cast(dataPtr); + QString serviceName = QString::fromUtf8(&dataPtr[sizeof(Message)]); + char uuidData[sizeof(msg->hostUuid)]; + + strncpy(uuidData, msg->hostUuid, sizeof(uuidData)); + QUuid hostUuid(QString::fromAscii(uuidData, sizeof(uuidData))); + strncpy(uuidData, msg->folderUuid, sizeof(uuidData)); + QUuid folderUuid(QString::fromAscii(uuidData, sizeof(uuidData))); + + if (hostUuid.isNull() || folderUuid.isNull()) { + qWarning() << "Null uuid"; + return; + } + if (hostUuid == _hostUuid) { + // A message from myself, ignore + return; + } + if (folderUuid != _folderUuid) { + // A message not about the same folder, ignore + return; + } + + qDebug() << "Got message from" << sender << msg->port; + handleHostSeen(hostUuid); + + QDateTime mtime = QDateTime::fromTime_t(msg->mtime); + qDebug() << mtime << _mtime << _mtime.secsTo(mtime); + if (_mtime.secsTo(mtime) > dateTimeCompareMinDelta) { + // We are outdated, let's try to connect to them. + emit foundMoreRecentHost(sender, msg->port, mtime); + } +} + +void Discoverer::handleHostSeen(const QUuid &hostUuid) +{ + bool host_previously_seen = _knownHosts.contains(hostUuid); + _knownHosts[hostUuid].lastSeen = QDateTime::currentDateTime(); + if (!host_previously_seen || _mtimeChanged) { + switchToActiveMode(); + } +} + +void Discoverer::handleTimerTimeout() +{ + qDebug() << "Timer"; + broadcastMessage(); + QDateTime now = QDateTime::currentDateTime(); + QHash::iterator i = _knownHosts.begin(); + while (i != _knownHosts.end()) { + if (i.value().lastSeen.secsTo(now) > lostHostTimeout) { + i = _knownHosts.erase(i); + } else { + ++i; + } + } + qDebug() << "Known hosts" << _knownHosts.size(); + if (_timer) { + _timer->start(idleBroadcastInterval * 0.7f, idleBroadcastInterval * 1.3f); + } else { + _fallbackTimer->start(idleBroadcastInterval * 1000); + } +} + +void Discoverer::handleTimerError() +{ + qDebug() << "SystemAlignedTimer broken"; + _timer->deleteLater(); + _timer = 0; + _fallbackTimer->start(activeBroadcastInterval * 1000); +} + +void Discoverer::handleOnlineStateChanged(bool online) +{ + if (online) { + qDebug() << "Online"; + if (_netInfo->currentMode() == QSystemNetworkInfo::UnknownMode || + _netInfo->currentMode() == QSystemNetworkInfo::WlanMode) { + switchToActiveMode(); + } + } else { + qDebug() << "Offline"; + _knownHosts.clear(); + _fallbackTimer->stop(); + if (_timer) _timer->stop(); + } +} -- cgit v1.2.3