Movatterモバイル変換


[0]ホーム

URL:


MDN Web Docs

Using WebSocketStream to write a client

TheWebSocketStream API is aPromise-based alternative toWebSocket for creating and using client-side WebSocket connections.WebSocketStream uses theStreams API to handle receiving and sending messages, meaning that socket connections can take advantage of streambackpressure automatically (no additional action required by the developer), regulating the speed of reading or writing to avoid bottlenecks in the application.

This article explains how to use theWebSocketStream API to create a WebSocket client.

Feature detection

To check whether theWebSocketStream API is supported, you can use the following:

js
if ("WebSocketStream" in self) {  // WebSocketStream is supported}

Creating a WebSocketStream object

To create a WebSocket client, you first need to create a newWebSocketStream instance using theWebSocketStream() constructor. In its simplest form, it takes the URL of the WebSocket server as an argument:

js
const wss = new WebSocketStream("wss://example.com/wss");

It can also take an options object containing custom protocols and/or anAbortSignal (seeClosing the connection):

js
const controller = new AbortController();const queueWSS = new WebSocketStream("wss://example.com/queue", {  protocols: ["amqp", "mqtt"],  signal: controller.signal,});

Sending and receiving data

TheWebSocketStream instance has anopened property — this returns a promise that fulfills with an object containing aReadableStream and aWritableStream instance once the WebSocket connection is opened successfully:

js
const { readable, writable } = await wss.opened;

CallinggetReader() andgetWriter() on these objects provides us with aReadableStreamDefaultReader and aWritableStreamDefaultWriter respectively, which can be used to read from and write to the socket connection:

js
const reader = readable.getReader();const writer = writable.getWriter();

To write data to the socket, you can useWritableStreamDefaultWriter.write():

js
writer.write("My message");

To read data from the socket, you can continuously callReadableStreamDefaultReader.read() until the stream has finished, which is indicated bydone beingtrue:

js
while (true) {  const { value, done } = await reader.read();  if (done) {    break;  }  // Process value in some way}

The browser automatically controls the rate at which the client receives and sends data by applying backpressure when needed. If data is arriving faster than the client canread() it, the underlying Streams API exerts backpressure on the server. In addition,write() operations will only proceed if it is safe to do so.

Closing the connection

WithWebSocketStream, the information previously available via theWebSocketclose anderror events is now available via theclosed property — this returns a promise that fulfills with an object containing the closing code (see the full list ofCloseEvent status codes) and reason indicating why the server closed the connection:

js
const { code, reason } = await wss.closed;

As mentioned earlier, the WebSocket connection can be closed using anAbortController. The necessaryAbortSignal is passed to theWebSocketStream constructor during creation, andAbortController.abort() can then be called when required:

js
const controller = new AbortController();const wss = new WebSocketStream("wss://example.com/wss", {  signal: controller.signal,});// some time latercontroller.abort();

Alternatively you can use theWebSocketStream.close() method to close a connection. This is mainly used if you wish to specify a custom code and/or reason:

js
wss.close({  code: 4000,  reason: "Night draws to a close",});

Note:Depending on the server setup and status code you use, the server may choose to ignore a custom code in favor of a valid code that is correct for the closing reason.

A complete sample client

To demonstrate basic usage ofWebSocketStream, we've created a sample client. You can see thefull listing at the bottom of the article, and follow along with the explanation below.

Note:To get the example working, you'll also need a server component. We wrote our client to work along with the Deno server explained inWriting a WebSocket server in JavaScript (Deno), but any compatible server will do.

The HTML for the demo is as follows. It includes informational<h2> and<p> elements, a<button> to close the WebSocket connection that is initially disabled, and a<div> for us to write output messages into.

html
<h2>WebSocketStream Test</h2><p>Sends a ping every five seconds</p><button disabled>Close socket connection</button><div></div>

Now on to the JavaScript. First we grab references to the output<div> and the close<button>, and define a utility function that writes messages to the<div>:

js
const output = document.querySelector("#output");const closeBtn = document.querySelector("#close");function writeToScreen(message) {  const pElem = document.createElement("p");  pElem.textContent = message;  output.appendChild(pElem);}

Next, we create anif...else structure to feature detectWebSocketStream and output an informative message on non-supporting browsers:

js
if (!("WebSocketStream" in self)) {  writeToScreen("Your browser does not support WebSocketStream");} else {  // supporting code path}

In the supporting code path, we begin by defining a variable containing the WebSocket server URL, and constructing a newWebSocketServer instance:

js
const wsURL = "ws://127.0.0.1/";const wss = new WebSocketStream(wsURL);

Note:Best practice is to use secure WebSockets (wss://) in production apps. However, in this demo we are connecting to localhost, therefore we need to use the non-secure WebSocket protocol (ws://) for the example to work.

The main bulk of our code is contained within thestart() function, which we define and then immediately invoke. We await theopened promise, then after it fulfills write a message to let the reader know the connection is successful and createReadableStreamDefaultReader andWritableStreamDefaultWriter instances from the returnedreadable andwritable properties.

Next, we create astart() function that sends "ping" messages to the server and receives "pong" messages back, and invoke it. In the function body we await thewss.opened promise and create a reader and writer from its fulfillment values. Once the socket is open, we communicate that to the user and enable the close button. Next, wewrite() a"ping" value to the socket and communicate that to the user. At this point, the server will respond with a"pong" message. We await theread() of the response, communicate it to the user, then write another"ping" to the server after a timeout of 5 seconds. This continues the"ping"/"pong" loop indefinitely.

js
async function start() {  const { readable, writable } = await wss.opened;  writeToScreen("CONNECTED");  closeBtn.disabled = false;  const reader = readable.getReader();  const writer = writable.getWriter();  writer.write("ping");  writeToScreen("SENT: ping");  while (true) {    const { value, done } = await reader.read();    writeToScreen(`RECEIVED: ${value}`);    if (done) {      break;    }    setTimeout(async () => {      try {        await writer.write("ping");        writeToScreen("SENT: ping");      } catch (e) {        writeToScreen(`Error writing to socket: ${e.message}`);      }    }, 5000);  }}start();

Note:ThesetTimeout() function wraps thewrite() call in atry...catch block to handle any errors that can arise if the application tries to write to the stream after it has been closed.

We now include a promise-style code section to inform the user of the code and reason if the WebSocket connection is closed, as signalled by theclosed promise fulfilling:

js
wss.closed.then((result) => {  writeToScreen(    `DISCONNECTED: code ${result.closeCode}, message "${result.reason}"`,  );  console.log("Socket closed", result.closeCode, result.reason);});

Finally, we add an event listener to the close button that closes the connection using theclose() method, with a code and custom reason. The function also disables the close button — we don't want users to press it once the connection is already closed.

js
closeBtn.addEventListener("click", () => {  wss.close({    code: 1000,    reason: "That's all folks",  });  closeBtn.disabled = true;});

Full listing

js
const output = document.querySelector("#output");const closeBtn = document.querySelector("#close");function writeToScreen(message) {  const pElem = document.createElement("p");  pElem.textContent = message;  output.appendChild(pElem);}if (!("WebSocketStream" in self)) {  writeToScreen("Your browser does not support WebSocketStream");} else {  const wsURL = "ws://127.0.0.1/";  const wss = new WebSocketStream(wsURL);  console.log(wss.url);  async function start() {    const { readable, writable, extensions, protocol } = await wss.opened;    writeToScreen("CONNECTED");    closeBtn.disabled = false;    const reader = readable.getReader();    const writer = writable.getWriter();    writer.write("ping");    writeToScreen("SENT: ping");    while (true) {      const { value, done } = await reader.read();      writeToScreen(`RECEIVED: ${value}`);      if (done) {        break;      }      setTimeout(() => {        writer.write("ping");        writeToScreen("SENT: ping");      }, 5000);    }  }  start();  wss.closed.then((result) => {    writeToScreen(      `DISCONNECTED: code ${result.closeCode}, message "${result.reason}"`,    );    console.log("Socket closed", result.closeCode, result.reason);  });  closeBtn.addEventListener("click", () => {    wss.close({      code: 1000,      reason: "That's all folks",    });    closeBtn.disabled = true;  });}

Help improve MDN

Learn how to contribute.

This page was last modified on byMDN contributors.


[8]ページ先頭

©2009-2025 Movatter.jp