feat: sample latency multiple times and tolerate keep-alive drops
- time several round-trips on the warm connection and report the fastest, shedding transient network jitter - time the warm-up request too and fall back to it if the node won't honour keep-alive, instead of failing the IP outright - stop at the first reuse failure and keep the samples already gathered
This commit is contained in:
+23
-7
@@ -73,13 +73,17 @@ pub async fn run(
|
||||
results
|
||||
}
|
||||
|
||||
/// Timed round-trips on the warm connection; the fastest is reported to shed
|
||||
/// transient network jitter.
|
||||
const SAMPLES: usize = 3;
|
||||
|
||||
/// Measure the steady-state round-trip latency through the node via `ip`.
|
||||
///
|
||||
/// Building the tunnel and the inner TLS is a one-time cost a live proxy
|
||||
/// session pays only once, so we exclude it: set up the connection, send a
|
||||
/// throwaway warm-up request, then time a second request on the warm
|
||||
/// connection. That matches what clients like v2rayN report — the per-request
|
||||
/// RTT, not the cold connect-and-handshake total.
|
||||
/// throwaway warm-up request, then time several follow-up requests on the warm
|
||||
/// connection and keep the fastest. That matches what clients like v2rayN
|
||||
/// report — the per-request RTT, not the cold connect-and-handshake total.
|
||||
pub async fn measure(node: &VlessNode, ip: IpAddr, timeout: Duration) -> Result<Duration> {
|
||||
let attempt = tokio::time::timeout(timeout, async {
|
||||
let tunnel = vless::connect(node, ip, TARGET_HOST, TARGET_PORT).await?;
|
||||
@@ -89,12 +93,24 @@ pub async fn measure(node: &VlessNode, ip: IpAddr, timeout: Duration) -> Result<
|
||||
let mut stream = vless::tls_connector().connect(sni, tunnel).await?;
|
||||
|
||||
// Warm-up: absorbs the tunnel + TLS setup cost (and TCP slow start).
|
||||
// Timed too, so it can stand in as the result if the node won't honour
|
||||
// keep-alive for the follow-ups, rather than failing the IP outright.
|
||||
let warmup_start = Instant::now();
|
||||
request_once(&mut stream).await?;
|
||||
let warmup_rtt = warmup_start.elapsed();
|
||||
|
||||
// Timed: pure request round-trip on the established connection.
|
||||
let started = Instant::now();
|
||||
request_once(&mut stream).await?;
|
||||
Ok(started.elapsed())
|
||||
// Fastest of several round-trips on the warm connection. Stop at the
|
||||
// first reuse failure and keep whatever samples we did gather.
|
||||
let mut best: Option<Duration> = None;
|
||||
for _ in 0..SAMPLES {
|
||||
let started = Instant::now();
|
||||
if request_once(&mut stream).await.is_err() {
|
||||
break;
|
||||
}
|
||||
let rtt = started.elapsed();
|
||||
best = Some(best.map_or(rtt, |b| b.min(rtt)));
|
||||
}
|
||||
Ok(best.unwrap_or(warmup_rtt))
|
||||
})
|
||||
.await;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user