Hoe WebTransport te gebruiken

Gepubliceerd: 8 juni 2020

WebTransport is een web-API die het HTTP/3 -protocol gebruikt als bidirectioneel transport. Het is bedoeld voor tweewegcommunicatie tussen een webclient en een HTTP/3-server. Het ondersteunt het verzenden van gegevens zowel onbetrouwbaar met de datagram-API's als betrouwbaar met de stream-API's .

Datagrammen zijn ideaal voor het verzenden en ontvangen van gegevens die geen sterke leveringsgaranties vereisen. De grootte van individuele datapakketten wordt beperkt door de maximale transmissie-eenheid (MTU) van de onderliggende verbinding. Het is niet gegarandeerd dat ze succesvol worden verzonden en dat ze, indien wel verzonden, in een willekeurige volgorde aankomen. Deze eigenschappen maken de datagram-API's ideaal voor dataoverdracht met lage latentie en een 'best-effort'-aanpak. Je kunt datagrammen zien als UDP-berichten (User Datagram Protocol) , maar dan versleuteld en met congestiebeheersing.

De stream-API's bieden daarentegen een betrouwbare , geordende gegevensoverdracht. Ze zijn zeer geschikt voor scenario's waarin u een of meer streams met geordende gegevens moet verzenden of ontvangen. Het gebruik van meerdere WebTransport-streams is vergelijkbaar met het opzetten van meerdere TCP- verbindingen, maar omdat HTTP/3 intern gebruikmaakt van het lichtere QUIC- protocol, kunnen ze zonder al te veel overhead worden geopend en gesloten.

Gebruiksvoorbeelden

Dit is een kleine lijst met mogelijke manieren waarop ontwikkelaars WebTransport zouden kunnen gebruiken.

  • Het versturen van de spelstatus met regelmatige tussenpozen en minimale latentie naar een server in kleine, onbetrouwbare en niet-volgorde berichten.
  • Het ontvangen van mediastromen die met minimale latentie vanaf een server worden verzonden, onafhankelijk van andere datastromen.
  • Het ontvangen van meldingen die vanaf een server worden verzonden terwijl een webpagina open is.

We willen graag meer horen over hoe u WebTransport wilt gebruiken.

Browserondersteuning

Browser Support

  • Chrome: 97.
  • Rand: 97.
  • Firefox: 114.
  • Safari: 26.4.

Source

Zoals bij alle functies die geen universele browserondersteuning hebben, raden we aan om functiedetectie toe te voegen.

Relatie tot andere technologieën

Is WebTransport een vervanging voor WebSockets?

Misschien. Er zijn situaties waarin zowel WebSockets als WebTransport geschikte communicatieprotocollen zouden kunnen zijn.

WebSocket-communicatie is gebaseerd op een enkele, betrouwbare, geordende stroom berichten, wat prima is voor bepaalde communicatiebehoeften. Als u die eigenschappen nodig hebt, kunnen de stream-API's van WebTransport die ook bieden. De datagram-API's van WebTransport bieden daarentegen levering met lage latentie, zonder garanties over betrouwbaarheid of volgorde, en zijn daarom geen directe vervanging voor WebSockets.

Bij gebruik van WebTransport, met de datagram-API's of meerdere gelijktijdige Streams-API-instanties, hoeft u zich geen zorgen te maken over head-of-line blocking , wat een probleem kan zijn bij WebSockets. Bovendien zijn er prestatievoordelen bij het tot stand brengen van nieuwe verbindingen, omdat de onderliggende QUIC-handshake sneller is dan het opstarten van TCP via TLS.

WebTransport maakt deel uit van een nieuwe conceptspecificatie, waardoor het WebSocket-ecosysteem rond client- en serverbibliotheken veel robuuster is geworden. Als je iets nodig hebt dat direct werkt met gangbare serverconfiguraties en brede ondersteuning biedt voor webclients, dan is WebSocket momenteel een betere keuze.

Is WebTransport hetzelfde als een UDP Socket API?

Nee, WebTransport is geen UDP-socket-API . Hoewel WebTransport HTTP/3 gebruikt, dat op zijn beurt "onder de motorkap" UDP gebruikt, stelt WebTransport eisen aan encryptie en congestiebeheer waardoor het meer is dan een eenvoudige UDP-socket-API.

Is WebTransport een alternatief voor WebRTC-datakanalen?

Ja, voor client-serververbindingen. WebTransport deelt veel van dezelfde eigenschappen met WebRTC-datakanalen , hoewel de onderliggende protocollen verschillen.

Over het algemeen vereist het draaien van een HTTP/3-compatibele server minder installatie en configuratie dan het onderhouden van een WebRTC-server, waarbij kennis van meerdere protocollen ( ICE , DTLS en SCTP ) nodig is om een ​​werkend transport te realiseren. WebRTC omvat veel meer complexe onderdelen die kunnen leiden tot mislukte client/server-onderhandelingen.

