[fix] prevent stale device online status

This commit is contained in:
dijunkun
2026-09-07 20:02:19 +08:00
parent d381cbaa9b
commit af729144fe
6 changed files with 104 additions and 39 deletions
@@ -14,42 +14,39 @@
#include "platform.h"
#include "platform/video_renderer.h"
#include "rd_log.h"
#include "runtime/device_presence_request.h"
#include "runtime/gui_runtime.h"
namespace crossdesk {
namespace {
constexpr auto kPresenceProbeTimeout = std::chrono::seconds(5);
constexpr auto kPresenceRefreshInterval = std::chrono::seconds(30);
constexpr auto kPresenceRetryInterval = std::chrono::seconds(1);
} // namespace
void GuiRuntime::HandleConnectionStatusChange() {
if (signal_connected_ && peer_ && need_to_send_recent_connections_) {
if (!recent_connection_ids_.empty()) {
nlohmann::json j;
j["type"] = "recent_connections_presence";
j["user_id"] = client_id_;
j["devices"] = nlohmann::json::array();
for (const auto& id : recent_connection_ids_) {
std::string pure_id = id;
size_t pos_y = pure_id.find('Y');
size_t pos_n = pure_id.find('N');
size_t pos = std::string::npos;
if (pos_y != std::string::npos &&
(pos_n == std::string::npos || pos_y < pos_n)) {
pos = pos_y;
} else if (pos_n != std::string::npos) {
pos = pos_n;
}
if (pos != std::string::npos) {
pure_id = pure_id.substr(0, pos);
}
j["devices"].push_back(pure_id);
}
auto s = j.dump();
SendSignalMessage(peer_, s.data(), s.size());
}
if (!signal_connected_ || !peer_) {
return;
}
const auto now = std::chrono::steady_clock::now();
if (now - last_presence_request_attempt_at_ < kPresenceRetryInterval) {
return;
}
// Consume the flag before sending so a concurrent reconnect cannot lose its
// refresh request. Keep it pending while disconnected or after a send error.
const bool requested = need_to_send_recent_connections_.exchange(false);
if (!requested && now < next_presence_refresh_at_) {
return;
}
last_presence_request_attempt_at_ = now;
const auto message =
BuildDevicePresenceRequest(client_id_, recent_connection_ids_, true).dump();
if (SendSignalMessage(peer_, message.data(), message.size()) == 0) {
next_presence_refresh_at_ = now + kPresenceRefreshInterval;
} else {
need_to_send_recent_connections_ = true;
}
need_to_send_recent_connections_ = false;
}
void GuiRuntime::HandlePendingPresenceProbe() {
@@ -200,11 +197,9 @@ int GuiRuntime::RequestSingleDevicePresence(const std::string& remote_id,
pending_presence_remember_password_ = remember_password;
}
nlohmann::json j;
j["type"] = "recent_connections_presence";
j["user_id"] = client_id_;
j["devices"] = nlohmann::json::array({remote_id});
auto s = j.dump();
const auto s = BuildDevicePresenceRequest(
client_id_, recent_connection_ids_, false, remote_id)
.dump();
int ret = SendSignalMessage(peer_, s.data(), s.size());
if (ret != 0) {
@@ -308,6 +303,7 @@ void GuiRuntime::CloseConnectionPeer() {
devices_.StopSpeakerCapturer();
devices_.StopKeyboardCapturer();
signal_connected_ = false;
device_presence_cache_.SetSignalConnected(false);
signal_status_ = SignalStatus::SignalClosed;
PeerPtr* peer;
{
@@ -7,6 +7,7 @@
#ifndef _DEVICE_PRESENCE_CACHE_H_
#define _DEVICE_PRESENCE_CACHE_H_
#include <chrono>
#include <mutex>
#include <string>
#include <unordered_map>
@@ -15,15 +16,29 @@ namespace crossdesk {
class DevicePresenceCache {
public:
void SetOnline(const std::string &device_id, bool online) {
using Clock = std::chrono::steady_clock;
static constexpr auto kMaxAge = std::chrono::seconds(60);
void SetSignalConnected(bool connected) {
std::lock_guard<std::mutex> lock(mutex_);
cache_[device_id] = online;
signal_connected_ = connected;
cache_.clear();
}
bool IsOnline(const std::string &device_id) const {
void SetOnline(const std::string &device_id, bool online,
Clock::time_point now = Clock::now()) {
std::lock_guard<std::mutex> lock(mutex_);
if (signal_connected_) {
cache_[device_id] = {online, now};
}
}
bool IsOnline(const std::string &device_id,
Clock::time_point now = Clock::now()) const {
std::lock_guard<std::mutex> lock(mutex_);
const auto it = cache_.find(device_id);
return it != cache_.end() && it->second;
return signal_connected_ && it != cache_.end() && it->second.online &&
now - it->second.updated_at < kMaxAge;
}
void Clear() {
@@ -32,7 +47,12 @@ public:
}
private:
std::unordered_map<std::string, bool> cache_;
struct Entry {
bool online;
Clock::time_point updated_at;
};
bool signal_connected_ = false;
std::unordered_map<std::string, Entry> cache_;
mutable std::mutex mutex_;
};
@@ -0,0 +1,43 @@
/*
* @Author: DI JUNKUN
* @Date: 2026-09-07
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _DEVICE_PRESENCE_REQUEST_H_
#define _DEVICE_PRESENCE_REQUEST_H_
#include <algorithm>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
namespace crossdesk {
inline nlohmann::json BuildDevicePresenceRequest(
const std::string& client_id, const std::vector<std::string>& recent_ids,
bool subscribe, const std::string& probe_id = {}) {
std::vector<std::string> devices;
for (const auto& id : recent_ids) {
// Recent connection keys contain Y/N plus display settings after the ID.
const std::string pure_id = id.substr(0, id.find_first_of("YN"));
if (!pure_id.empty() &&
std::find(devices.begin(), devices.end(), pure_id) == devices.end()) {
devices.push_back(pure_id);
}
}
if (!probe_id.empty() &&
std::find(devices.begin(), devices.end(), probe_id) == devices.end()) {
devices.push_back(probe_id);
}
// Include recent devices even in a one-shot probe: older servers ignore
// subscribe=false and replace their subscriptions with this device list.
return {{"type", "recent_connections_presence"},
{"user_id", client_id},
{"devices", devices},
{"subscribe", subscribe}};
}
} // namespace crossdesk
#endif
@@ -200,6 +200,8 @@ int GuiRuntime::CreateConnectionPeer() {
// The previous peer may have left a terminal status behind. Reset it before
// Init() starts emitting callbacks for the newly selected server.
signal_connected_ = false;
device_presence_cache_.SetSignalConnected(false);
need_to_send_recent_connections_ = true;
signal_status_ = SignalStatus::SignalConnecting;
{
@@ -167,6 +167,8 @@ void PeerEventHandler::OnSignalStatus(SignalStatus status, const char* user_id,
std::string client_id(user_id, user_id_size);
if (client_id == runtime->client_id_) {
runtime->signal_status_ = status;
runtime->device_presence_cache_.SetSignalConnected(
status == SignalStatus::SignalConnected);
if (SignalStatus::SignalConnecting == status) {
runtime->signal_connected_ = false;
} else if (SignalStatus::SignalConnected == status) {
+5 -3
View File
@@ -55,13 +55,15 @@ struct RecentConnectionsState {
int recent_connection_image_height_ = 90;
uint32_t recent_connection_image_save_time_ = 0;
DevicePresenceCache device_presence_cache_;
bool need_to_send_recent_connections_ = true;
std::atomic<bool> need_to_send_recent_connections_{true};
std::chrono::steady_clock::time_point next_presence_refresh_at_{};
std::chrono::steady_clock::time_point last_presence_request_attempt_at_{};
};
struct PeerState {
SignalStatus signal_status_ = SignalStatus::SignalClosed;
std::string signal_status_str_;
bool signal_connected_ = false;
std::atomic<bool> signal_connected_{false};
PeerPtr *peer_ = nullptr;
PeerPtr *peer_reserved_ = nullptr;
std::string video_primary_label_ = "primary_display";
@@ -227,4 +229,4 @@ struct RuntimeState : InfrastructureState,
} // namespace crossdesk::gui_detail
#endif
#endif