Signatur
Beschreibung
Die Methode createBidirectionalStream() des WebTransport-Interfaces öffnet einen neuen, vom Client gestarteten Stream, über den in beide Richtungen Daten übertragen werden können. Das Promise wird mit einem WebTransportBidirectionalStream erfüllt. Dieser enthält einen readable-Stream (WebTransportReceiveStream) für Daten vom Server und einen writable-Stream (WebTransportSendStream) für Daten zum Server.
Bidirektionale Streams eignen sich für Anfrage/Antwort-Muster wie RPC-Aufrufe, Chat-Sitzungen oder Befehle, auf die der Server antwortet. Die Übertragung ist zuverlässig und geordnet, genau wie bei TCP. Weil jeder Stream aber ein eigener QUIC-Stream ist, gibt es zwischen verschiedenen Streams kein Head-of-Line-Blocking. Für unzuverlässige Übertragungen mit geringer Latenz nutzt man stattdessen WebTransport.datagrams, für Daten in nur eine Richtung createUnidirectionalStream().
Vor dem Aufruf sollte await transport.ready abgewartet werden. Die übertragenen Daten sind immer Bytes (Uint8Array). Text muss daher mit TextEncoder/TextDecoder bzw. TextEncoderStream/TextDecoderStream umgewandelt werden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | Object | {} | Optionales Konfigurationsobjekt. |
| $options.sendOrder | number | 0 | Priorität des Sende-Streams. Streams mit höherem Wert werden beim Senden bevorzugt. Der Wert kann später über writable.sendOrder geändert werden. |
| $options.sendGroup | WebTransportSendGroup | null | Sendegruppe, erzeugt mit transport.createSendGroup(). Die Priorisierung über sendOrder gilt dann innerhalb der Gruppe (noch nicht überall unterstützt). |
| $options.waitUntilAvailable | boolean | false | Bei true wartet das Promise, bis der Server weitere Streams erlaubt (Flusskontrolle). Bei false wird es sofort abgelehnt, wenn das Stream-Limit erreicht ist. |
Rückgabewert
readable (WebTransportReceiveStream) und writable (WebTransportSendStream) erfüllt wird. Es wird mit InvalidStateError abgelehnt, wenn die Verbindung geschlossen ist oder gerade geschlossen wird.Beispiele
Stream öffnen, Nachricht senden und Antwort lesen
const transport = new WebTransport('https://example.com:4433/echo');
await transport.ready;
const stream = await transport.createBidirectionalStream();
// Senden
const writer = stream.writable.getWriter();
await writer.write(new TextEncoder().encode('Hallo Server'));
await writer.close(); // Senderichtung beenden (FIN)
// Antwort lesen
const reader = stream.readable.getReader();
const decoder = new TextDecoder();
let antwort = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
antwort += decoder.decode(value, { stream: true });
}
console.log('Antwort:', antwort);
RPC-Hilfsfunktion mit JSON und Text-Streams
const rpc = async (transport, method, params) => {
const { readable, writable } = await transport.createBidirectionalStream({
waitUntilAvailable: true
});
const writer = writable.getWriter();
await writer.write(new TextEncoder().encode(JSON.stringify({ method, params })));
await writer.close();
let text = '';
for await (const chunk of readable.pipeThrough(new TextDecoderStream())) {
text += chunk;
}
return JSON.parse(text);
};
const transport = new WebTransport('https://api.example.com:4433/rpc');
await transport.ready;
const user = await rpc(transport, 'getUser', { id: 42 });
console.log(user.name);
Priorisierte Streams und Fehlerbehandlung
const transport = new WebTransport('https://game.example.com:4433/');
await transport.ready;
try {
// Steuerbefehle bevorzugt senden
const control = await transport.createBidirectionalStream({ sendOrder: 10 });
const bulk = await transport.createBidirectionalStream({ sendOrder: 0 });
const cw = control.writable.getWriter();
await cw.write(Uint8Array.of(0x01)); // z. B. "Ping"
// Stream bei Bedarf abbrechen
await bulk.writable.abort();
await bulk.readable.cancel();
} catch (err) {
if (err.name === 'InvalidStateError') {
console.error('Verbindung ist bereits geschlossen');
} else {
console.error('Stream-Fehler:', err);
}
}
// Wichtig · Fallstricke
- Kompatibilität: Wird in Chromium-Browsern und Firefox unterstützt, in Safari erst in neueren Versionen. Die Unterstützung vorher mit
'WebTransport' in globalThisprüfen. Funktioniert nur in sicheren Kontexten (HTTPS). Außerdem braucht der Server HTTP/3 mit WebTransport-Unterstützung. - Immer zuerst
await transport.readyabwarten. Ist die Verbindung geschlossen, wird das Promise mitInvalidStateErrorabgelehnt. - Streams übertragen nur Bytes. Nachrichtengrenzen bleiben nicht erhalten: Ein
write()kann beim Empfänger in mehrerenread()-Teilen ankommen oder mit anderen zusammengefasst werden. Deshalb braucht man ein eigenes Framing (z. B. ein Längenpräfix) oder schließt den Stream pro Nachricht. - Den Writer mit
close()schließen, sonst erfährt der Server nicht, dass keine Daten mehr kommen. Nicht mehr benötigte Streams mitabort()/cancel()freigeben, denn der Server begrenzt die Zahl gleichzeitiger Streams (Flusskontrolle). - Die Optionen
sendGroupundwaitUntilAvailablewerden noch nicht überall unterstützt. - Vom Server gestartete bidirektionale Streams erhält man nicht über diese Methode, sondern über
transport.incomingBidirectionalStreams.