De WebTransport API is ontworpen met de gebruiksscenario's van webontwikkelaars in gedachten en voelt meer aan als het schrijven van code voor moderne webplatformen dan het gebruik van de datakanaalinterfaces van WebRTC. In tegenstelling tot WebRTC wordt WebTransport ondersteund binnen Web Workers , waardoor je client-servercommunicatie kunt uitvoeren onafhankelijk van een specifieke HTML-pagina. Omdat WebTransport een Streams -compatibele interface biedt, ondersteunt het optimalisaties rondom backpressure .

Als je echter al een werkende WebRTC-client/serverconfiguratie hebt waar je tevreden mee bent, biedt overschakelen naar WebTransport mogelijk niet veel voordelen.

Experiment

De beste manier om met WebTransport te experimenteren is door een compatibele HTTP/3-server op te starten. Gebruik deze pagina met een eenvoudige JavaScript-client om de communicatie tussen client en server uit te proberen.

Daarnaast is er een door de community onderhouden echo-server beschikbaar op webtransport.day .

Gebruik de API

WebTransport is ontworpen op basis van moderne webplatformprimitieven, zoals de Streams API . Het maakt veelvuldig gebruik van promises en werkt goed samen met async en await .

De huidige WebTransport-implementatie in Chromium ondersteunt drie verschillende soorten verkeer: datagrammen, en zowel unidirectionele als bidirectionele streams.

Verbinden met een server

Je kunt verbinding maken met een HTTP/3-server door een WebTransport instantie aan te maken. Het schema van de URL moet https zijn. Je moet het poortnummer expliciet opgeven.

Je moet de ready promise gebruiken om te wachten tot de verbinding tot stand is gebracht. Deze belofte blijft onvervuld totdat de configuratie is voltooid en wordt afgewezen als de verbinding mislukt tijdens de QUIC/TLS-fase.

De closed belofte wordt nagekomen wanneer de verbinding normaal wordt verbroken en wordt afgewezen als de verbreking onverwacht was.

Als de server de verbinding weigert vanwege een foutmelding van de client (bijvoorbeeld omdat het URL-pad ongeldig is), dan wordt de verbinding ook geweigerd closed ), terwijl ready onopgelost blijft.

const url = 'https://example.com:4999/foo/bar';
const transport = new WebTransport(url);

// Optionally, set up functions to respond to
// the connection closing:
transport.closed.then(() => {
  console.log(`The HTTP/3 connection to ${url} closed gracefully.`);
}).catch((error) => {
  console.error(`The HTTP/3 connection to ${url} closed due to ${error}.`);
});

// Once .ready fulfills, the connection can be used.
await transport.ready;

Datagram API's

Zodra je een WebTransport-instantie hebt die is verbonden met een server, kun je deze gebruiken om afzonderlijke stukjes data, ook wel datagrammen genoemd, te verzenden en te ontvangen.

De writeable getter retourneert een WritableStream , die een webclient kan gebruiken om gegevens naar de server te sturen. De readable getter retourneert een ReadableStream , waarmee je gegevens van de server kunt ontvangen. Beide streams zijn inherent onbetrouwbaar, dus het is mogelijk dat de gegevens die je schrijft niet door de server worden ontvangen, en omgekeerd.

Beide typen streams gebruiken Uint8Array instanties voor gegevensoverdracht.

// Send two datagrams to the server.
const writer = transport.datagrams.writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);

// Read datagrams from the server.
const reader = transport.datagrams.readable.getReader();
while (true) {
  const {value, done} = await reader.read();
  if (done) {
    break;
  }
  // value is a Uint8Array.
  console.log(value);
}

Streams API's

Zodra je verbinding hebt gemaakt met de server, kun je WebTransport ook gebruiken om gegevens te verzenden en te ontvangen via de Streams API's.

Elk deel van alle streams is een Uint8Array . In tegenstelling tot de Datagram API's zijn deze streams betrouwbaar. Maar elke stream is onafhankelijk, dus de volgorde van de gegevens over de streams heen is niet gegarandeerd.

WebTransportSendStream

Een WebTransportSendStream wordt door de webclient aangemaakt met behulp van de createUnidirectionalStream() methode van een WebTransport instantie, die een promise voor de WebTransportSendStream retourneert.

Gebruik de close() methode van de WritableStreamDefaultWriter om de bijbehorende HTTP/3-stream te sluiten. De browser probeert alle nog in behandeling zijnde gegevens te verzenden voordat de bijbehorende stream daadwerkelijk wordt gesloten.

