diff --git a/libs/hbb_common b/libs/hbb_common index 55395c6fc..c50c1fef3 160000 --- a/libs/hbb_common +++ b/libs/hbb_common @@ -1 +1 @@ -Subproject commit 55395c6fcbcb8dd4bc8d4e7ab4d7d7c8d1789b43 +Subproject commit c50c1fef326de134ad2b15e7cfbf92133c9cefc8 diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index a49a2d841..cf8bce00d 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -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 Remote { let _keep_it = client::hc_connection(feedback, rendezvous_server, token).await; let mut last_recv_time = Instant::now(); + let mut webrtc_suspect_since: Option = None; + let mut last_rx_progress = peer.rx_progress(); loop { tokio::select! { @@ -313,6 +328,23 @@ impl Remote { 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;