mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-26 23:33:02 +08:00
[fix] prevent stale device online status
This commit is contained in:
@@ -14,42 +14,39 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "platform/video_renderer.h"
|
#include "platform/video_renderer.h"
|
||||||
#include "rd_log.h"
|
#include "rd_log.h"
|
||||||
|
#include "runtime/device_presence_request.h"
|
||||||
#include "runtime/gui_runtime.h"
|
#include "runtime/gui_runtime.h"
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr auto kPresenceProbeTimeout = std::chrono::seconds(5);
|
constexpr auto kPresenceProbeTimeout = std::chrono::seconds(5);
|
||||||
|
constexpr auto kPresenceRefreshInterval = std::chrono::seconds(30);
|
||||||
|
constexpr auto kPresenceRetryInterval = std::chrono::seconds(1);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void GuiRuntime::HandleConnectionStatusChange() {
|
void GuiRuntime::HandleConnectionStatusChange() {
|
||||||
if (signal_connected_ && peer_ && need_to_send_recent_connections_) {
|
if (!signal_connected_ || !peer_) {
|
||||||
if (!recent_connection_ids_.empty()) {
|
return;
|
||||||
nlohmann::json j;
|
}
|
||||||
j["type"] = "recent_connections_presence";
|
const auto now = std::chrono::steady_clock::now();
|
||||||
j["user_id"] = client_id_;
|
if (now - last_presence_request_attempt_at_ < kPresenceRetryInterval) {
|
||||||
j["devices"] = nlohmann::json::array();
|
return;
|
||||||
for (const auto& id : recent_connection_ids_) {
|
}
|
||||||
std::string pure_id = id;
|
// Consume the flag before sending so a concurrent reconnect cannot lose its
|
||||||
size_t pos_y = pure_id.find('Y');
|
// refresh request. Keep it pending while disconnected or after a send error.
|
||||||
size_t pos_n = pure_id.find('N');
|
const bool requested = need_to_send_recent_connections_.exchange(false);
|
||||||
size_t pos = std::string::npos;
|
if (!requested && now < next_presence_refresh_at_) {
|
||||||
if (pos_y != std::string::npos &&
|
return;
|
||||||
(pos_n == std::string::npos || pos_y < pos_n)) {
|
}
|
||||||
pos = pos_y;
|
last_presence_request_attempt_at_ = now;
|
||||||
} else if (pos_n != std::string::npos) {
|
const auto message =
|
||||||
pos = pos_n;
|
BuildDevicePresenceRequest(client_id_, recent_connection_ids_, true).dump();
|
||||||
}
|
if (SendSignalMessage(peer_, message.data(), message.size()) == 0) {
|
||||||
if (pos != std::string::npos) {
|
next_presence_refresh_at_ = now + kPresenceRefreshInterval;
|
||||||
pure_id = pure_id.substr(0, pos);
|
} else {
|
||||||
}
|
need_to_send_recent_connections_ = true;
|
||||||
j["devices"].push_back(pure_id);
|
|
||||||
}
|
|
||||||
auto s = j.dump();
|
|
||||||
SendSignalMessage(peer_, s.data(), s.size());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
need_to_send_recent_connections_ = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiRuntime::HandlePendingPresenceProbe() {
|
void GuiRuntime::HandlePendingPresenceProbe() {
|
||||||
@@ -200,11 +197,9 @@ int GuiRuntime::RequestSingleDevicePresence(const std::string& remote_id,
|
|||||||
pending_presence_remember_password_ = remember_password;
|
pending_presence_remember_password_ = remember_password;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlohmann::json j;
|
const auto s = BuildDevicePresenceRequest(
|
||||||
j["type"] = "recent_connections_presence";
|
client_id_, recent_connection_ids_, false, remote_id)
|
||||||
j["user_id"] = client_id_;
|
.dump();
|
||||||
j["devices"] = nlohmann::json::array({remote_id});
|
|
||||||
auto s = j.dump();
|
|
||||||
|
|
||||||
int ret = SendSignalMessage(peer_, s.data(), s.size());
|
int ret = SendSignalMessage(peer_, s.data(), s.size());
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
@@ -308,6 +303,7 @@ void GuiRuntime::CloseConnectionPeer() {
|
|||||||
devices_.StopSpeakerCapturer();
|
devices_.StopSpeakerCapturer();
|
||||||
devices_.StopKeyboardCapturer();
|
devices_.StopKeyboardCapturer();
|
||||||
signal_connected_ = false;
|
signal_connected_ = false;
|
||||||
|
device_presence_cache_.SetSignalConnected(false);
|
||||||
signal_status_ = SignalStatus::SignalClosed;
|
signal_status_ = SignalStatus::SignalClosed;
|
||||||
PeerPtr* peer;
|
PeerPtr* peer;
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#ifndef _DEVICE_PRESENCE_CACHE_H_
|
#ifndef _DEVICE_PRESENCE_CACHE_H_
|
||||||
#define _DEVICE_PRESENCE_CACHE_H_
|
#define _DEVICE_PRESENCE_CACHE_H_
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -15,15 +16,29 @@ namespace crossdesk {
|
|||||||
|
|
||||||
class DevicePresenceCache {
|
class DevicePresenceCache {
|
||||||
public:
|
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_);
|
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_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
const auto it = cache_.find(device_id);
|
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() {
|
void Clear() {
|
||||||
@@ -32,7 +47,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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_;
|
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
|
// The previous peer may have left a terminal status behind. Reset it before
|
||||||
// Init() starts emitting callbacks for the newly selected server.
|
// Init() starts emitting callbacks for the newly selected server.
|
||||||
signal_connected_ = false;
|
signal_connected_ = false;
|
||||||
|
device_presence_cache_.SetSignalConnected(false);
|
||||||
|
need_to_send_recent_connections_ = true;
|
||||||
signal_status_ = SignalStatus::SignalConnecting;
|
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);
|
std::string client_id(user_id, user_id_size);
|
||||||
if (client_id == runtime->client_id_) {
|
if (client_id == runtime->client_id_) {
|
||||||
runtime->signal_status_ = status;
|
runtime->signal_status_ = status;
|
||||||
|
runtime->device_presence_cache_.SetSignalConnected(
|
||||||
|
status == SignalStatus::SignalConnected);
|
||||||
if (SignalStatus::SignalConnecting == status) {
|
if (SignalStatus::SignalConnecting == status) {
|
||||||
runtime->signal_connected_ = false;
|
runtime->signal_connected_ = false;
|
||||||
} else if (SignalStatus::SignalConnected == status) {
|
} else if (SignalStatus::SignalConnected == status) {
|
||||||
|
|||||||
@@ -55,13 +55,15 @@ struct RecentConnectionsState {
|
|||||||
int recent_connection_image_height_ = 90;
|
int recent_connection_image_height_ = 90;
|
||||||
uint32_t recent_connection_image_save_time_ = 0;
|
uint32_t recent_connection_image_save_time_ = 0;
|
||||||
DevicePresenceCache device_presence_cache_;
|
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 {
|
struct PeerState {
|
||||||
SignalStatus signal_status_ = SignalStatus::SignalClosed;
|
SignalStatus signal_status_ = SignalStatus::SignalClosed;
|
||||||
std::string signal_status_str_;
|
std::string signal_status_str_;
|
||||||
bool signal_connected_ = false;
|
std::atomic<bool> signal_connected_{false};
|
||||||
PeerPtr *peer_ = nullptr;
|
PeerPtr *peer_ = nullptr;
|
||||||
PeerPtr *peer_reserved_ = nullptr;
|
PeerPtr *peer_reserved_ = nullptr;
|
||||||
std::string video_primary_label_ = "primary_display";
|
std::string video_primary_label_ = "primary_display";
|
||||||
@@ -227,4 +229,4 @@ struct RuntimeState : InfrastructureState,
|
|||||||
|
|
||||||
} // namespace crossdesk::gui_detail
|
} // namespace crossdesk::gui_detail
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user