mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-08 21:41:02 +03:00
kcp: recover from a silent peer on the endpoint's own clock
KCP is the other transport with nothing to receive when the peer dies, and it was the slower of the two: its endpoint reaps a connection only after 60s without a packet, which is past the 30s inactivity timeout above it, so in practice nothing but that timeout ever noticed. The endpoint already tracks when each connection last heard from its peer and now exposes it, so this reads that rather than anything derived from the session loop - it keeps answering while that loop is busy sending. Its liveness ping now goes out about every 2s rather than every 10s, so silence means the peer rather than an idle link, and eight seconds of it is several missed pings. Same threshold and the same recovery as the WebRTC half, so a user sees the same thing on either transport. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -4280,7 +4280,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kcp-sys"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rustdesk-org/kcp-sys?branch=rustdesk-patches#023a0065398968989f2ddfcf5cc72bb886d02675"
|
||||
source = "git+https://github.com/rustdesk-org/kcp-sys?branch=rustdesk-patches#938eda3e5e9757a612385503af7a6cb1189b2cdd"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto_impl",
|
||||
|
||||
@@ -16,18 +16,22 @@ use crate::{
|
||||
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.
|
||||
// sees silence and only a timeout ends it. These bound that wait for the two transports that
|
||||
// have a liveness signal of their 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.
|
||||
// Neither is 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. WebRTC caps that at its existing
|
||||
// send timeout; KCP sets none, so a large message can delay the check by however long it takes
|
||||
// to drain. 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);
|
||||
// KCP has no equivalent hint, only how long since a packet last arrived; its endpoint pings an
|
||||
// idle peer about every 2s, so this is several missed pings.
|
||||
const KCP_PEER_SILENCE_LIMIT: Duration = Duration::from_secs(8);
|
||||
#[cfg(feature = "unix-file-copy-paste")]
|
||||
use crate::{clipboard::try_empty_clipboard_files, clipboard_file::unix_file_clip};
|
||||
use base::{
|
||||
@@ -339,7 +343,11 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
webrtc_suspect_since = None;
|
||||
}
|
||||
let peer_gone = webrtc_suspect_since
|
||||
.map_or(false, |since| since.elapsed() >= WEBRTC_SUSPECT_GRACE);
|
||||
.map_or(false, |since| since.elapsed() >= WEBRTC_SUSPECT_GRACE)
|
||||
|| kcp
|
||||
.as_ref()
|
||||
.and_then(|k| k.peer_silent_for())
|
||||
.map_or(false, |silent| silent >= KCP_PEER_SILENCE_LIMIT);
|
||||
if peer_gone {
|
||||
log::info!("Peer stopped answering, reconnecting");
|
||||
self.handler.msgbox("restarting-show", "Connecting...", "Connection in progress. Please wait.", "");
|
||||
|
||||
@@ -8,14 +8,15 @@ use hbb_common::{
|
||||
tokio_util, ResultType, Stream,
|
||||
};
|
||||
use kcp_sys::{
|
||||
endpoint::KcpEndpoint,
|
||||
endpoint::{ConnId, KcpEndpoint},
|
||||
packet_def::{KcpPacket, KcpPacketHeader},
|
||||
stream,
|
||||
};
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
pub struct KcpStream {
|
||||
_endpoint: KcpEndpoint,
|
||||
endpoint: KcpEndpoint,
|
||||
conn_id: ConnId,
|
||||
stop_sender: Option<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
@@ -41,6 +42,14 @@ impl KcpStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// How long since a valid packet was last received from the peer, or `None` once the
|
||||
/// connection is gone. Answered by the KCP endpoint's own tasks, not by the session's read
|
||||
/// loop, so it stays meaningful while that loop is busy sending a large message; and the
|
||||
/// endpoint pings an idle peer often enough that silence here means the peer, not quiet.
|
||||
pub fn peer_silent_for(&self) -> Option<std::time::Duration> {
|
||||
self.endpoint.peer_silent_for(&self.conn_id)
|
||||
}
|
||||
|
||||
fn create_framed(stream: stream::KcpStream, local_addr: Option<SocketAddr>) -> Stream {
|
||||
Stream::Tcp(FramedStream(
|
||||
tokio_util::codec::Framed::new(DynTcpStream(Box::new(stream)), BytesCodec::new()),
|
||||
@@ -77,7 +86,8 @@ impl KcpStream {
|
||||
if let Some(stream) = stream::KcpStream::new(&endpoint, conn_id) {
|
||||
Ok((
|
||||
Self {
|
||||
_endpoint: endpoint,
|
||||
endpoint,
|
||||
conn_id,
|
||||
stop_sender: Some(stop_sender),
|
||||
},
|
||||
Self::create_framed(stream, udp_socket.local_addr().ok()),
|
||||
@@ -108,7 +118,8 @@ impl KcpStream {
|
||||
if let Some(stream) = stream::KcpStream::new(&endpoint, conn_id) {
|
||||
Ok((
|
||||
Self {
|
||||
_endpoint: endpoint,
|
||||
endpoint,
|
||||
conn_id,
|
||||
stop_sender: Some(stop_sender),
|
||||
},
|
||||
Self::create_framed(stream, udp_socket.local_addr().ok()),
|
||||
|
||||
Reference in New Issue
Block a user