Signatur
Beschreibung
createUnidirectionalStream() öffnet über eine bestehende WebTransport-Verbindung (HTTP/3 bzw. QUIC) einen neuen unidirektionalen Stream. Nur der Client schreibt, der Server liest. Das zurückgegebene Promise wird mit einem WebTransportSendStream erfüllt. Dieser erbt von WritableStream, deshalb schreibt man die Daten wie gewohnt über einen WritableStreamDefaultWriter als Uint8Array.
Unidirektionale Streams sind zuverlässig und geordnet. Anders als bei Datagrammen kommen die Daten also vollständig und in der richtigen Reihenfolge an. Jeder Stream ist von den anderen unabhängig. Geht in einem Stream ein Paket verloren, bremst das die übrigen Streams nicht aus (kein Head-of-Line-Blocking). Deshalb passen sie gut für in sich abgeschlossene Nachrichten, zum Beispiel einzelne Spielzustände, Log-Einträge, Datei-Teile oder Telemetrie-Pakete, bei denen keine Antwort über denselben Stream nötig ist.
Über die Option sendOrder legst du fest, welcher Stream beim Senden bevorzugt wird. Streams mit höherem Wert werden zuerst bedient. Mit waitUntilAvailable bestimmst du, was passiert, wenn der Server gerade keine weiteren Streams zulässt (Flusskontrolle): Entweder wartet das Promise, bis wieder ein Stream frei ist, oder es wird sofort abgelehnt.
Die Methode darf erst aufgerufen werden, wenn die Verbindung steht. Warte also vorher auf transport.ready. Schließt du den Writer mit close(), erfährt der Server, dass der Stream zu Ende ist. Mit abort() brichst du den Stream mit einem Fehler ab.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | Object | {} | Optionales Konfigurationsobjekt mit den folgenden Eigenschaften. |
| $options.sendOrder | number | 0 | Sendepriorität des Streams. Streams mit höherem Wert werden beim Senden bevorzugt, bei gleichem Wert teilen sie sich die Bandbreite. Lässt sich später über stream.sendOrder ändern. |
| $options.waitUntilAvailable | boolean | false | Bei true wartet das Promise, bis der Server laut Flusskontrolle einen weiteren Stream erlaubt. Bei false wird es sofort abgelehnt, wenn gerade kein Stream verfügbar ist. |
| $options.sendGroup | WebTransportSendGroup | Optionale Sendegruppe, in der sendOrder relativ zu anderen Streams derselben Gruppe gilt. Neu im Standard und noch nicht überall umgesetzt. |
Rückgabewert
WebTransportSendStream (Unterklasse von WritableStream) erfüllt wird. Es wird mit einem InvalidStateError abgelehnt, wenn die Verbindung geschlossen oder fehlgeschlagen ist. Ohne waitUntilAvailable wird es auch abgelehnt, wenn gerade kein Stream verfügbar ist.Beispiele
Einfache Textnachricht über einen unidirektionalen Stream senden
const transport = new WebTransport('https://example.com:4433/wt');
await transport.ready;
const stream = await transport.createUnidirectionalStream();
const writer = stream.getWriter();
const encoder = new TextEncoder();
await writer.write(encoder.encode('Hallo Server!'));
// Stream sauber beenden – der Server erhält ein End-of-Stream
await writer.close();
console.log('Nachricht gesendet und Stream geschlossen');
Eine Nachricht pro Stream – mit Priorität über sendOrder
const transport = new WebTransport('https://game.example.com/session');
await transport.ready;
const encoder = new TextEncoder();
// Jede Nachricht in einem eigenen Stream: kein Head-of-Line-Blocking
const sendMessage = async (payload, priority = 0) => {
const stream = await transport.createUnidirectionalStream({
sendOrder: priority,
waitUntilAvailable: true
});
const writer = stream.getWriter();
await writer.write(encoder.encode(JSON.stringify(payload)));
await writer.close();
};
// Eingaben des Spielers haben Vorrang vor Telemetrie
await Promise.all([
sendMessage({ type: 'telemetry', fps: 60 }, 0),
sendMessage({ type: 'input', key: 'jump' }, 10)
]);
console.log('Alle Nachrichten übertragen');
Datei in Blöcken hochladen mit Fehlerbehandlung
const uploadFile = async (transport, file) => {
let writer;
try {
const stream = await transport.createUnidirectionalStream();
writer = stream.getWriter();
// Datei-Stream direkt in Blöcken lesen und senden
const reader = file.stream().getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
await writer.ready; // Gegendruck beachten
await writer.write(value);
}
await writer.close();
console.log(`${file.name} hochgeladen`);
} catch (err) {
console.error('Upload fehlgeschlagen:', err.name, err.message);
await writer?.abort(err).catch(() => {});
}
};
const transport = new WebTransport('https://upload.example.com/wt');
await transport.ready;
const input = document.querySelector('input[type=file]');
input.addEventListener('change', () => uploadFile(transport, input.files[0]));
// Wichtig · Fallstricke
- Erst auf
transport.readywarten: Rufst du die Methode vor dem Verbindungsaufbau auf, wartet sie in der Regel. Scheitert die Verbindung, wird das Promise abgelehnt. Nachtransport.close()oder einem Verbindungsfehler gibt es einenInvalidStateError. - Nur Binärdaten:
write()nimmtArrayBuffer/Uint8Arrayan. Strings musst du vorher mitTextEncoderumwandeln. - Keine Nachrichtengrenzen innerhalb eines Streams: Mehrere
write()-Aufrufe kommen beim Server als Bytestrom an. Schickst du mehrere Nachrichten über einen Stream, brauchst du eine eigene Rahmung (z. B. Längenpräfix). Alternativ nutzt du einen Stream pro Nachricht. - Stream-Limits: Der Server begrenzt, wie viele Streams gleichzeitig offen sein dürfen. Mit
waitUntilAvailable: truewartest du auf einen freien Platz, statt einen Fehler zu bekommen. Schließe Streams immer mitclose()oderabort(). - Voraussetzungen: Die Methode gibt es nur in sicheren Kontexten (HTTPS). Der Server muss HTTP/3 mit WebTransport unterstützen. Selbstsignierte Zertifikate gehen nur mit
serverCertificateHashesim Konstruktor. - Kompatibilität: Chromium-Browser ab Version 97 und Firefox ab 114 unterstützen die Methode, Safari erst in neueren Versionen.
sendOrderundsendGroupsind nicht überall umgesetzt. Prüfe die Unterstützung mit'WebTransport' in globalThisund halte einen Ersatz bereit (z. B. WebSocket).