mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-16 17:10:20 +08:00
[fix] prevent stale device online status
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user