diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 8fd74548f..736cd8c02 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -79,6 +79,11 @@ Client::~Client() delete m_socketFactory; } +void Client::setServerAddress(const NetworkAddress &address) +{ + m_serverAddress = address; +} + void Client::connect(size_t addressIndex) { if (m_stream != nullptr) { diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 7681e899b..b92ed77b1 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -77,6 +77,7 @@ public: the client is trying to connect or is already connected. */ void connect(size_t addressIndex = 0); + void setServerAddress(const NetworkAddress &address); //! Disconnect /*! diff --git a/src/lib/deskflow/ClientApp.cpp b/src/lib/deskflow/ClientApp.cpp index 663cfddfb..34e777879 100644 --- a/src/lib/deskflow/ClientApp.cpp +++ b/src/lib/deskflow/ClientApp.cpp @@ -54,21 +54,44 @@ ClientApp::ClientApp(IEventQueue *events, const QString &processName) : App(even void ClientApp::parseArgs() { - // save server address - if (const auto address = Settings::value(Settings::Client::RemoteHost).toString(); !address.isEmpty()) { - try { - *m_serverAddress = NetworkAddress(address.toStdString(), Settings::value(Settings::Core::Port).toInt()); - m_serverAddress->resolve(); - } catch (SocketAddressException &e) { - // allow an address that we can't look up if we're restartable. - // we'll try to resolve the address each time we connect to the - // server. a bad port will never get better. patch by Brent - // Priddy. - if (e.getError() == SocketAddressException::SocketError::BadPort) { - LOG_CRIT("%s: %s" BYE, qPrintable(processName()), e.what(), qPrintable(processName())); - bye(s_exitFailed); + // save server addresses (comma-separated list supported) + if (const auto addressList = Settings::value(Settings::Client::RemoteHost).toString(); !addressList.isEmpty()) { + const int port = Settings::value(Settings::Core::Port).toInt(); + const QStringList addresses = addressList.split(',', Qt::SkipEmptyParts); + + for (const QString &addr : addresses) { + const QString trimmedAddr = addr.trimmed(); + if (trimmedAddr.isEmpty()) { + continue; + } + + try { + NetworkAddress netAddr(trimmedAddr.toStdString(), port); + netAddr.resolve(); + m_serverAddresses.push_back(std::move(netAddr)); + LOG_DEBUG("added server address: %s", qPrintable(trimmedAddr)); + } catch (SocketAddressException &e) { + // allow an address that we can't look up if we're restartable. + // we'll try to resolve the address each time we connect to the + // server. a bad port will never get better. + if (e.getError() == SocketAddressException::SocketError::BadPort) { + LOG_CRIT("%s: %s" BYE, qPrintable(processName()), e.what(), qPrintable(processName())); + bye(s_exitFailed); + } else { + // Still add it - we'll try to resolve later + NetworkAddress netAddr(trimmedAddr.toStdString(), port); + m_serverAddresses.push_back(std::move(netAddr)); + LOG_WARN("could not resolve address '%s': %s (will retry later)", qPrintable(trimmedAddr), e.what()); + } } } + + if (m_serverAddresses.isEmpty()) { + LOG_CRIT("%s: no valid server addresses specified" BYE, qPrintable(processName()), qPrintable(processName())); + bye(s_exitFailed); + } + + LOG_NOTE("configured %zu server address(es)", static_cast(m_serverAddresses.size())); } } @@ -158,23 +181,39 @@ void ClientApp::scheduleClientRestart(double retryTime) getEvents()->addHandler(EventTypes::Timer, timer, [this, timer](const auto &e) { handleClientRestart(e, timer); }); } -void ClientApp::handleClientConnected() const +void ClientApp::handleClientConnected() { LOG_IPC("connected to server"); + // Reset server index on successful connection + m_currentServerIndex = 0; + m_lastServerAddressIndex = 0; } void ClientApp::handleClientFailed(const Event &e) { if ((++m_lastServerAddressIndex) < m_client->getLastResolvedAddressesCount()) { + // Try next resolved address for current hostname std::unique_ptr info(static_cast(e.getData())); - LOG_WARN("failed to connect to server=%s, trying next address", info->m_what.c_str()); + LOG_WARN("failed to connect to server=%s, trying next resolved address", info->m_what.c_str()); if (!m_suspended) { scheduleClientRestart(s_retryTime); } } else { + // All resolved addresses exhausted, try next server in list m_lastServerAddressIndex = 0; - handleClientRefused(e); + tryNextServer(); + + if (m_currentServerIndex == 0) { + // We've cycled through all servers, treat as refused + handleClientRefused(e); + } else { + std::unique_ptr info(static_cast(e.getData())); + LOG_WARN("failed to connect to server=%s, trying next server in list", info->m_what.c_str()); + if (!m_suspended) { + scheduleClientRestart(s_retryTime); + } + } } } @@ -248,12 +287,13 @@ bool ClientApp::startClient() if (m_clientScreen == nullptr) { clientScreen = openClientScreen(); m_client = openClient( - Settings::value(Settings::Core::ScreenName).toString().toStdString(), *m_serverAddress, clientScreen + Settings::value(Settings::Core::ScreenName).toString().toStdString(), getCurrentServerAddress(), clientScreen ); m_clientScreen = clientScreen; LOG_NOTE("started client"); } + m_client->setServerAddress(getCurrentServerAddress()); m_client->connect(m_lastServerAddressIndex); return true; @@ -325,22 +365,33 @@ int ClientApp::start() int ClientApp::runInner(StartupFunc startup) { - // general initialization - m_serverAddress = new NetworkAddress; - int result; try { // run result = startup(); } catch (...) { - delete m_serverAddress; - throw; } return result; } +NetworkAddress &ClientApp::getCurrentServerAddress() +{ + if (m_serverAddresses.isEmpty()) { + throw std::runtime_error("No server addresses configured"); + } + return m_serverAddresses[m_currentServerIndex]; +} + +void ClientApp::tryNextServer() +{ + if (m_serverAddresses.size() > 1) { + m_currentServerIndex = (m_currentServerIndex + 1) % m_serverAddresses.size(); + LOG_DEBUG("switching to server %zu of %zu", m_currentServerIndex + 1, m_serverAddresses.size()); + } +} + void ClientApp::startNode() { // start the client. if this return false then we've failed and diff --git a/src/lib/deskflow/ClientApp.h b/src/lib/deskflow/ClientApp.h index 7f61b7fa8..f38379ba8 100644 --- a/src/lib/deskflow/ClientApp.h +++ b/src/lib/deskflow/ClientApp.h @@ -9,6 +9,9 @@ #pragma once #include "deskflow/App.h" +#include "net/NetworkAddress.h" + +#include namespace deskflow { class Screen; @@ -17,7 +20,6 @@ class ClientArgs; class Event; class Client; -class NetworkAddress; class Thread; class ISocketFactory; @@ -54,7 +56,7 @@ public: void closeClientScreen(deskflow::Screen *screen); void handleClientRestart(const Event &, EventQueueTimer *vtimer); void scheduleClientRestart(double retryTime); - void handleClientConnected() const; + void handleClientConnected(); void handleClientFailed(const Event &e); void handleClientRefused(const Event &e); void handleClientDisconnected(); @@ -78,10 +80,13 @@ public: private: ISocketFactory *getSocketFactory() const; + NetworkAddress &getCurrentServerAddress(); + void tryNextServer(); bool m_suspended = false; Client *m_client = nullptr; deskflow::Screen *m_clientScreen = nullptr; - NetworkAddress *m_serverAddress = nullptr; + QList m_serverAddresses; + size_t m_currentServerIndex = 0; size_t m_lastServerAddressIndex = 0; };