// Send two Uint8Arrays to the server.
const stream = await transport.createUnidirectionalStream();
const writer = stream.writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);
try {
  await writer.close();
  console.log('All data has been sent.');
} catch (error) {
  console.error(`An error occurred: ${error}`);
}

Gebruik op dezelfde manier de abort() methode van de WritableStreamDefaultWriter om een RESET_STREAM naar de server te sturen. Bij gebruik van abort() kan de browser alle nog niet verzonden gegevens negeren.

const ws = await transport.createUnidirectionalStream();
const writer = ws.getWriter();
writer.write(...);
writer.write(...);
await writer.abort();
// Not all the data may have been written.

WebTransportReceiveStream

De server initieert WebTransportReceiveStream . Het verkrijgen van een WebTransportReceiveStream is een proces in twee stappen voor een webclient. Eerst roept de client het attribuut incomingUnidirectionalStreams van een WebTransport instantie aan, wat een ReadableStream retourneert. Elk deel van die ReadableStream is op zijn beurt een WebTransportReceiveStream die kan worden gebruikt om Uint8Array instanties te lezen die door de server worden verzonden.

async function readFrom(receiveStream) {
  const reader = receiveStream.readable.getReader();
  while (true) {
    const {done, value} = await reader.read();
    if (done) {
      break;
    }
    // value is a Uint8Array
    console.log(value);
  }
}

const rs = transport.incomingUnidirectionalStreams;
const reader = rs.getReader();
while (true) {
  const {done, value} = await reader.read();
  if (done) {
    break;
  }
  // value is an instance of WebTransportReceiveStream
  await readFrom(value);
}

Je kunt het sluiten van een stream detecteren met behulp van de closed promise van de ReadableStreamDefaultReader . Wanneer de onderliggende HTTP/3-stream wordt gesloten met de `FIN`-bit , wordt de closed promise vervuld nadat alle gegevens zijn gelezen. Wanneer de HTTP/3-stream abrupt wordt gesloten (bijvoorbeeld door RESET_STREAM ), wordt de closed promise afgewezen.

// Assume an active receiveStream
const reader = receiveStream.readable.getReader();
reader.closed.then(() => {
  console.log('The receiveStream closed gracefully.');
}).catch(() => {
  console.error('The receiveStream closed abruptly.');
});

WebTransportBidirectionalStream

Een WebTransportBidirectionalStream kan zowel door de server als door de client worden aangemaakt.

Webclients kunnen er een maken met behulp van de createBidirectionalStream() methode van een WebTransport instantie, die een promise retourneert voor een WebTransportBidirectionalStream .

const stream = await transport.createBidirectionalStream();
// stream is a WebTransportBidirectionalStream
// stream.readable is a ReadableStream
// stream.writable is a WritableStream

Je kunt luisteren naar een WebTransportBidirectionalStream die door de server is aangemaakt met het attribuut incomingBidirectionalStreams van een WebTransport instantie, die een ReadableStream retourneert. Elk deel van die ReadableStream is op zijn beurt een WebTransportBidirectionalStream .

const rs = transport.incomingBidirectionalStreams;
const reader = rs.getReader();
while (true) {
  const {done, value} = await reader.read();
  if (done) {
    break;
  }
  // value is a WebTransportBidirectionalStream
  // value.readable is a ReadableStream
  // value.writable is a WritableStream
}

Een WebTransportBidirectionalStream is een combinatie van een WebTransportSendStream en WebTransportReceiveStream . De voorbeelden in de vorige twee secties leggen uit hoe je ze kunt gebruiken.

Polyfill

Er is een polyfill (of liever een ponyfill die functionaliteit biedt als een op zichzelf staande module die je kunt gebruiken) genaamd webtransport-ponyfill-websocket beschikbaar die een aantal functies van WebTransport implementeert. Lees de beperkingen in de README van het project zorgvuldig door om te bepalen of deze oplossing geschikt is voor jouw gebruikssituatie.

Privacy- en beveiligingsaspecten

Raadpleeg het betreffende gedeelte van de conceptspecificatie voor bindende richtlijnen.

Feedback

Is er iets aan de API dat onhandig is of niet werkt zoals verwacht? Of ontbreken er onderdelen die je nodig hebt om je idee te implementeren?

Uw publieke steun helpt Chrome bij het prioriteren van functies en laat andere browserleveranciers zien hoe belangrijk het is om deze te ondersteunen.

Algemene discussie

Voor algemene vragen of problemen die niet in een van de andere categorieën passen, kun je de Google-groep web-transport-dev gebruiken.

Dankbetuigingen

We hebben informatie uit de WebTransport Explainer en de conceptspecificatie verwerkt. Met dank aan de respectievelijke auteurs voor het leveren van die basis.