mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-08 21:41:02 +03:00
webrtc: recover from a silent peer in about 8s instead of 30s
A controlled peer that is killed, switched away by a user switch, or rebooted leaves no trace on a UDP transport: there is no reset to receive, so the session sees silence, and only the 30s inactivity timeout ends it. By then the remote machine may have finished rebooting and be reachable again, while the user has been watching a frozen frame the whole time and is then told the peer reset the connection. ICE already knows sooner. It reports Disconnected about 5s after it stops hearing from the peer, from its own task, so it stays accurate even while this loop is busy sending. That state is transient by design - a Wi-Fi roam or a sleep/wake recovers from it - so it is treated as suspicion, not as death: three more seconds with the transport receiving nothing, and the session reconnects. Receive progress cancels the suspicion, so a peer that is merely slow, or one ICE was late to clear, is not dropped. This only reaches the existing recovery sooner; it does not replace it. The first reconnect goes out immediately and, if it fails, falls into the same retry the UI already applies to any unexpected disconnect. The restart reconnect event is reused deliberately: it is what asks for exactly that, with no error dialog in front of it, and the UI shows "Connecting..." for it rather than anything about restarting. Its five-minute grace stays reserved for a restart the user actually asked for - silence is no evidence of a reboot. The 30s timeout is unchanged and still backs every transport. TCP and WebSocket are untouched. The controlled side is untouched: it detects a dead controller on the same 30s, which wastes some capture but nothing a user sees. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns
This commit is contained in:
Submodule libs/hbb_common updated: 55395c6fcb...c50c1fef32
@@ -15,6 +15,19 @@ use crate::{
|
||||
// Restart msgbox text is kept as a legacy UI fallback; Flutter handles the type as a control event.
|
||||
const RESTART_REMOTE_DEVICE_NO_DATA_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const KCP_CLOSE_REASON_FLUSH_DELAY: Duration = Duration::from_millis(30);
|
||||
// A peer that is killed, logged out or rebooted sends nothing at all over UDP, so the session
|
||||
// sees silence and only a timeout ends it. This bounds that wait for WebRTC, which has a liveness
|
||||
// signal of its own; TCP and WebSocket are left exactly as they were, and the 30s timeout below
|
||||
// still backs all of them.
|
||||
//
|
||||
// Not a hard upper bound on how soon this notices: sends are awaited inline in this same loop, so
|
||||
// one in progress keeps the tick below from running, capped by the transport's own send timeout.
|
||||
// Removing that needs the framing work that would stop one message owning the link.
|
||||
//
|
||||
// Grace after ICE reports Disconnected, which it does ~5s after it stops hearing from the peer,
|
||||
// for ~8s in total. Disconnected is transient by design, so this waits out a Wi-Fi roam or a
|
||||
// sleep/wake rather than acting on the first hint.
|
||||
const WEBRTC_SUSPECT_GRACE: Duration = Duration::from_secs(3);
|
||||
#[cfg(feature = "unix-file-copy-paste")]
|
||||
use crate::{clipboard::try_empty_clipboard_files, clipboard_file::unix_file_clip};
|
||||
use base::{
|
||||
@@ -247,6 +260,8 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
|
||||
let _keep_it = client::hc_connection(feedback, rendezvous_server, token).await;
|
||||
let mut last_recv_time = Instant::now();
|
||||
let mut webrtc_suspect_since: Option<Instant> = None;
|
||||
let mut last_rx_progress = peer.rx_progress();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
@@ -313,6 +328,23 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
self.handler.msgbox("restarting-show", "Restarting remote device", "Connection in progress. Please wait.", "");
|
||||
break;
|
||||
}
|
||||
// Bytes seen by the transport, so a message too large to have arrived
|
||||
// whole still counts, and it clears a suspicion ICE raised late.
|
||||
let rx_progress = peer.rx_progress();
|
||||
let progressed = rx_progress.is_some() && rx_progress != last_rx_progress;
|
||||
last_rx_progress = rx_progress;
|
||||
if peer.webrtc_disconnected() && !progressed {
|
||||
webrtc_suspect_since.get_or_insert_with(Instant::now);
|
||||
} else {
|
||||
webrtc_suspect_since = None;
|
||||
}
|
||||
let peer_gone = webrtc_suspect_since
|
||||
.map_or(false, |since| since.elapsed() >= WEBRTC_SUSPECT_GRACE);
|
||||
if peer_gone {
|
||||
log::info!("Peer stopped answering, reconnecting");
|
||||
self.handler.msgbox("restarting-show", "Connecting...", "Connection in progress. Please wait.", "");
|
||||
break;
|
||||
}
|
||||
let elapsed = fps_instant.elapsed().as_millis();
|
||||
if elapsed < 1000 {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user