feat(gui): Retry mechanism for Windows Daemon IPC client

This commit is contained in:
Nick Bolton
2025-04-18 13:56:37 +01:00
parent 3b3d9e14fd
commit 35f0e9e6e4
7 changed files with 94 additions and 39 deletions

View File

@ -289,7 +289,9 @@ void MainWindow::connectSlots()
connect(&m_coreProcess, &CoreProcess::processStateChanged, this, &MainWindow::coreProcessStateChanged);
connect(&m_coreProcess, &CoreProcess::connectionStateChanged, this, &MainWindow::coreConnectionStateChanged);
connect(&m_coreProcess, &CoreProcess::secureSocket, this, &MainWindow::secureSocket);
connect(&m_coreProcess, &CoreProcess::daemonIpcClientConnectFailed, this, &MainWindow::daemonIpcClientConnectFailed);
connect(
&m_coreProcess, &CoreProcess::daemonIpcClientConnectionFailed, this, &MainWindow::daemonIpcClientConnectionFailed
);
connect(m_actionAbout, &QAction::triggered, this, &MainWindow::openAboutDialog);
connect(m_actionClearSettings, &QAction::triggered, this, &MainWindow::clearSettings);
@ -1145,7 +1147,7 @@ bool MainWindow::regenerateLocalFingerprints()
return true;
}
void MainWindow::daemonIpcClientConnectFailed()
void MainWindow::daemonIpcClientConnectionFailed()
{
if (deskflow::gui::messages::showDaemonOffline(this)) {
m_coreProcess.retryDaemon();

View File

@ -158,7 +158,7 @@ private:
void showAndActivate();
void showHostNameEditor();
void setHostName();
void daemonIpcClientConnectFailed();
void daemonIpcClientConnectionFailed();
void toggleCanRunCore(bool enableButtons);
void remoteHostChanged(const QString newRemoteHost);

View File

@ -140,7 +140,9 @@ CoreProcess::CoreProcess(const IServerConfig &serverConfig, std::shared_ptr<Deps
m_daemonIpcClient{new ipc::DaemonIpcClient(this)}
{
connect(m_daemonIpcClient, &ipc::DaemonIpcClient::connected, this, &CoreProcess::daemonIpcClientConnected);
connect(m_daemonIpcClient, &ipc::DaemonIpcClient::connectFailed, this, &CoreProcess::daemonIpcClientConnectFailed);
connect(
m_daemonIpcClient, &ipc::DaemonIpcClient::connectionFailed, this, &CoreProcess::daemonIpcClientConnectionFailed
);
connect(&m_pDeps->process(), &QProcessProxy::finished, this, &CoreProcess::onProcessFinished);

View File

@ -120,7 +120,7 @@ signals:
void connectionStateChanged(ConnectionState state);
void processStateChanged(ProcessState state);
void secureSocket(bool enabled);
void daemonIpcClientConnectFailed();
void daemonIpcClientConnectionFailed();
private slots:
void onProcessFinished(int exitCode, QProcess::ExitStatus);

View File

@ -16,6 +16,7 @@
namespace deskflow::gui::ipc {
const auto kTimeout = 1000;
const auto kRetryLimit = 3;
DaemonIpcClient::DaemonIpcClient(QObject *parent)
: QObject(parent),
@ -27,54 +28,95 @@ DaemonIpcClient::DaemonIpcClient(QObject *parent)
bool DaemonIpcClient::connectToServer()
{
if (m_connecting) {
qDebug() << "daemon ipc client already connecting to server";
if (m_state == State::Connecting) {
qWarning() << "daemon ipc client already connecting to server";
return false;
}
qDebug() << "daemon ipc client connecting to server:" << kDaemonIpcName;
m_connecting = true;
m_socket->connectToServer(kDaemonIpcName);
if (!m_socket->waitForConnected(kTimeout)) {
qWarning() << "daemon ipc client failed to connect";
m_connecting = false;
Q_EMIT connectFailed();
return false;
if (m_state != State::Unconnected) {
qDebug() << "daemon ipc client not in unconnected state, disconnecting";
disconnectFromServer();
}
if (!sendMessage("hello", "hello", false)) {
qWarning() << "daemon ipc client failed to send hello";
m_connecting = false;
Q_EMIT connectFailed();
return false;
if (m_socket->state() != QLocalSocket::UnconnectedState) {
qWarning() << "daemon ipc client socket not in unconnected state, disconnecting";
disconnectFromServer();
}
m_connecting = false;
m_connected = true;
for (int i = 0; i < kRetryLimit; ++i) {
if (i == 0) {
qDebug() << "daemon ipc client connecting to server:" << kDaemonIpcName;
} else {
qDebug() << "daemon ipc client retrying connection, attempt:" << i + 1;
}
qDebug() << "daemon ipc client connected";
Q_EMIT connected();
m_state = State::Connecting;
m_socket->connectToServer(kDaemonIpcName);
return true;
if (!m_socket->waitForConnected(kTimeout)) {
qWarning() << "daemon ipc client failed to connect";
disconnectFromServer();
continue;
}
if (!sendMessage("hello", "hello", false)) {
qWarning() << "daemon ipc client failed to send hello";
disconnectFromServer();
continue;
}
m_state = State::Connected;
qDebug() << "daemon ipc client connected";
Q_EMIT connected();
return true;
}
qWarning() << "daemon ipc client failed to connect after" << kRetryLimit << "attempts";
disconnectFromServer();
Q_EMIT connectionFailed();
return false;
}
void DaemonIpcClient::disconnectFromServer()
{
m_state = State::Disconnecting;
qDebug() << "daemon ipc client disconnecting from server";
m_socket->disconnectFromServer();
if (m_socket->state() != QLocalSocket::UnconnectedState) {
qDebug() << "daemon ipc client waiting for socket to disconnect";
m_socket->waitForDisconnected(kTimeout);
qDebug() << "daemon ipc client disconnected from server";
} else {
qDebug() << "daemon ipc client socket already disconnected";
}
m_state = State::Unconnected;
}
void DaemonIpcClient::handleDisconnected()
{
qWarning() << "daemon ipc client disconnected from server";
m_connected = false;
Q_EMIT connectFailed();
qDebug() << "daemon ipc client disconnected from server";
if (m_state == State::Connected) {
Q_EMIT connectionFailed();
}
m_state = State::Unconnected;
}
void DaemonIpcClient::handleErrorOccurred()
{
qWarning() << "daemon ipc client error:" << m_socket->errorString();
m_connected = false;
disconnectFromServer();
if (m_state == State::Connected) {
Q_EMIT connectionFailed();
}
}
bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
{
if (expectConnected && !m_connected) {
if (expectConnected && !isConnected()) {
qWarning() << "cannot send command, ipc client not connected";
return false;
}
@ -90,7 +132,7 @@ bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectA
qDebug() << "daemon ipc client waiting for ack: " << expectAck;
if (!m_socket->waitForReadyRead(kTimeout)) {
qWarning() << "daemon ipc client failed to read response";
qWarning() << "daemon ipc client socket ready read timed out";
return false;
}
@ -124,8 +166,8 @@ bool DaemonIpcClient::keepAlive()
}
if (!sendMessage("noop")) {
qWarning() << "daemon ipc client keep alive ping failed";
m_connected = false;
qWarning() << "daemon ipc client keep alive ping failed, reconnecting";
connectToServer();
return false;
}

View File

@ -16,9 +16,19 @@ class DaemonIpcClient : public QObject
{
Q_OBJECT
// Represents underlying socket state and whether the server responded to the hello message.
enum class State
{
Unconnected,
Connecting,
Connected,
Disconnecting,
};
public:
explicit DaemonIpcClient(QObject *parent = nullptr);
bool connectToServer();
void disconnectFromServer();
bool sendLogLevel(const QString &logLevel);
bool sendStartProcess(const QString &command, bool elevate);
bool sendStopProcess();
@ -27,12 +37,12 @@ public:
bool isConnected() const
{
return m_connected;
return m_state == State::Connected;
}
signals:
void connected();
void connectFailed();
void connectionFailed();
private slots:
void handleDisconnected();
@ -44,8 +54,7 @@ private:
private:
QLocalSocket *m_socket;
bool m_connected{false};
bool m_connecting{false};
State m_state{State::Unconnected};
};
} // namespace deskflow::gui::ipc

View File

@ -246,7 +246,7 @@ void MSWindowsWatchdog::mainLoop(void *)
LOG_DEBUG("watchdog main loop finished");
if (m_process != nullptr) {
LOG_DEBUG("terminated running process on exit");
LOG_DEBUG("terminating running process on exit");
m_process->shutdown();
m_process.reset();
}