Ga naar inhoud
HexaTransfer
Terug naar blog
Technische verdiepingen

WebRTC Bestandsoverdracht: Browser-to-Browser Tutorial

Draag over bestanden directly between browsers using WebRTC. Data channels, signaling, en peer connection setup voor real-time bestand sharing.

WebRTC-bestandsoverdracht verplaatst bytes rechtstreeks tussen twee browsers via een RTCDataChannel, zonder dat een server in het datapad zit na de eerste handshake. Dat is een privacy-voordeel dat de AVG expliciet aanmoedigt: wanneer gegevens de server van een verwerker nooit bereiken, is het verwerkingsrisico minimaal. De benodigde onderdelen: een signaleringskanaal (WebSocket of een kleine relay) om SDP-aanbiedingen en ICE-kandidaten uit te wisselen, STUN-servers voor NAT-detectie, een TURN-terugval voor symmetrische NAT's, en een datakanaal geconfigureerd voor betrouwbare, geordende bezorging. Zodra de peer-verbinding tot stand is gebracht, verzendt u stukken van 16-256 KB via channel.send() over een met DTLS 1.2 versleutelde SCTP-associatie, op ruwweg lijnsnelheid.

Hoe WebRTC werkelijk peer-to-peer tot stand brengt

WebRTC is geen magie — het is ICE plus SDP plus DTLS plus SCTP gestapeld op elkaar. De verzender maakt een RTCPeerConnection aan, opent een datakanaal, genereert een SDP-aanbieding en stuurt die via uw signaleringskanaal naar de ontvanger. De ontvanger antwoordt. Beide zijden wisselen vervolgens ICE-kandidaten uit (lokaal IP, reflexief IP via STUN, relay-IP via TURN) totdat ze een werkend pad vinden. DTLS 1.2 handshakt end-to-end, SCTP draait erop voor betrouwbare streaming en uw bytes beginnen te stromen.

De versleuteling is verplicht en ingebouwd. U kunt er niet van afzien. Dat is een zinvol beveiligingsvoordeel — anders dan WebSockets hoeft u niet te onthouden om iets in applicatielaagcrypto te wikkelen om afluisteren op het netwerk te voorkomen. Voor echte end-to-end encryptie tegen uw eigen signaleringsserver legt u een tweede ronde AES-256-GCM bovenop, want een kwaadaardige signaleringsserver kan zijn eigen DTLS-certificaat omwisselen.

Signalering: het deel dat WebRTC niet definieert

WebRTC laat signalering bewust aan u over. Een WebSocket op uw server volstaat; een gedeelde ruimtecode gepost naar een Firebase Realtime Database ook, of zelfs handmatig geplakte SDP-strings. Wat telt is dat beide peers uiteindelijk een aanbieding, een antwoord en een stroom ICE-kandidaten uitwisselen.

Minimale signaleringsserver in Node:

const rooms = new Map();
wss.on('connection', (ws) => {
  ws.on('message', (raw) => {
    const msg = JSON.parse(raw);
    if (msg.type === 'join') {
      const room = rooms.get(msg.room) ?? new Set();
      room.add(ws); rooms.set(msg.room, room);
    } else {
      for (const peer of rooms.get(msg.room) ?? []) {
        if (peer !== ws) peer.send(raw);
      }
    }
  });
});

Minder dan twintig regels. De server ziet nooit bestandsbytes — alleen SDP- en ICE-metadata. U kunt dit hosten op een VPS van vijf euro of Cloudflare Workers en honderden gelijktijdige overdrachten bedienen.

De peer-verbinding opzetten

Maak de verbinding aan met Google's publieke STUN plus een TURN-terugval:

const pc = new RTCPeerConnection({
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { urls: 'turn:turn.example.com:3478',
      username: 'gebruiker', credential: 'wachtwoord' }
  ]
});

Circa 15-25% van residentiële verbindingen zit achter symmetrische NAT's die STUN niet kan doorboren, dus TURN is niet optioneel voor een productieservice. Draai coturn op een VPS met voldoende bandbreedte voor gerelayed verkeer, of betaal voor een beheerde TURN-provider zoals Xirsys of Twilio.

Open het datakanaal aan de aanbiederskant vóór het aanmaken van de aanbieding:

const channel = pc.createDataChannel('file', {
  ordered: true, maxRetransmits: null
});
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signaling.send({ type: 'offer', sdp: offer.sdp });

Geordend + onbeperkte herhalingen geeft u TCP-gelijkwaardige betrouwbaarheid. Ongeordende modus is sneller maar vereist herassemblage op applicatieniveau.

Het bestand opdelen voor het kanaal

SCTP heeft een praktische limiet van 256 KB per bericht, en oudere browsers hebben moeite boven 16 KB. Een veilige standaard zijn stukken van 16 KB gelezen uit het bestand via File.slice():

const chunkSize = 16 * 1024;
let offset = 0;
channel.bufferedAmountLowThreshold = 1024 * 1024;
function sendNext() {
  while (offset < file.size && channel.bufferedAmount < 4 * 1024 * 1024) {
    const chunk = file.slice(offset, offset + chunkSize);
    chunk.arrayBuffer().then((buf) => channel.send(buf));
    offset += chunkSize;
  }
}
channel.onbufferedamountlow = sendNext;
sendNext();

