docs(blog): add “Cached ID reconnect” post in 7 languages with cover
- Add multi-language post “Cached ID reconnect: auto rejoin and resume”:
zh, en, ja, ko, de, fr, es under: frontend/content/blog/cached-id-reconnect/*.mdx
- Include cover asset: frontend/public/blog-assets/cached-id-reconnect.webp
- Describe receiver auto-join, reconnect workflow, and resume behavior.
- Tag with WebRTC/P2P/reconnect for discoverability.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: "Ein Klick, wieder da – Fels in der Brandung: Cached‑ID‑Auto‑Join und robuste Wiederverbindung in PrivyDrop"
|
||||
description: "Neu auf der Empfängerseite: Auto‑Beitritt per zwischengespeicherter ID und durchgängige Wiederverbindung für geschmeidige Abläufe – automatischer Raumbeitritt, One‑Click‑Connect, Doppelklick zum Cache‑Update und stabile Erholung in wackeligen Netzen."
|
||||
date: "2025-11-25"
|
||||
author: "david bai"
|
||||
cover: "/blog-assets/cached-id-reconnect.webp"
|
||||
tags: ["Neues Feature", "Automatische Wiederverbindung", "Gecachte ID", "WebRTC", "P2P"]
|
||||
status: "published"
|
||||
---
|
||||
|
||||

|
||||
|
||||
## Einleitung: Warum „Auto‑Join“ und „Wiederverbindung“ zählen
|
||||
|
||||
Neue PrivyDrop‑Nutzer stolpern häufig über zwei kleine Reibungen:
|
||||
|
||||
- Beim Wechsel von Senden zu Empfangen muss die Raum‑ID erneut eingefügt werden.
|
||||
- In Café‑WLANs oder im Mobilfunk erzwingt ein kurzer Haker eine manuelle Wiederverbindung.
|
||||
|
||||
Klein – und doch im Alltag entscheidend dafür, ob sich etwas „mühelos“ anfühlt. Deshalb haben wir zwei Feinschliffe ausgeliefert, die den Fluss wirklich glattziehen:
|
||||
|
||||
- „Cached‑ID‑Auto‑Join“ für Empfänger: Wenn die Bedingungen passen, füllen wir automatisch aus und treten sofort bei.
|
||||
- Durchgängige, robuste Wiederverbindung: Fällt Socket oder P2P, erholen sich Aushandlung und Verbindung selbsttätig.
|
||||
|
||||
Das alles ohne unsere Architektur‑Leitplanke zu verletzen: Backend nur für Signalisierung und Räume; Dateien bleiben E2E‑verschlüsselt und gehen Browser‑zu‑Browser direkt.
|
||||
|
||||
---
|
||||
|
||||
## Funktion 1: Auto‑Join mit zwischengespeicherter ID (Empfänger)
|
||||
|
||||
Beim Wechsel zum Empfangen füllen wir die letzte gespeicherte Raum‑ID automatisch ein und treten sofort bei, wenn:
|
||||
|
||||
- Sie sich im Empfangen‑Tab befinden und noch keinem Raum beigetreten sind;
|
||||
- die URL keinen `roomId`‑Parameter enthält (URL hat Vorrang – kein Überschreiben);
|
||||
- das Eingabefeld leer ist (kein Überschreiben der Nutzer‑Eingabe);
|
||||
- eine gecachte ID in localStorage vorhanden ist.
|
||||
|
||||
Der Check läuft beim Tab‑Wechsel: Zuerst wird ausgefüllt, dann direkt die Beitrittslogik aufgerufen – ein Einfügen/Klick weniger.
|
||||
|
||||
- Code‑Anker:
|
||||
- Auto‑Join useEffect (Empfänger): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp.tsx#L151
|
||||
- Cache‑Helfer (localStorage): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/roomIdCache.ts#L1
|
||||
|
||||
Wann greift es nicht?
|
||||
|
||||
- Sie sind bereits in einem Raum;
|
||||
- die URL trägt explizit `roomId` (z. B. geteilter Deep‑Link);
|
||||
- das Eingabefeld enthält bereits Text in Bearbeitung;
|
||||
- keine gecachte ID gefunden.
|
||||
|
||||
---
|
||||
|
||||
## Funktion 2: „ID speichern/verwenden“ auf Sender‑Seite (Doppelklick zum Aktualisieren)
|
||||
|
||||
Auf der Sender‑Seite bekommt das ID‑Feld einen smarten „Wiederverwenden“‑Button mit zwei Zuständen:
|
||||
|
||||
- ID speichern: Ab Eingabelänge ≥ 8 wird der Button aktiv; Klick speichert die aktuelle Eingabe als Cache‑ID.
|
||||
- Gecachte ID verwenden: Existiert eine, schreibt ein Klick sie ins Feld und tritt sofort bei; Doppelklick schaltet ~3 s auf „ID speichern“, um den Cache zu erneuern.
|
||||
|
||||
Implementierungsnotizen:
|
||||
|
||||
- Einfach/Doppelklick via 400 ms‑Fenster und Timer, Cleanup beim Unmount;
|
||||
- Nach „Gecachte ID verwenden“ tritt der Sender sofort dem Raum bei (kein zusätzlicher „Beitreten“‑Klick);
|
||||
- IDs mit weniger als 8 Zeichen werden nicht gespeichert – Schutz vor versehentlichen Kurz‑IDs.
|
||||
|
||||
- Code‑Anker:
|
||||
- Einfach/Doppelklick inkl. Timer‑Cleanup: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/CachedIdActionButton.tsx#L112
|
||||
- Sofortiger Beitritt bei „Gecachte ID verwenden“ (Sender): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/SendTabPanel.tsx#L193
|
||||
|
||||
---
|
||||
|
||||
## Wiederverbindung: Von der Erkennung bis zur Erholung
|
||||
|
||||
Wir beobachten drei Einstiegspunkte und stoßen die Wiederverbindung an:
|
||||
|
||||
- Socket getrennt: Nach Reconnect und geändertem `socketId` erfolgt der automatische Raum‑Beitritt;
|
||||
- P2P getrennt/fehlgeschlagen/geschlossen: Status markieren und Verbindung neu aufbauen;
|
||||
- Proaktiver `socketId`‑Check: Bei Socket‑Recovery erneut validieren.
|
||||
|
||||
- Code‑Anker:
|
||||
- Auto‑Beitritt nach Socket‑Connect: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L121
|
||||
- Vereinheitlichter attemptReconnection‑Einstieg: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L185
|
||||
- `lastJoinedSocketId` verfolgen und bei Bedarf `initiator-online`: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L460
|
||||
- Sender verarbeitet `recipient-ready` und startet Neuverhandlung: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L12
|
||||
- Empfänger antwortet auf `initiator-online` mit `recipient-ready`: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Recipient.ts#L14
|
||||
- Backend‑Relais:
|
||||
- ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L63
|
||||
- initiator-online: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L102
|
||||
- recipient-ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L108
|
||||
- peer-disconnected: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119
|
||||
|
||||
### Sequenz (Mermaid)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant S as Signaling Server
|
||||
participant A as Sender (initiator)
|
||||
participant B as Empfänger (recipient)
|
||||
|
||||
Note over A,B: Netzschwankungen trennen Socket/P2P
|
||||
A->>A: attemptReconnection()
|
||||
A->>S: join(roomId) / (ggf.) initiator-online
|
||||
S-->>B: initiator-online
|
||||
B->>S: recipient-ready(peerId)
|
||||
S-->>A: recipient-ready(peerId)
|
||||
A->>B: offer
|
||||
B->>A: answer
|
||||
A-->>B: ICE candidates
|
||||
B-->>A: ICE candidates
|
||||
Note over A,B: Verbindung wiederhergestellt, DataChannel neu aufgebaut
|
||||
```
|
||||
|
||||
### Zuverlässigkeitsdetails
|
||||
|
||||
- ICE‑Kandidaten‑Queue: Ist die Remote‑Description nicht bereit oder die Verbindung im Schließen, werden Kandidaten gepuffert und später geflusht; siehe https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L219-L256.
|
||||
- DataChannel‑Backpressure & Chunking: Sender‑Schwelle `bufferedAmountLowThreshold=256KB` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L82); Netzsteuerung `maxBuffer≈3MB / lowThreshold≈512KB / 64KB‑Chunks` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L66-L111, https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L160-L210).
|
||||
- Mobile Wake Lock: Beim Verbinden anfordern, bei Trennung/Fehler freigeben – reduziert Unterbrechungen im Hintergrund.
|
||||
- Fehlerkapselung & Retries: seltene `sendData failed` werden gekapselt, angezeigt und erneut versucht (siehe `sendWithBackpressure`).
|
||||
|
||||
### Kurze vs. lange IDs: Wiederverwendungsstrategie
|
||||
|
||||
- Kurze IDs (4‑stellig) erhalten nach „leerem Raum + Trennung“ eine Gnaden‑TTL von 15 Min. (900 s) – schnelle Wiederverbindung im Fenster; siehe https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119-L125.
|
||||
- Standard‑Ablaufzeit für Räume: 24 h; nur bei leerem Raum nach Trennung wird temporär auf 15 Min. umgestellt; siehe https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/services/redis.ts#L6.
|
||||
- Lange IDs (UUID‑artig) eignen sich für Wiederverwendung über Sitzungen/Geräte hinweg – am besten mit dem Cache‑Button kombinieren.
|
||||
|
||||
---
|
||||
|
||||
## Ausprobieren (Hands‑on)
|
||||
|
||||
Schnelltest am Desktop:
|
||||
|
||||
1. Auf Sender‑Seite eine benutzerdefinierte ID (≥ 8 Zeichen) eingeben und „ID speichern“ klicken.
|
||||
2. Zum Empfänger wechseln: Wenn die Bedingungen passen, wird automatisch ausgefüllt und beigetreten.
|
||||
3. Ausfall simulieren (WLAN aus, Hotspot an, refresh & zurück) und die Auto‑Wiederverbindung beobachten.
|
||||
4. Auf Sender‑Seite „Gecachte ID verwenden“ doppelklicken, kurzzeitig auf „ID speichern“ umschalten und auf eine neue lange ID aktualisieren.
|
||||
|
||||
Mobil/Problemnetze:
|
||||
|
||||
- Hintergrund → Vordergrund; Wechsel WLAN ↔ Mobilfunk.
|
||||
- Prüfen, ob der Empfänger auto‑beitritt und die Übertragung nahtlos fortsetzt.
|
||||
|
||||
---
|
||||
|
||||
## Schluss & Aufruf
|
||||
|
||||
Je geschmeidiger die Verbindung, desto größer der P2P‑Wert. Cached‑ID‑Auto‑Join und robuste Wiederverbindung machen PrivyDrop im echten Netz noch verlässlicher.
|
||||
|
||||
Wenn Ihnen das gefällt, freuen wir uns über einen Stern auf GitHub (<u>https://github.com/david-bai00/PrivyDrop</u>). Das hilft, entdeckt zu werden – und treibt uns an, weiter zu feilen.
|
||||
|
||||
Jetzt online testen: <u>https://www.privydrop.app</u>. Feedback und Verbesserungsvorschläge gern als Issue – helfen Sie uns, das „glatte Gefühl“ weiter auszubauen.
|
||||
|
||||
Zusätzlich sorgt Cloudflare CDN für Beschleunigung über Regionen hinweg – schnellere, stabilere Zugriffe, weniger Ruckler.
|
||||
|
||||
Weiterführende Lektüre:
|
||||
|
||||
- [Warum ich PrivyDrop Open Source gestellt habe](/blog/privydrop-open-source)
|
||||
- [Wie WebRTC Browser‑Direkttransfer ermöglicht](/blog/webRTC-file-transfer)
|
||||
- [Resumable Transfers: Schluss mit der Großdatei‑Anxiety](/blog/resumable-transfers)
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: "One‑Click Reconnect, Rock‑Solid: A Deep Dive into Cached‑ID Auto‑Join and Resilient Reconnect in PrivyDrop"
|
||||
description: "New on the receiver: cached‑ID auto‑join and full‑path reconnect for smoother flows — auto room join, one‑tap direct connect, double‑tap to update the cache, and steady recovery on flaky networks."
|
||||
date: "2025-11-25"
|
||||
author: "david bai"
|
||||
cover: "/blog-assets/cached-id-reconnect.webp"
|
||||
tags: ["New Feature", "Auto Reconnect", "Cached ID", "WebRTC", "P2P"]
|
||||
status: "published"
|
||||
---
|
||||
|
||||

|
||||
|
||||
## Introduction: Why “Auto‑Join” and “Reconnect” Matter
|
||||
|
||||
New users of PrivyDrop often run into two tiny frictions:
|
||||
|
||||
- When switching from Sender to Receiver, you have to paste the room ID again.
|
||||
- On café Wi‑Fi or mobile data, a brief blip means a manual reconnect.
|
||||
|
||||
Tiny? Yes. Frequent in real‑world networks? Absolutely. And they decide whether an app feels “effortless.” So we shipped two polish‑level upgrades that make the flow truly smooth:
|
||||
|
||||
- Receiver “Cached‑ID Auto‑Join”: when conditions match, we auto‑fill and join the room for you.
|
||||
- End‑to‑end “Resilient Reconnect”: whether Socket or P2P drops, negotiation and connection recover on their own.
|
||||
|
||||
Most importantly, none of this changes our red‑line architecture: the backend only handles signaling and room management; files are always end‑to‑end encrypted and go directly browser‑to‑browser.
|
||||
|
||||
---
|
||||
|
||||
## Feature 1: Receiver Cached‑ID Auto‑Join
|
||||
|
||||
When you switch to the Receiver tab, if the following conditions are met, the last cached room ID will be auto‑filled and the app will immediately join the room:
|
||||
|
||||
- You’re on the Receiver tab and not already in a room;
|
||||
- The URL has no explicit `roomId` param (URL wins — we don’t override);
|
||||
- The input is currently empty (we don’t override your typing);
|
||||
- A cached ID exists in localStorage.
|
||||
|
||||
This logic triggers on tab switch. If matched, we first fill the input, then immediately call the join routine—one less paste/click.
|
||||
|
||||
- Code anchors:
|
||||
- Receiver‑side auto‑join useEffect: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp.tsx#L151
|
||||
- Cache helper (localStorage): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/roomIdCache.ts#L1
|
||||
|
||||
When will it not trigger?
|
||||
|
||||
- You’re already in a room;
|
||||
- The URL explicitly carries a `roomId` (e.g., a shared deep link);
|
||||
- There’s already text in the input that you’re editing;
|
||||
- No cached ID is found.
|
||||
|
||||
---
|
||||
|
||||
## Feature 2: Sender “Save/Use Cached ID” (Double‑Tap to Update)
|
||||
|
||||
On the Sender side, the room ID field gets a smart “Reuse” button that toggles between two states:
|
||||
|
||||
- Save ID: when the input length is ≥ 8, the button becomes active; clicking saves the current input as the cached ID.
|
||||
- Use Cached ID: if a cached ID exists, a single tap writes it into the input and joins immediately; a double‑tap flips the button to “Save ID” for about 3 seconds so you can refresh the cache.
|
||||
|
||||
Implementation notes:
|
||||
|
||||
- Single/double taps use a 400ms window with a timer that’s cleaned up on unmount;
|
||||
- After “Use Cached ID” is clicked, the Sender joins the room immediately (no extra “Join” click);
|
||||
- We don’t allow saving IDs shorter than 8 chars to avoid accidental short saves.
|
||||
|
||||
- Code anchors:
|
||||
- Single/double‑tap with timer cleanup: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/CachedIdActionButton.tsx#L112
|
||||
- Auto‑join immediately on “Use Cached ID” (Sender): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/SendTabPanel.tsx#L193
|
||||
|
||||
---
|
||||
|
||||
## Reconnect: From Detection to Full Recovery
|
||||
|
||||
We watch for disconnects from three entry points and trigger reconnection:
|
||||
|
||||
- Socket disconnected: after reconnecting, if `socketId` changes, we auto re‑join the room;
|
||||
- P2P disconnected/failed/closed: we flag state and attempt to rebuild the connection;
|
||||
- Proactive `socketId` change check: on socket recovery, we validate once more.
|
||||
|
||||
- Code anchors:
|
||||
- Auto re‑join after socket connects: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L121
|
||||
- Unified attemptReconnection entry: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L185
|
||||
- Track `lastJoinedSocketId` and trigger `initiator-online` when needed: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L460
|
||||
- Sender handles `recipient-ready` and restarts negotiation: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L12
|
||||
- Receiver responds to `initiator-online` with `recipient-ready`: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Recipient.ts#L14
|
||||
- Backend signaling relay:
|
||||
- ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L63
|
||||
- initiator-online: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L102
|
||||
- recipient-ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L108
|
||||
- peer-disconnected: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119
|
||||
|
||||
### Sequence (Mermaid)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant S as Signaling Server
|
||||
participant A as Sender (initiator)
|
||||
participant B as Receiver (recipient)
|
||||
|
||||
Note over A,B: Network blips cause Socket/P2P disconnects
|
||||
A->>A: attemptReconnection()
|
||||
A->>S: join(roomId) / (maybe) initiator-online
|
||||
S-->>B: initiator-online
|
||||
B->>S: recipient-ready(peerId)
|
||||
S-->>A: recipient-ready(peerId)
|
||||
A->>B: offer
|
||||
B->>A: answer
|
||||
A-->>B: ICE candidates
|
||||
B-->>A: ICE candidates
|
||||
Note over A,B: Connection restored, DataChannel re-established
|
||||
```
|
||||
|
||||
### Reliability Details
|
||||
|
||||
- ICE candidate queue: if the remote description isn’t ready or the connection is closing/closed, candidates are queued and flushed later; see https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L219-L256.
|
||||
- DataChannel backpressure & chunking: Sender threshold `bufferedAmountLowThreshold=256KB` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L82); network control `maxBuffer≈3MB / lowThreshold≈512KB / 64KB chunks` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L66-L111, https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L160-L210).
|
||||
- Mobile wake lock: request Wake Lock when connected; release on disconnect/failure to reduce background interruptions.
|
||||
- Error wrapping & retries: rare `sendData failed` paths are wrapped, surfaced, and retried (see `sendWithBackpressure`).
|
||||
|
||||
### Short vs Long IDs: Reuse Strategy
|
||||
|
||||
- Short IDs (4‑digit) get a 15‑minute (900s) grace TTL when a room becomes empty after a disconnect—allowing quick reconnection within the window; see https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119-L125.
|
||||
- Default room expiry is 24 hours; only empty‑room disconnects switch to the temporary 15‑minute keepalive; see https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/services/redis.ts#L6.
|
||||
- Long IDs (UUID‑like) are better for cross‑session/cross‑device reuse; pair them with the cached‑ID button for best ergonomics.
|
||||
|
||||
---
|
||||
|
||||
## Try It (Hands‑On)
|
||||
|
||||
Desktop quick try:
|
||||
|
||||
1. On the Sender, enter a custom ID with ≥ 8 characters and click “Save ID”.
|
||||
2. Switch to the Receiver: if conditions match, it auto‑fills and joins the room.
|
||||
3. Simulate a dropout (turn Wi‑Fi off, switch to hotspot, refresh and return) and watch it reconnect automatically.
|
||||
4. On the Sender, double‑tap “Use Cached ID” to temporarily switch to “Save ID” and update to a new long ID.
|
||||
|
||||
Mobile/poor network scenarios:
|
||||
|
||||
- Background → foreground; switch Wi‑Fi ↔ cellular.
|
||||
- Observe whether the Receiver auto‑joins, and whether transfer resumes automatically.
|
||||
|
||||
---
|
||||
|
||||
## Wrap‑Up & Call to Action
|
||||
|
||||
Smoother connections amplify the value of P2P. Cached‑ID auto‑join on the receiver and resilient reconnect across the stack make PrivyDrop sturdier and more dependable in the real world.
|
||||
|
||||
If you find this useful, please star us on GitHub (<u>https://github.com/david-bai00/PrivyDrop</u>) so more people can discover it. Your star directly affects search and recommendation signals—and fuels our motivation to keep polishing.
|
||||
|
||||
Try it online: <u>https://www.privydrop.app</u>. We also welcome issues with your feedback and suggestions—help us make the “smooth experience” even smoother.
|
||||
|
||||
Additionally, our domain is accelerated via Cloudflare CDN (saintly cyber help), significantly improving cross‑region speed and stability so more users can open the site without hiccups.
|
||||
|
||||
Further Reading:
|
||||
|
||||
- [Why I Open‑Sourced PrivyDrop](/blog/privydrop-open-source)
|
||||
- [How WebRTC Enables Browser‑Direct Transfer](/blog/webRTC-file-transfer)
|
||||
- [Resumable Transfers: Say Goodbye to Big‑File Anxiety](/blog/resumable-transfers)
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: "Un toque y de vuelta, sólido como roca: Auto‑unión por ID en caché y reconexión resiliente en PrivyDrop"
|
||||
description: "Novedad en el receptor: auto‑entrada mediante ID en caché y reconexión de extremo a extremo para un flujo más suave — entrada automática, conexión directa con un toque, doble toque para actualizar la caché y recuperación firme en redes inestables."
|
||||
date: "2025-11-25"
|
||||
author: "david bai"
|
||||
cover: "/blog-assets/cached-id-reconnect.webp"
|
||||
tags: ["Nueva función", "Reconexión automática", "ID en caché", "WebRTC", "P2P"]
|
||||
status: "published"
|
||||
---
|
||||
|
||||

|
||||
|
||||
## Introducción: por qué importan el “auto‑join” y la “reconexión”
|
||||
|
||||
Quien prueba PrivyDrop por primera vez tropieza con dos fricciones pequeñas pero frecuentes:
|
||||
|
||||
- Al pasar de Enviar a Recibir, hay que pegar de nuevo el ID de sala.
|
||||
- En Wi‑Fi de cafetería o datos móviles, un microcorte obliga a reconectar manualmente.
|
||||
|
||||
Pequeñeces, sí. Pero en red real aparecen mucho y deciden si algo se siente “sin esfuerzo”. Por eso lanzamos dos mejoras de acabado que pulen el flujo hasta hacerlo realmente suave:
|
||||
|
||||
- “Auto‑unión por ID en caché” en el receptor: si se cumplen las condiciones, rellenamos y entramos en la sala automáticamente.
|
||||
- “Reconexion resiliente” de extremo a extremo: caiga Socket o P2P, la negociación y la conexión se recuperan solas.
|
||||
|
||||
Y lo más importante: no tocamos nuestra línea roja arquitectónica. El backend solo hace señalización y salas; los archivos siempre viajan de navegador a navegador con cifrado de extremo a extremo.
|
||||
|
||||
---
|
||||
|
||||
## Función 1: Auto‑unión del receptor con ID en caché
|
||||
|
||||
Al cambiar a la pestaña de Receptor, si se cumplen estas condiciones, rellenamos el último ID de sala guardado y entramos al instante:
|
||||
|
||||
- Estás en la pestaña de Recibir y aún no estás en una sala;
|
||||
- La URL no incluye `roomId` (la URL manda; no sobrescribimos);
|
||||
- El campo de entrada está vacío (no pisamos lo que escribes);
|
||||
- Existe un ID en caché en localStorage.
|
||||
|
||||
La lógica se dispara al cambiar de pestaña: primero rellenamos, luego llamamos directamente a unirse—un pegado/clic menos.
|
||||
|
||||
- Anclas de código:
|
||||
- useEffect de auto‑entrada en el receptor: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp.tsx#L151
|
||||
- Utilidad de caché (localStorage): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/roomIdCache.ts#L1
|
||||
|
||||
¿Cuándo no se activa?
|
||||
|
||||
- Ya estás dentro de una sala;
|
||||
- La URL trae `roomId` explícito (por ejemplo, enlace compartido con parámetro);
|
||||
- El campo de entrada ya contiene texto en edición;
|
||||
- No hay ID en caché.
|
||||
|
||||
---
|
||||
|
||||
## Función 2: “Guardar/Usar ID en caché” en el emisor (doble toque para actualizar)
|
||||
|
||||
En el lado Emisor, el campo del ID incorpora un botón inteligente de “Reusar” con dos estados:
|
||||
|
||||
- Guardar ID: con longitud ≥ 8, el botón se habilita; al pulsar, guarda el texto actual como ID en caché.
|
||||
- Usar ID en caché: si existe, un toque lo escribe en el campo y se une de inmediato; con doble toque, el botón pasa durante ~3 s a “Guardar ID” para que puedas actualizar la caché.
|
||||
|
||||
Notas de implementación:
|
||||
|
||||
- El simple/doble toque se decide en una ventana de 400 ms con temporizador, limpiado al desmontar;
|
||||
- Tras “Usar ID en caché”, el emisor entra a la sala inmediatamente (sin pulsar “Unirse”);
|
||||
- No permitimos guardar IDs de menos de 8 caracteres para evitar guardados accidentales.
|
||||
|
||||
- Anclas de código:
|
||||
- Simple/doble toque y limpieza del temporizador: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/CachedIdActionButton.tsx#L112
|
||||
- Unión inmediata al “Usar ID en caché” (emisor): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/SendTabPanel.tsx#L193
|
||||
|
||||
---
|
||||
|
||||
## Reconexión: de la detección a la recuperación completa
|
||||
|
||||
Observamos tres puntos para detectar cortes y disparar la reconexión:
|
||||
|
||||
- Socket desconectado: si al volver cambia el `socketId`, re‑entramos a la sala automáticamente;
|
||||
- P2P desconectado/fallido/cerrado: marcamos estado e intentamos reconstruir la conexión;
|
||||
- Comprobación proactiva de cambio de `socketId`: al recuperar el socket, validamos de nuevo.
|
||||
|
||||
- Anclas de código:
|
||||
- Re‑entrada automática tras reconectar el socket: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L121
|
||||
- Punto unificado de attemptReconnection: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L185
|
||||
- Registro de `lastJoinedSocketId` y envío de `initiator-online` si procede: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L460
|
||||
- El emisor recibe `recipient-ready` y reinicia la negociación: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L12
|
||||
- El receptor responde a `initiator-online` con `recipient-ready`: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Recipient.ts#L14
|
||||
- Relé de señalización en backend:
|
||||
- ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L63
|
||||
- initiator-online: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L102
|
||||
- recipient-ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L108
|
||||
- peer-disconnected: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119
|
||||
|
||||
### Secuencia (Mermaid)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant S as Signaling Server
|
||||
participant A as Emisor (initiator)
|
||||
participant B as Receptor (recipient)
|
||||
|
||||
Note over A,B: Microcortes provocan desconexión de Socket/P2P
|
||||
A->>A: attemptReconnection()
|
||||
A->>S: join(roomId) / (quizá) initiator-online
|
||||
S-->>B: initiator-online
|
||||
B->>S: recipient-ready(peerId)
|
||||
S-->>A: recipient-ready(peerId)
|
||||
A->>B: offer
|
||||
B->>A: answer
|
||||
A-->>B: ICE candidates
|
||||
B-->>A: ICE candidates
|
||||
Note over A,B: Conexión restaurada, DataChannel restablecido
|
||||
```
|
||||
|
||||
### Detalles de fiabilidad
|
||||
|
||||
- Cola de candidatos ICE: si la descripción remota no está lista o la conexión se cierra, los candidatos se encolan y se envían después; ver https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L219-L256.
|
||||
- Backpressure y troceado del DataChannel: umbral del emisor `bufferedAmountLowThreshold=256KB` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L82); control de red `maxBuffer≈3MB / lowThreshold≈512KB / trozos de 64KB` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L66-L111, https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L160-L210).
|
||||
- Wake Lock móvil: se solicita al conectar y se libera al desconectar/fallar, para reducir interrupciones al pasar a segundo plano.
|
||||
- Envoltorio de errores y reintentos: los raros `sendData failed` se capturan, se muestran y se reintentan (ver `sendWithBackpressure`).
|
||||
|
||||
### Estrategia de reutilización: IDs cortos vs largos
|
||||
|
||||
- IDs cortos (4 dígitos) reciben un TTL de 15 minutos (900s) cuando la sala queda vacía tras la desconexión; permite reconectar fácilmente en esa ventana; ver https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119-L125.
|
||||
- La expiración por defecto de la sala es 24 horas; solo en “sala vacía + desconexión” pasamos a la retención temporal de 15 minutos; ver https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/services/redis.ts#L6.
|
||||
- IDs largos (tipo UUID) son mejores para reutilización entre sesiones/dispositivos; combínalos con el botón de caché para la mejor ergonomía.
|
||||
|
||||
---
|
||||
|
||||
## Cómo probarlo (hands‑on)
|
||||
|
||||
Prueba rápida en escritorio:
|
||||
|
||||
1. En el Emisor, escribe un ID personalizado de ≥ 8 caracteres y pulsa “Guardar ID”.
|
||||
2. Cambia al Receptor: si se cumplen condiciones, se rellenará y entrará automáticamente.
|
||||
3. Simula un corte (apaga el Wi‑Fi, cambia a hotspot, recarga y vuelve) y observa la reconexión automática.
|
||||
4. En el Emisor, haz doble toque en “Usar ID en caché” para cambiar temporalmente a “Guardar ID” y actualizar a un ID largo nuevo.
|
||||
|
||||
Móvil/redes pobres:
|
||||
|
||||
- Segundo plano → primer plano; cambia entre Wi‑Fi ↔ datos.
|
||||
- Observa si el receptor se auto‑une y si la transferencia se reanuda sola.
|
||||
|
||||
---
|
||||
|
||||
## Cierre y llamada a la acción
|
||||
|
||||
Cuanto más suave es la conexión, más crece el valor del P2P. El auto‑join por ID en caché y la reconexión resiliente hacen que PrivyDrop sea más robusto y confiable en redes reales.
|
||||
|
||||
Si te resulta útil, déjanos una estrella en GitHub (<u>https://github.com/david-bai00/PrivyDrop</u>) para que más personas nos encuentren. Tu estrella impacta en búsqueda y recomendaciones—y alimenta nuestras ganas de seguir puliendo.
|
||||
|
||||
Pruébalo online: <u>https://www.privydrop.app</u>. También te invitamos a abrir issues con comentarios y sugerencias para seguir afinando esa “experiencia sin fricción”.
|
||||
|
||||
Además, el dominio está acelerado con Cloudflare CDN, mejorando notablemente velocidad y estabilidad entre regiones para una apertura sin tirones.
|
||||
|
||||
Lecturas recomendadas:
|
||||
|
||||
- [Por qué hice PrivyDrop de código abierto](/blog/privydrop-open-source)
|
||||
- [Cómo WebRTC permite la transferencia directa entre navegadores](/blog/webRTC-file-transfer)
|
||||
- [Transferencias reanudables: adiós a la ansiedad por los archivos grandes](/blog/resumable-transfers)
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
---
|
||||
title: "Un clic et ça repart, solide comme un roc : Auto‑join via ID en cache et reconnexion résiliente dans PrivyDrop"
|
||||
description: "Nouveauté côté réception : auto‑entrée grâce à l’ID en cache et reconnexion de bout en bout pour un flux plus fluide — entrée automatique, connexion directe en un clic, double‑clic pour mettre à jour le cache, et reprise fiable sur réseau instable."
|
||||
date: "2025-11-25"
|
||||
author: "david bai"
|
||||
cover: "/blog-assets/cached-id-reconnect.webp"
|
||||
tags: ["Nouvelle fonctionnalité", "Reconnexion automatique", "ID en cache", "WebRTC", "P2P"]
|
||||
status: "published"
|
||||
---
|
||||
|
||||

|
||||
|
||||
## Introduction : pourquoi « auto‑join » et « reconnexion »
|
||||
|
||||
Les nouveaux utilisateurs de PrivyDrop rencontrent souvent deux petites frictions :
|
||||
|
||||
- En passant d’Envoyer à Recevoir, il faut recoller l’ID de salle ;
|
||||
- Sur un Wi‑Fi de café ou en 4G, une micro‑coupure impose une reconnexion manuelle.
|
||||
|
||||
Des détails ? Oui. Mais très fréquents dans le monde réel — ils font la différence entre « ça marche » et « c’est fluide ». Nous avons donc livré deux finitions qui rendent l’expérience vraiment soyeuse :
|
||||
|
||||
- « Auto‑join via ID en cache » côté récepteur : si les conditions sont réunies, on pré‑remplit et on rejoint la salle automatiquement ;
|
||||
- « Reconnexion résiliente » de bout en bout : que Socket ou P2P tombe, la négociation et la connexion se rétablissent seules.
|
||||
|
||||
Le tout sans toucher à notre ligne rouge architecturale : le backend ne fait que la signalisation et la gestion de salle ; les fichiers restent chiffrés de bout en bout, directement de navigateur à navigateur.
|
||||
|
||||
---
|
||||
|
||||
## Fonction 1 : Auto‑join du récepteur avec ID en cache
|
||||
|
||||
Lorsque vous passez à l’onglet Récepteur, si les conditions suivantes sont réunies, le dernier ID de salle en cache est pré‑rempli et l’entrée est immédiate :
|
||||
|
||||
- Vous êtes sur l’onglet Récepteur et pas encore dans une salle ;
|
||||
- L’URL ne contient pas `roomId` (l’URL l’emporte — pas d’écrasement) ;
|
||||
- Le champ de saisie est vide (on ne remplace pas votre saisie) ;
|
||||
- Un ID en cache existe dans le localStorage.
|
||||
|
||||
La logique se déclenche au changement d’onglet. Si c’est bon, on remplit d’abord, puis on appelle aussitôt la routine d’entrée — un collage/clic de moins.
|
||||
|
||||
- Repères de code :
|
||||
- useEffect d’auto‑entrée côté récepteur : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp.tsx#L151
|
||||
- Utilitaire de cache (localStorage) : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/roomIdCache.ts#L1
|
||||
|
||||
Quand cela ne s’applique‑t‑il pas ?
|
||||
|
||||
- Vous êtes déjà dans une salle ;
|
||||
- L’URL porte explicitement `roomId` (lien de partage avec paramètre) ;
|
||||
- Le champ contient déjà un texte en cours de saisie ;
|
||||
- Aucun ID en cache n’est trouvé.
|
||||
|
||||
---
|
||||
|
||||
## Fonction 2 : « Enregistrer / Utiliser l’ID en cache » côté émetteur (double‑clic pour mettre à jour)
|
||||
|
||||
Sur l’émetteur, le champ d’ID accueille un bouton « Réutiliser » astucieux qui alterne entre deux états :
|
||||
|
||||
- Enregistrer l’ID : quand la longueur ≥ 8, le bouton s’active ; un clic enregistre la saisie courante comme ID en cache.
|
||||
- Utiliser l’ID en cache : s’il existe, un clic l’insère et rejoint la salle immédiatement ; un double‑clic bascule ~3 s en « Enregistrer l’ID » pour actualiser le cache.
|
||||
|
||||
Notes d’implémentation :
|
||||
|
||||
- Simple/double‑clic via une fenêtre de 400 ms, timer nettoyé au démontage ;
|
||||
- Après « Utiliser l’ID en cache », l’émetteur rejoint la salle immédiatement (pas de clic « Rejoindre » supplémentaire) ;
|
||||
- Pas d’enregistrement d’ID de moins de 8 caractères pour éviter les « courts » accidentels.
|
||||
|
||||
- Repères de code :
|
||||
- Simple/double‑clic et nettoyage du timer : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/CachedIdActionButton.tsx#L112
|
||||
- Rejoindre immédiatement après « Utiliser l’ID en cache » (émetteur) : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/SendTabPanel.tsx#L193
|
||||
|
||||
---
|
||||
|
||||
## Reconnexion : de la détection au rétablissement complet
|
||||
|
||||
Nous surveillons trois points d’entrée et déclenchons la reconnexion :
|
||||
|
||||
- Socket déconnecté : après reconnexion, si le `socketId` change, on ré‑entre automatiquement ;
|
||||
- P2P déconnecté/échec/fermé : on marque l’état et on tente de reconstruire la connexion ;
|
||||
- Vérification proactive de changement de `socketId` : à la reprise du socket, on revalide.
|
||||
|
||||
- Repères de code :
|
||||
- Ré‑entrée auto après connexion du socket : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L121
|
||||
- Point d’entrée unifié attemptReconnection : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L185
|
||||
- Suivi de `lastJoinedSocketId` et émission de `initiator-online` si nécessaire : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L460
|
||||
- Côté émetteur, réception de `recipient-ready` et reprise de la négociation : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L12
|
||||
- Côté récepteur, réponse `recipient-ready` à `initiator-online` : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Recipient.ts#L14
|
||||
- Relais côté backend :
|
||||
- ready : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L63
|
||||
- initiator-online : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L102
|
||||
- recipient-ready : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L108
|
||||
- peer-disconnected : https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119
|
||||
|
||||
### Séquence (Mermaid)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant S as Signaling Server
|
||||
participant A as Émetteur (initiator)
|
||||
participant B as Récepteur (recipient)
|
||||
|
||||
Note over A,B: Les aléas réseau coupent Socket/P2P
|
||||
A->>A: attemptReconnection()
|
||||
A->>S: join(roomId) / (évent.) initiator-online
|
||||
S-->>B: initiator-online
|
||||
B->>S: recipient-ready(peerId)
|
||||
S-->>A: recipient-ready(peerId)
|
||||
A->>B: offer
|
||||
B->>A: answer
|
||||
A-->>B: ICE candidates
|
||||
B-->>A: ICE candidates
|
||||
Note over A,B: Connexion restaurée, DataChannel ré‑établi
|
||||
```
|
||||
|
||||
### Détails de fiabilité
|
||||
|
||||
- File d’attente des candidats ICE : si la description distante n’est pas prête ou que la connexion se ferme, on met en file et on rejoue plus tard ; voir https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L219-L256.
|
||||
- Rétro‑pression et découpage DataChannel : seuil émetteur `bufferedAmountLowThreshold=256KB` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L82) ; contrôle réseau `maxBuffer≈3MB / lowThreshold≈512KB / chunks de 64KB` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L66-L111, https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L160-L210).
|
||||
- Wake Lock mobile : demande à l’établissement de la connexion, libération à la déconnexion/échec — pour réduire les interruptions en arrière‑plan.
|
||||
- Encapsulation d’erreurs et retries : les rares `sendData failed` sont capturés, surfacés et réessayés (voir `sendWithBackpressure`).
|
||||
|
||||
### Stratégie de réutilisation : IDs courts vs longs
|
||||
|
||||
- IDs courts (4 chiffres) : en cas de « salle vide + déconnexion », TTL de grâce de 15 minutes (900s) — reconnexion rapide dans la fenêtre ; voir https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119-L125.
|
||||
- Expiration par défaut : 24 h ; seul le cas « salle vide + déconnexion » passe en conservation temporaire de 15 min ; voir https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/services/redis.ts#L6.
|
||||
- IDs longs (type UUID) : mieux pour la réutilisation inter‑sessions/appareils ; les combiner avec le bouton d’ID en cache offre la meilleure ergonomie.
|
||||
|
||||
---
|
||||
|
||||
## Prise en main (hands‑on)
|
||||
|
||||
Essai rapide sur desktop :
|
||||
|
||||
1. Côté Émetteur, entrez un ID personnalisé (≥ 8 caractères) et cliquez « Enregistrer l’ID ».
|
||||
2. Passez au Récepteur : si les conditions sont réunies, auto‑remplissage et entrée immédiate.
|
||||
3. Simulez une coupure (coupure Wi‑Fi, bascule hotspot, actualiser puis revenir) et observez la reconnexion automatique.
|
||||
4. Côté Émetteur, double‑cliquez « Utiliser l’ID en cache » pour basculer brièvement en « Enregistrer l’ID » et mettre à jour vers un nouvel ID long.
|
||||
|
||||
Mobile / réseaux difficiles :
|
||||
|
||||
- Arrière‑plan → premier plan ; bascule Wi‑Fi ↔ cellulaire.
|
||||
- Vérifiez l’auto‑entrée du Récepteur et la reprise automatique du transfert.
|
||||
|
||||
---
|
||||
|
||||
## Conclusion & appel à l’action
|
||||
|
||||
Plus la connexion est fluide, plus la valeur du P2P grandit. L’auto‑join via ID en cache et la reconnexion résiliente renforcent la robustesse de PrivyDrop dans les réseaux réels.
|
||||
|
||||
Si vous aimez, mettez‑nous une étoile sur GitHub (<u>https://github.com/david-bai00/PrivyDrop</u>) — cela accroît la visibilité et nourrit notre envie de peaufiner.
|
||||
|
||||
Essai en ligne : <u>https://www.privydrop.app</u>. Vos retours et idées sont bienvenus via les issues : continuons ensemble à polir « l’expérience soyeuse ».
|
||||
|
||||
Par ailleurs, notre domaine bénéficie de l’accélération Cloudflare CDN, améliorant nettement vitesse et stabilité inter‑régions.
|
||||
|
||||
Pour aller plus loin :
|
||||
|
||||
- [Pourquoi j’ai open‑sourcé PrivyDrop](/blog/privydrop-open-source)
|
||||
- [Comment WebRTC permet le transfert direct entre navigateurs](/blog/webRTC-file-transfer)
|
||||
- [Transferts reprenables : adieu à l’anxiété des gros fichiers](/blog/resumable-transfers)
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: "ワンタップで復帰、盤石の安定性:PrivyDrop のキャッシュID自動参加と切断時の再接続を徹底解説"
|
||||
description: "受信側のキャッシュID自動入室と全経路の自動再接続で、体験はより滑らかに。自動入室、ワンタップ直結、ダブルタップでキャッシュ更新、そして不安定なネットでも粘り強く復帰します。"
|
||||
date: "2025-11-25"
|
||||
author: "david bai"
|
||||
cover: "/blog-assets/cached-id-reconnect.webp"
|
||||
tags: ["新機能", "自動再接続", "キャッシュID", "WebRTC", "P2P"]
|
||||
status: "published"
|
||||
---
|
||||
|
||||

|
||||
|
||||
## はじめに:なぜ「自動入室」と「再接続」なのか
|
||||
|
||||
PrivyDrop を初めて使うと、よくある小さな引っかかりが二つあります。
|
||||
|
||||
- 送信から受信に切り替えるたび、部屋の ID をもう一度貼り付ける。
|
||||
- カフェの Wi‑Fi やモバイル回線で一瞬切れると、手動でつなぎ直す。
|
||||
|
||||
小さなこと。でも現実のネットワークでは頻出で、使い心地を左右します。そこで私たちは、体験を“するり”と滑らかにする二つの磨き込みを加えました。
|
||||
|
||||
- 受信側「キャッシュIDの自動入室」:条件を満たせば、自動で入力&即入室。
|
||||
-, エンドツーエンドの「粘り強い再接続」:Socket / P2P のどちらが落ちても、自動で再ネゴシエーション&復旧。
|
||||
|
||||
そして大切なのは、アーキテクチャのレッドラインは不変であること。バックエンドは信令とルーム管理のみ、ファイルは常に E2E 暗号化でブラウザ間を直送します。
|
||||
|
||||
---
|
||||
|
||||
## 機能1:受信側のキャッシュID自動入室
|
||||
|
||||
受信タブへ切り替えた際、以下の条件を満たすと、最後に保存した部屋 ID を自動入力し、すぐ入室します。
|
||||
|
||||
- 受信タブにいて、まだ入室していない;
|
||||
- URL に `roomId` パラメータがない(URL が優先、上書きしない);
|
||||
- 入力欄が空(ユーザーの入力は上書きしない);
|
||||
- localStorage にキャッシュ ID が存在する。
|
||||
|
||||
この判定はタブ切り替え時に走ります。条件一致なら、入力欄を埋めてからそのまま入室ロジックを呼び出し、貼り付け/クリックを 1 回減らします。
|
||||
|
||||
- コード参照:
|
||||
- 受信側の自動入室 useEffect: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp.tsx#L151
|
||||
- キャッシュユーティリティ(localStorage): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/roomIdCache.ts#L1
|
||||
|
||||
発動しないとき:
|
||||
|
||||
- すでに入室している;
|
||||
- URL が明示的に `roomId` を持つ(共有リンクなど);
|
||||
- 入力欄に既に文字があり編集中;
|
||||
- キャッシュ ID が存在しない。
|
||||
|
||||
---
|
||||
|
||||
## 機能2:送信側の「保存/使用」ボタン(ダブルタップで更新)
|
||||
|
||||
送信側の部屋 ID 入力欄に、賢い「再利用」ボタンを追加しました。状態は 2 つに切り替わります。
|
||||
|
||||
- ID を保存:入力長が 8 文字以上で有効化。クリックで現在の入力をキャッシュ ID として保存。
|
||||
- キャッシュ ID を使用:キャッシュがあれば、ワンタップで入力欄に反映してそのまま入室。ダブルタップすると約 3 秒だけ「ID を保存」に切り替わり、キャッシュを更新できます。
|
||||
|
||||
実装メモ:
|
||||
|
||||
- シングル/ダブルタップは 400ms の判定窓+タイマーで実現し、アンマウント時にクリーンアップ;
|
||||
- 「キャッシュ ID を使用」後は送信側が即入室(追加の「入室」操作は不要);
|
||||
- 8 文字未満は保存不可にして、短い ID の誤保存を防止。
|
||||
|
||||
- コード参照:
|
||||
- シングル/ダブルタップとタイマーのクリーンアップ: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/CachedIdActionButton.tsx#L112
|
||||
- 「キャッシュ ID を使用」で即入室(送信側): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/SendTabPanel.tsx#L193
|
||||
|
||||
---
|
||||
|
||||
## 再接続:検知から復旧までの流れ
|
||||
|
||||
私たちは 3 つの入口から「切断」を監視し、再接続を走らせます。
|
||||
|
||||
- Socket 切断:再接続後に `socketId` が変わっていれば自動再入室;
|
||||
- P2P 切断/失敗/クローズ:状態をマーキングし、接続再構築を試行;
|
||||
- `socketId` の変化を能動チェック:Socket 復旧時に再確認。
|
||||
|
||||
- コード参照:
|
||||
- Socket 接続後の自動再入室: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L121
|
||||
- 再接続の統一エントリ: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L185
|
||||
- `lastJoinedSocketId` の記録と必要時の `initiator-online` 送出: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L460
|
||||
- 送信側の `recipient-ready` 受信と再ネゴシエーション開始: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L12
|
||||
- 受信側の `initiator-online` 受信と `recipient-ready` 応答: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Recipient.ts#L14
|
||||
- バックエンドの信令リレー:
|
||||
- ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L63
|
||||
- initiator-online: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L102
|
||||
- recipient-ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L108
|
||||
- peer-disconnected: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119
|
||||
|
||||
### シーケンス(Mermaid)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant S as Signaling Server
|
||||
participant A as 送信側(initiator)
|
||||
participant B as 受信側(recipient)
|
||||
|
||||
Note over A,B: ネットの揺らぎで Socket/P2P が切断
|
||||
A->>A: attemptReconnection()
|
||||
A->>S: join(roomId) / (場合により)initiator-online
|
||||
S-->>B: initiator-online
|
||||
B->>S: recipient-ready(peerId)
|
||||
S-->>A: recipient-ready(peerId)
|
||||
A->>B: offer
|
||||
B->>A: answer
|
||||
A-->>B: ICE candidates
|
||||
B-->>A: ICE candidates
|
||||
Note over A,B: 接続回復、DataChannel 再確立
|
||||
```
|
||||
|
||||
### 信頼性ディテール
|
||||
|
||||
- ICE 候補キュー:リモート記述が未確立、または接続がクローズ系なら候補をキューイングし、後でまとめて反映;https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L219-L256。
|
||||
- DataChannel の背圧と分割送信:送信側しきい値 `bufferedAmountLowThreshold=256KB`(https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L82);ネットワーク制御は `maxBuffer≈3MB / lowThreshold≈512KB / 64KB チャンク`(https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L66-L111, https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L160-L210)。
|
||||
- モバイルの Wake Lock:接続時に取得、切断/失敗で解放。バックグラウンド遷移による中断を低減。
|
||||
- エラー包みとリトライ:まれな `sendData failed` を捕捉し、表面化&再試行(`sendWithBackpressure` を参照)。
|
||||
|
||||
### 短い ID と長い ID の使い分け
|
||||
|
||||
- 短い ID(4 桁)は「空室で切断」時、バックエンドが 15 分(900s)の TTL に更新。猶予内は再接続が容易;https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119-L125。
|
||||
- 既定の部屋期限は 24 時間。空室切断のときのみ 15 分の一時保持に切替;https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/services/redis.ts#L6。
|
||||
- 長い ID(UUID 相当)はセッション横断・デバイス横断の再利用に向く。キャッシュ ID ボタンと組み合わせると最良。
|
||||
|
||||
---
|
||||
|
||||
## 触ってみる(クイックスタート)
|
||||
|
||||
デスクトップでの手早い体験:
|
||||
|
||||
1. 送信側で 8 文字以上の任意 ID を入力し、「ID を保存」をクリック。
|
||||
2. 受信側へ切り替え:条件を満たせば自動入力&即入室。
|
||||
3. 切断を再現(Wi‑Fi を切る、テザリングへ切替、リロード→戻る)して、自動復帰を観察。
|
||||
4. 送信側で「キャッシュ ID を使用」をダブルタップし、一時的に「ID を保存」に切替→新しい長い ID へ更新。
|
||||
|
||||
モバイル/弱い回線の場面:
|
||||
|
||||
- バックグラウンド→フォアグラウンド、Wi‑Fi とセルラーの切替。
|
||||
- 受信側の自動入室や、転送の自動再開を確認。
|
||||
|
||||
---
|
||||
|
||||
## 結びとお願い
|
||||
|
||||
“するり”とつながるほど、P2P の価値は増幅します。受信側のキャッシュ ID 自動入室と、スタック全体の再接続により、PrivyDrop は現実のネット環境でいっそう頑丈で頼れる存在になりました。
|
||||
|
||||
もし気に入っていただけたら、ぜひ GitHub で Star をお願いします(<u>https://github.com/david-bai00/PrivyDrop</u>)。見つけてもらいやすくなるだけでなく、私たちの磨き込みの原動力にもなります。
|
||||
|
||||
オンライン体験:<u>https://www.privydrop.app</u>。Issue から体験フィードバックや改善提案も歓迎します。“なめらかな体験”を、さらに厚くしていきましょう。
|
||||
|
||||
なお、ドメインは Cloudflare CDN による加速を有効化。地域間の速度と安定性が向上し、より多くのユーザーがストレスなくアクセスできます。
|
||||
|
||||
関連記事:
|
||||
|
||||
- [なぜ PrivyDrop をオープンソース化したのか](/blog/privydrop-open-source)
|
||||
- [WebRTC はどうやってブラウザ直送を実現するのか](/blog/webRTC-file-transfer)
|
||||
- [レジューム転送:大容量でも焦らない](/blog/resumable-transfers)
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: "원탭 재연결, 바위처럼 단단하게: PrivyDrop의 캐시 ID 자동 입장과 탄탄한 재연결 완전 해부"
|
||||
description: "받는 쪽 캐시 ID 자동 입장과 전 구간 재연결로 흐름이 더 매끄럽게—자동 입장, 한 번 탭으로 즉시 연결, 두 번 탭으로 캐시 갱신, 불안정한 네트워크에서도 끈질긴 복구."
|
||||
date: "2025-11-25"
|
||||
author: "david bai"
|
||||
cover: "/blog-assets/cached-id-reconnect.webp"
|
||||
tags: ["신기능", "자동 재연결", "캐시된 ID", "WebRTC", "P2P"]
|
||||
status: "published"
|
||||
---
|
||||
|
||||

|
||||
|
||||
## 소개: 왜 “자동 입장”과 “재연결”인가
|
||||
|
||||
PrivyDrop을 처음 쓰면 자주 마주치는 두 가지 작은 마찰이 있습니다.
|
||||
|
||||
- 발신 → 수신으로 바꿀 때마다 방 ID를 다시 붙여넣어야 함
|
||||
- 카페 Wi‑Fi나 모바일 네트워크에서 잠깐 끊기면 직접 다시 연결해야 함
|
||||
|
||||
작지만, 현실 네트워크에서는 자주 일어납니다. 그리고 “손맛”을 좌우합니다. 그래서 흐름을 정말 부드럽게 만드는 두 가지 개선을 넣었습니다.
|
||||
|
||||
- 수신 측 “캐시 ID 자동 입장”: 조건이 맞으면 자동으로 입력하고 곧바로 입장
|
||||
- 전체 경로 “탄탄한 재연결”: Socket/P2P 어느 쪽이 끊겨도 스스로 재협상/복구
|
||||
|
||||
무엇보다 우리의 아키텍처 레드라인은 그대로입니다. 백엔드는 신호와 방 관리만 담당하고, 파일은 E2E로 암호화된 상태로 브라우저끼리 직접 전송됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 기능 1: 수신 측 캐시 ID 자동 입장
|
||||
|
||||
수신 탭으로 전환했을 때 아래 조건을 만족하면, 마지막으로 저장된 방 ID를 자동으로 채우고 즉시 입장합니다.
|
||||
|
||||
- 현재 수신 탭이고 아직 방에 들어가지 않았음
|
||||
- URL에 `roomId` 파라미터가 없음(주소가 우선, 덮어쓰지 않음)
|
||||
- 입력 칸이 비어 있음(사용자 입력을 덮어쓰지 않음)
|
||||
- localStorage에 캐시 ID가 존재함
|
||||
|
||||
이 로직은 탭 전환 때 트리거됩니다. 조건이 맞으면 입력 칸을 채운 뒤 바로 입장 로직을 호출하여, 붙여넣기/클릭을 한 번 줄입니다.
|
||||
|
||||
- 코드 앵커:
|
||||
- 수신 측 자동 입장 useEffect: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp.tsx#L151
|
||||
- 캐시 유틸(localStorage): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/roomIdCache.ts#L1
|
||||
|
||||
다음 상황에서는 동작하지 않습니다.
|
||||
|
||||
- 이미 방에 들어가 있음
|
||||
- URL에 `roomId`가 명시됨(공유 링크 등)
|
||||
- 입력 칸에 이미 텍스트가 있고 편집 중임
|
||||
- 캐시 ID가 없음
|
||||
|
||||
---
|
||||
|
||||
## 기능 2: 발신 측 “저장/사용” 버튼(더블 탭으로 업데이트)
|
||||
|
||||
발신 측 방 ID 입력란에 똑똑한 “재사용” 버튼을 추가했습니다. 두 가지 상태를 오갑니다.
|
||||
|
||||
- ID 저장: 입력 길이가 8자 이상이면 활성화. 클릭 시 현재 입력을 캐시 ID로 저장
|
||||
- 캐시 ID 사용: 캐시가 있으면 한 번 탭으로 입력란에 채우고 곧바로 입장. 두 번 탭하면 약 3초간 “ID 저장”으로 잠시 전환되어 캐시를 업데이트할 수 있음
|
||||
|
||||
구현 노트:
|
||||
|
||||
- 단/복 탭은 400ms 윈도우로 판별하며, 컴포넌트 언마운트 시 타이머를 정리
|
||||
- “캐시 ID 사용” 후에는 발신 측이 즉시 입장(추가 “입장” 클릭 불필요)
|
||||
- 8자 미만은 저장 불가로 하여, 짧은 ID 오저장을 방지
|
||||
|
||||
- 코드 앵커:
|
||||
- 단/복 탭과 타이머 정리: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/CachedIdActionButton.tsx#L112
|
||||
- “캐시 ID 사용” 시 즉시 입장(발신 측): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/SendTabPanel.tsx#L193
|
||||
|
||||
---
|
||||
|
||||
## 재연결: 감지부터 완전 복구까지
|
||||
|
||||
세 가지 지점에서 끊김을 감지하고 재연결을 시도합니다.
|
||||
|
||||
- Socket 끊김: 재연결 후 `socketId`가 바뀌면 자동 재입장
|
||||
- P2P 끊김/실패/종료: 상태를 표시하고 연결 재구성을 시도
|
||||
- `socketId` 변경의 능동 확인: 소켓 복구 시 한 번 더 검증
|
||||
|
||||
- 코드 앵커:
|
||||
- 소켓 연결 후 자동 재입장: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L121
|
||||
- attemptReconnection 통합 진입점: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L185
|
||||
- `lastJoinedSocketId` 기록 및 필요 시 `initiator-online` 트리거: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L460
|
||||
- 발신 측의 `recipient-ready` 처리 및 재협상 시작: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L12
|
||||
- 수신 측의 `initiator-online` 응답(`recipient-ready` 전송): https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Recipient.ts#L14
|
||||
- 백엔드 신호 릴레이:
|
||||
- ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L63
|
||||
- initiator-online: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L102
|
||||
- recipient-ready: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L108
|
||||
- peer-disconnected: https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119
|
||||
|
||||
### 시퀀스(Mermaid)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant S as Signaling Server
|
||||
participant A as 발신자(initiator)
|
||||
participant B as 수신자(recipient)
|
||||
|
||||
Note over A,B: 네트워크 흔들림으로 Socket/P2P 끊김
|
||||
A->>A: attemptReconnection()
|
||||
A->>S: join(roomId) / (필요 시) initiator-online
|
||||
S-->>B: initiator-online
|
||||
B->>S: recipient-ready(peerId)
|
||||
S-->>A: recipient-ready(peerId)
|
||||
A->>B: offer
|
||||
B->>A: answer
|
||||
A-->>B: ICE candidates
|
||||
B-->>A: ICE candidates
|
||||
Note over A,B: 연결 복구, DataChannel 재수립
|
||||
```
|
||||
|
||||
### 신뢰성 디테일
|
||||
|
||||
- ICE 후보 큐: 원격 설명이 준비되지 않았거나 연결이 종료 단계면 후보를 큐에 담아 두었다가 나중에 한 번에 반영; https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L219-L256.
|
||||
- DataChannel 배압과 청킹: 발신 측 임계치 `bufferedAmountLowThreshold=256KB` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L82); 네트워크 제어 `maxBuffer≈3MB / lowThreshold≈512KB / 64KB 청크` (https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L66-L111, https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L160-L210).
|
||||
- 모바일 Wake Lock: 연결 시 획득, 끊김/실패 시 해제 — 백그라운드 전환으로 인한 중단을 줄임.
|
||||
- 에러 래핑과 재시도: 드물게 발생하는 `sendData failed` 경로를 포착/표면화/재시도(`sendWithBackpressure` 참고).
|
||||
|
||||
### 짧은 ID와 긴 ID 재사용 전략
|
||||
|
||||
- 짧은 ID(4자리)는 “빈 방 + 끊김” 시 백엔드가 TTL을 15분(900s)으로 갱신 — 그 창에서 빠르게 재연결 가능; https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119-L125.
|
||||
- 기본 방 만료는 24시간; “빈 방 + 끊김”에 한해 일시적으로 15분 보존으로 전환; https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/services/redis.ts#L6.
|
||||
- 긴 ID(UUID 유사)는 세션/디바이스를 넘어 재사용에 유리 — 캐시 ID 버튼과 함께 쓰면 가장 편함.
|
||||
|
||||
---
|
||||
|
||||
## 바로 써보기 (Hands‑on)
|
||||
|
||||
데스크톱 빠른 체험:
|
||||
|
||||
1. 발신 측에서 8자 이상 사용자 지정 ID를 입력하고 “ID 저장” 클릭
|
||||
2. 수신 측으로 전환: 조건이 맞으면 자동 채움 후 즉시 입장
|
||||
3. 끊김 시나리오 시뮬레이션(Wi‑Fi 끄기, 핫스팟 전환, 새로고침 후 복귀) → 자동 재연결 관찰
|
||||
4. 발신 측 “캐시 ID 사용” 더블 탭 → 잠시 “ID 저장”으로 전환 → 새 긴 ID로 갱신
|
||||
|
||||
모바일/열악한 네트워크:
|
||||
|
||||
- 백그라운드 ↔ 포그라운드 전환, Wi‑Fi ↔ 셀룰러 전환
|
||||
- 수신 측 자동 입장과 전송 자동 복구 동작 확인
|
||||
|
||||
---
|
||||
|
||||
## 맺음말 & 액션
|
||||
|
||||
연결이 매끄러울수록 P2P의 가치는 커집니다. 캐시 ID 자동 입장과 전 구간 재연결로, PrivyDrop은 현실 네트워크에서 더 튼튼하고 믿을 만해졌습니다.
|
||||
|
||||
유용했다면 GitHub 별을 부탁드립니다(<u>https://github.com/david-bai00/PrivyDrop</u>). 더 많은 사람이 발견할 수 있고, 저희가 계속 다듬어 가는 동력이 됩니다.
|
||||
|
||||
온라인 체험: <u>https://www.privydrop.app</u>. Issue로 사용 소감과 제안을 남겨 주세요. “부드러운 경험”을 더 두텁게 만들어가겠습니다.
|
||||
|
||||
덧붙여, 도메인은 Cloudflare CDN 가속을 사용합니다. 지역 간 속도와 안정성이 크게 향상되어, 더 많은 지역에서 끊김 없이 접속할 수 있습니다.
|
||||
|
||||
더 읽기:
|
||||
|
||||
- [내가 PrivyDrop을 오픈 소스로 공개한 이유](/blog/privydrop-open-source)
|
||||
- [WebRTC가 브라우저 직결 전송을 구현하는 방법](/blog/webRTC-file-transfer)
|
||||
- [중단 후 재개 전송: 대용량 전송의 불안을 넘어](/blog/resumable-transfers)
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
---
|
||||
title: "一键复连,稳如磐石:PrivyDrop 接收端缓存ID自动连接与断线重连全解析"
|
||||
description: "新增接收端缓存ID自动连接与断线重连,让连接更顺滑:自动入房、单击直连、双击更新缓存、弱网稳复连。"
|
||||
date: "2025-11-25"
|
||||
author: "david bai"
|
||||
cover: "/blog-assets/cached-id-reconnect.webp"
|
||||
tags: [新功能, 断线重连, 缓存ID, WebRTC, P2P]
|
||||
status: "published"
|
||||
---
|
||||
|
||||

|
||||
|
||||
## 引言:为什么我们要做“自动入房”和“断线重连”
|
||||
|
||||
很多第一次使用 PrivyDrop 的用户,都会经历这样两件“小事”:
|
||||
|
||||
- 从发送端切到接收端时,需要再粘贴一次房间 ID;
|
||||
- 在咖啡店 Wi‑Fi 或移动网络下,短暂断网就要手动重连。
|
||||
|
||||
这两件“小事”,在真实世界里却非常高频,也直接决定了“顺不顺手”。为此,我们上线了两项把体验打磨到“顺滑”的改进:
|
||||
|
||||
- 接收端“缓存 ID 自动连接”:满足条件时,自动填充并直接入房;
|
||||
- 全链路“断线重连”:Socket/P2P 任一断开,均自动恢复协商与连接。
|
||||
|
||||
更重要的是,这些改进不改变我们的架构红线:后端只做信令与房间管理,文件数据始终端到端加密,浏览器之间直传。
|
||||
|
||||
---
|
||||
|
||||
## 功能一:接收端缓存 ID 自动连接
|
||||
|
||||
当你切换到“接收”面板,如果满足以下条件,将自动填充上次保存的房间 ID 并直接入房:
|
||||
|
||||
- 当前处于接收面板,且尚未在房间内;
|
||||
- URL 未携带 `roomId` 参数(URL 优先,不做覆盖);
|
||||
- 输入框当前为空(不覆盖用户已有输入);
|
||||
- 本地存在缓存 ID(localStorage)。
|
||||
|
||||
上述逻辑在切换面板时触发,一旦命中,会先填充输入框,再立即调用加入逻辑,减少一次粘贴/点击。
|
||||
|
||||
- 代码锚点:
|
||||
- 前端自动入房 useEffect(接收端):[frontend/components/ClipboardApp.tsx#L151](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp.tsx#L151)
|
||||
- 缓存工具(localStorage):[frontend/lib/roomIdCache.ts#L1](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/roomIdCache.ts#L1)
|
||||
|
||||
何时不会触发?
|
||||
|
||||
- 你已在房间中;
|
||||
- URL 显式携带了 `roomId`(例如分享链接带参直达);
|
||||
- 输入框里已有你正在编辑的 ID;
|
||||
- 本地没有缓存 ID。
|
||||
|
||||
---
|
||||
|
||||
## 功能二:发送端“保存/使用缓存 ID”(支持双击更新)
|
||||
|
||||
发送端的房间 ID 输入区新增了一个“复用按钮”,在两种状态间智能切换:
|
||||
|
||||
- 保存 ID:当输入长度 ≥ 8 位时按钮可用,点击后将当前输入保存为缓存 ID;
|
||||
- 使用缓存 ID:若存在缓存 ID,单击即将其写入输入框并立刻入房;双击会“短暂切换”为“保存 ID”,便于你替换更新缓存(约 3 秒后恢复)。
|
||||
|
||||
实现要点:
|
||||
|
||||
- 单/双击通过 400ms 窗口配合计时器实现,并在组件卸载时清理;
|
||||
- “使用缓存 ID”单击后,发送端会立即加入房间(无需再点“加入”);
|
||||
- 输入长度不足 8 位时不会允许保存,避免误存短 ID。
|
||||
|
||||
- 代码锚点:
|
||||
- 单/双击与计时器清理:[frontend/components/ClipboardApp/CachedIdActionButton.tsx#L112](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/CachedIdActionButton.tsx#L112)
|
||||
- 使用缓存 ID 后立刻直连(发送端):[frontend/components/ClipboardApp/SendTabPanel.tsx#L193](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/components/ClipboardApp/SendTabPanel.tsx#L193)
|
||||
|
||||
---
|
||||
|
||||
## 断线重连:从检测到恢复的全链路
|
||||
|
||||
我们从三个入口观测“断开”并触发重连:
|
||||
|
||||
- Socket 断开:重连后若 `socketId` 变化,将自动重新入房;
|
||||
- P2P 断开/失败/关闭:标记状态并尝试重建连接;
|
||||
- 主动判断 `socketId` 变化:在 socket 连接恢复时复核一次。
|
||||
|
||||
- 代码锚点:
|
||||
- Socket 连接后自动重入房:[frontend/lib/webrtc_base.ts#L121](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L121)
|
||||
- 尝试重连的统一入口:[frontend/lib/webrtc_base.ts#L185](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L185)
|
||||
- 记录 `lastJoinedSocketId` 并在需要时触发 `initiator-online`:[frontend/lib/webrtc_base.ts#L460](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L460)
|
||||
- 发送端接收 `recipient-ready` 并重启协商:[frontend/lib/webrtc_Initiator.ts#L12](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L12)
|
||||
- 接收端响应 `initiator-online` 并发送 `recipient-ready`:[frontend/lib/webrtc_Recipient.ts#L14](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Recipient.ts#L14)
|
||||
- 后端信令转发:
|
||||
- ready:[backend/src/socket/handlers.ts#L63](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L63)
|
||||
- initiator-online:[backend/src/socket/handlers.ts#L102](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L102)
|
||||
- recipient-ready:[backend/src/socket/handlers.ts#L108](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L108)
|
||||
- peer-disconnected:[backend/src/socket/handlers.ts#L119](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119)
|
||||
|
||||
### 时序(Mermaid)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant S as Signaling Server
|
||||
participant A as 发送端(initiator)
|
||||
participant B as 接收端(recipient)
|
||||
|
||||
Note over A,B: 网络波动导致 Socket/P2P 断开
|
||||
A->>A: attemptReconnection()
|
||||
A->>S: join(roomId) / (可能) initiator-online
|
||||
S-->>B: initiator-online
|
||||
B->>S: recipient-ready(peerId)
|
||||
S-->>A: recipient-ready(peerId)
|
||||
A->>B: offer
|
||||
B->>A: answer
|
||||
A-->>B: ICE candidates
|
||||
B-->>A: ICE candidates
|
||||
Note over A,B: 连接恢复,DataChannel 重建
|
||||
```
|
||||
|
||||
### 可靠性细节
|
||||
|
||||
- ICE 候选队列:若远端描述尚未建立或连接处于关闭态,候选会入队,待可用时批量补交;见 [frontend/lib/webrtc_base.ts#L219-L256](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_base.ts#L219-L256)。
|
||||
- DataChannel 背压与分片:发送端阈值 `bufferedAmountLowThreshold=256KB`([frontend/lib/webrtc_Initiator.ts#L82](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/webrtc_Initiator.ts#L82));网络发送控制 `maxBuffer≈3MB / lowThreshold≈512KB / 64KB 分片`([frontend/lib/transfer/NetworkTransmitter.ts#L66-L111](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L66-L111)、[frontend/lib/transfer/NetworkTransmitter.ts#L160-L210](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/frontend/lib/transfer/NetworkTransmitter.ts#L160-L210))。
|
||||
- 移动端唤醒锁:连接建立时申请 Wake Lock,断开/失败时释放,降低切后台导致的意外中断;
|
||||
- 错误兜底与重试:小概率 `sendData failed` 会被包装、上报与重试(详见 `sendWithBackpressure` 相关逻辑)。
|
||||
|
||||
### 短 ID 与长 ID 的复用策略
|
||||
|
||||
- 短 ID(4 位)在“空房断开”后,会由后端将房间 TTL 刷新为 15 分钟(900s),窗口期内可直接重连,超时回收;见 [backend/src/socket/handlers.ts#L119-L125](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/socket/handlers.ts#L119-L125)。
|
||||
- 默认房间过期时间为 24 小时,仅在空房断开发生临时 15 分钟保留;见 [backend/src/services/redis.ts#L6](https://github.com/david-bai00/PrivyDrop/blob/v1.1.1/backend/src/services/redis.ts#L6)。
|
||||
- 长 ID(如 UUID 级别长度)更适合跨会话、跨设备的持续复用;与缓存 ID 按钮配合使用体验最佳。
|
||||
|
||||
---
|
||||
|
||||
## 如何体验(上手指南)
|
||||
|
||||
桌面端快速体验:
|
||||
|
||||
1. 在发送端输入一个 ≥8 位的自定义 ID,点击“保存 ID”;
|
||||
2. 切换到接收端:若满足条件,将自动填充并直接入房;
|
||||
3. 模拟断线(如:关闭 Wi‑Fi、切到手机热点、刷新页面再返回),观察自动重连;
|
||||
4. 在发送端双击“使用缓存 ID”,短暂切换为“保存 ID”,更新为新的长 ID。
|
||||
|
||||
移动端/弱网场景:
|
||||
|
||||
- 切后台 → 回前台;Wi‑Fi ↔ 蜂窝之间切换;
|
||||
- 关注接收端是否自动入房、传输是否自动恢复。
|
||||
|
||||
---
|
||||
|
||||
## 结语与行动号召
|
||||
|
||||
我们相信,越“顺手”的连接,越能放大 P2P 的价值。接收端缓存 ID 自动连接与断线重连,让 PrivyDrop 在真实网络环境下更加稳健、可依赖。
|
||||
|
||||
如果觉得好用,请到 GitHub 给我们一个 Star(<u>https://github.com/david-bai00/PrivyDrop</u>),便于更多人发现与受益;你的 Star 也会直接影响搜索与推荐权重,是我们持续打磨产品的动力。
|
||||
|
||||
在线体验:<u>https://www.privydrop.app</u>。也欢迎在 Issue 中反馈你的使用体验与改进建议,让我们把“顺滑体验”继续做厚。
|
||||
|
||||
另外,域名已启用 Cloudflare CDN 加速(赛博菩萨),显著提升跨区域的访问速度与稳定性,让更多地区用户打开网站不再卡顿,整体体验更流畅。
|
||||
|
||||
延伸阅读:
|
||||
|
||||
- [我为什么开源了 PrivyDrop](/blog/privydrop-open-source)
|
||||
- [WebRTC 如何实现浏览器直传](/blog/webRTC-file-transfer)
|
||||
- [断点续传:让大文件传输告别焦虑](/blog/resumable-transfers)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
Reference in New Issue
Block a user