Het bufferedAmount-watermerk voorkomt dat u gigabytes in de verzendstack van SCTP laadt en het geheugen uitput. Wanneer de buffer onder 1 MB daalt, vult u aan tot 4 MB. Dit geeft een doorvoer van ruwweg 40-80 MB/s op lokale netwerken en 5-20 MB/s op typisch residentieel breedband.

Bytes ontvangen en naar schijf schrijven

Luister op de beantwoordende zijde naar het inkomende kanaal:

pc.ondatachannel = ({ channel }) => {
  const chunks = [];
  let received = 0;
  channel.onmessage = ({ data }) => {
    chunks.push(data);
    received += data.byteLength;
    updateProgress(received);
    if (received === expectedSize) finish(chunks);
  };
};

Voor bestanden groter dan 500 MB stapelt u niet op in RAM. Gebruik de File System Access API om rechtstreeks naar schijf te streamen:

const handle = await window.showSaveFilePicker({ suggestedName: fileName });
const writable = await handle.createWritable();
channel.onmessage = async ({ data }) => writable.write(data);

Firefox en Safari ondersteunen showSaveFilePicker nog niet, dus val terug op een Blob + URL.createObjectURL-download voor die browsers, begrensd tot 2 GB.

Bestandsmetadata versturen voor de bytes

De ontvanger heeft vóór de bytestream bestandsnaam, grootte en MIME-type nodig. Gebruik een kleine JSON-handshake op het datakanaal:

channel.send(JSON.stringify({
  type: 'metadata', name: file.name,
  size: file.size, mime: file.type, sha256: fileHash
}));

Schakel dan over naar binaire modus. De ontvanger wisselt op basis van of data een string of ArrayBuffer is. Voeg een SHA-256 van het bestand toe voor integriteitsverificatie na de overdracht, en optioneel een sleutelvingerafdruk als u applicatieniveau AES-256-GCM bovenop legt.

Verbindingsfouten afhandelen

WebRTC-datakanalen mislukken op drie manieren: ICE voltooit zich nooit (NAT, firewallblokkering), de DTLS-handshake mislukt (klokverschil, certificaatproblemen), of de verbinding valt weg midden in een overdracht (laptop in slaapstand, netwerkwissel). Luister op pc.oniceconnectionstatechange en reageer op 'failed' of 'disconnected'. Chrome wacht enkele seconden bij 'disconnected' voor het naar 'failed' gaat; Safari is minder geduldig.

Bij een mislukking midden in een overdracht herstart u ICE zonder de gehele verbinding opnieuw op te bouwen:

await pc.restartIce();
const offer = await pc.createOffer({ iceRestart: true });
// opnieuw sturen via signalering

Als de herstart mislukt, val dan terug op hervattbare upload via uw server — een hybride P2P + server-architectuur. Sommige WebRTC-overdrachtstools gebruiken dit patroon omdat het de 15% netwerkcondities afhandelt waarbij zuiver P2P simpelweg niet werkt.

End-to-end encryptie toevoegen tegen uw eigen server

WebRTC's ingebouwde DTLS beschermt tegen netwerkaanvallers maar niet tegen een kwaadaardige of gecompromitteerde signaleringsserver. Voor echte end-to-end encryptie laten beide peers een ECDH P-256-sleutelpaar genereren, wisselen ze openbare sleutels uit via een korte out-of-band-code (QR of een passphrase van zes woorden), leiden ze een gedeeld geheim af via HKDF-SHA256 en versleutelen ze elk datakanaalsbericht met AES-256-GCM voor de send-aanroep. Zo kan de signaleringsserver uw bestanden niet lezen, zelfs als hij DTLS-certificaten omwisselt.

HexaTransfer gebruikt een hybride architectuur — ciphertekst opgeslagen aan de serverzijde met client-side AES-256-GCM — waarbij directheid wordt ingeruild voor offline-tolerant delen. Probeer het op https://hexatransfer.com — gratis, geen account vereist, maximaal 10 GB.

Waar WebRTC wint en verliest

WebRTC-bestandsoverdracht schittert wanneer beide peers tegelijk online zijn, wanneer privacy van uw eigen server telt, en wanneer bestanden groot genoeg zijn (100 MB+) dat relaybandbreedte pijnlijk duur zou zijn. Het verliest wanneer gebruikers willen verzenden en weglopen, wanneer ontvangers de link uren later openen, of wanneer ontvangers achter restrictieve zakelijke netwerken zitten die STUN en TURN blokkeren. Voor een algemeen overdrachtsgereedschap dekt zuiver P2P misschien 60% van de gebruiksgevallen comfortabel. De overige 40% heeft een server-gebaseerde terugval nodig — precies de reden waarom bijna elk "P2P-bestandsoverdracht"-product ergens een relay in de architectuur heeft.

Verstuur grote bestanden veilig met end-to-end-versleuteling

Draag bestanden tot 10 GB gratis over met end-to-end-versleuteling. Geen account nodig. Uw bestanden worden in uw browser versleuteld voordat ze worden geüpload — niemand anders kan ze lezen.

Een bestand verzenden