LMX
BlogSelectedWorkSponsorsAbout
Command Palette

Search for a command to run...

Blog

WebSocket Tutorial

LMXOct 24, 20196 min read#JavaScript#WebSocket

This article introduces the basic concepts, operating principles, and common APIs of the WebSocket protocol, with a complete example showing how to implement two-way communication in a frontend project.

1. What Is WebSocket?

WebSocket is a network communication protocol required by many advanced features. People encountering it for the first time often ask the same question: if HTTP already exists, why do we need another protocol, and what does it offer?

The answer is simple: HTTP has a limitation—communication can only be initiated by the client. For example, to learn today's weather, the client must send a request and the server returns the result. HTTP does not let a server proactively push information to a client. This one-way request model becomes awkward when the server's state changes continuously. The client has to use polling, asking at intervals whether new information is available. Chat rooms are a typical example. Polling is inefficient and wastes resources because it must connect repeatedly or keep an HTTP connection open. WebSocket was created as a better solution.

Its defining feature is true, equal, two-way communication: the server can proactively push information to the client, and the client can send information to the server. It is a form of server push technology.

img

Other characteristics include:

  1. It is built on TCP, so server-side implementations are relatively straightforward.

  2. It is highly compatible with HTTP. The default ports are also 80 and 443, and its handshake uses HTTP, which makes it less likely to be blocked and allows it to pass through HTTP proxies.

  3. Its data format is lightweight, with little overhead and efficient communication.

  4. It can send both text and binary data.

  5. It has no same-origin restriction; a client can communicate with any server.

  6. Its protocol identifier is ws (or wss when encrypted), and the server address is a URL.

img

2. A Simple WebSocket Example

var ws = new WebSocket("ws://echo.websocket.org");

ws.onopen = function (evt) {
  console.log("Connection open ...");
  ws.send("Hello WebSockets!");
};

ws.onmessage = function (evt) {
  console.log("Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function (evt) {
  console.log("Connection closed.");
};

3. Common WebSocket APIs

  • WebSocket constructor

    let ws = new WebSocket("ws://localhost:8080");

    After this statement runs, the client starts connecting to the server.

  • WebSocket state (readyState)

    The readyState property returns the instance's current state. There are four possible states.

    CONNECTING: value 0, connection in progress
    OPEN: value 1, connected
    CLOSING: value 2, connection is closing
    CLOSED: value 3, connection is closed
  • websocket.onopen (callback after a successful connection)

    The instance's onopen property specifies the callback to invoke after the connection succeeds.

    ws.onopen = function () {
      ws.send("Hello Server!");
    };

    Use addEventListener when you need multiple callbacks.

    ws.addEventListener("open", function (event) {
      ws.send("Hello Server!");
    });
  • websocket.onclose (callback after the connection closes)

    The instance's onclose property specifies the callback to invoke when the connection closes.

    ws.onclose = function (event) {
      console.log("onclose");
    };

    Use addEventListener when you need multiple callbacks.

    ws.addEventListener("close", function (event) {
      console.log("onclose");
    });
  • websocket.onmessage (callback after data arrives from the server)

    ws.onmessage = function (event) {
      // Read data from event.data
      var data = event.data;
      // Process the data
    };
  • websocket.send (send data to the server)

    ws.send("your message");
  • websocket.onerror (callback when an error occurs)

    ws.onerror = function (event) {
      // Handle the error event
    };

4. WebSocket Heartbeats and Reconnection

WebSocket creates a long-lived connection between the frontend and backend. Either side may lose the connection under certain conditions without receiving immediate feedback. Heartbeats and reconnection are therefore needed to keep the connection sustainable and stable.

When using the native WebSocket API, a device losing its network connection does not immediately trigger a WebSocket event, so the frontend cannot tell that the connection has been lost. Only after calling websocket.send does the browser notice; it then triggers onclose immediately or after a delay, depending on the browser and version. The backend WebSocket service may also fail and break the connection without notifying the frontend.

The frontend should therefore send periodic ping heartbeat messages. The backend immediately replies with pong to confirm that the connection is healthy. If no pong arrives within a certain time, the connection is considered unhealthy and the frontend reconnects. In short, the frontend takes the initiative: it sends regular ping messages to detect network and connection problems, and continues reconnecting after an error until the connection succeeds.

A typical heartbeat check looks like this:

// Periodically check the connection. If connected, send a message to reset
// the maximum connection lifetime on both server and client. Reconnect if closed.
let heartCheck = {
  // Set slightly below the server timeout so communication can reset it
  // before the connection is about to close.
  timeout: 100000,
  serverTimeoutObj: null,
  reset: function () {
    clearTimeout(this.serverTimeoutObj);
    return this;
  },
  start: function () {
    this.serverTimeoutObj = window.setInterval(() => {
      if (websocket.readyState === 1) {
        websocket.send("ping");
      } else {
        window.clearTimeout(this.serverTimeoutObj);
        // Reconnect or perform other handling
      }
    }, this.timeout);
  },
};

5. Complete WebSocket Example

function newWebSocket(option) {
  console.log("new webSocket.....");
  let websocket = null;
  // Check whether WebSocket is supported in the current environment
  if (window.WebSocket) {
    if (!websocket) {
      websocket = new WebSocket("your request URL");
    }
  } else {
    console.log("not support websocket");
  }
  // Callback invoked when the connection is established
  websocket.onopen = function (e) {
    // Reset the heartbeat after a successful connection
    heartCheck.reset().start();
    console.log("connected successfully");
  };
  // Keep trying to reconnect after a connection error
  websocket.onerror = function () {
    console.log(`onerror`);
    newWebSocket();
  };
  // Callback invoked when a message arrives
  websocket.onmessage = function (e) {
    console.log("onmessage", e.data);
    var message = e.data;
    if (message) {
      // Handle the received message
      if (option != undefined) {
        // Invoke the supplied object's method with the message
        option.onmessage(message);
      }
    }
  };

  // Callback invoked when the server closes the connection
  websocket.onclose = function () {
    console.log("onclose");
  };
  // Close WebSocket proactively when the window closes to avoid server errors
  window.onbeforeunload = () => {
    return websocket.close();
  };

  // Periodically check the connection. Send a message when connected to reset
  // the maximum lifetime; reconnect if the connection has closed.
  var heartCheck = {
    // Keep the heartbeat slightly shorter than the server-side timeout.
    timeout: 100000,
    serverTimeoutObj: null,
    reset: function () {
      clearTimeout(this.serverTimeoutObj);
      return this;
    },
    start: function () {
      this.serverTimeoutObj = window.setInterval(() => {
        if (websocket.readyState === 1) {
          websocket.send("ping");
        } else {
          console.log("websocket stop", websocket.readyState);
          window.clearTimeout(this.serverTimeoutObj);
          newWebSocket(option);
        }
      }, this.timeout);
    },
  };
  return websocket;
}

Discussion

Support the author
Previous post

Intermediate Front-End Interview Compilation - Middle Volume

Next post

Implementing the function of car trajectory query with OpenLayers

LMX BlogWeb · TypeScript · Vue · Node.js
Crafted by
@LMXZQQ
Build
unavailable
Date
2026-10-30
Articles
29
Deployed on
Nginx
Source code
GitHub
License
MIT License
Typeface
Geist
Stack
  • next@16.4.3
  • react@19.3.1
  • @base-ui/react@1.8.0
  • shadcn@4.12.0
  • tailwindcss@4.3.3
Analytics
  • OpenPanel
  • Google Analytics
For agents
  • llms.txt
  • index.md
Inspired by
  1. Tailwind CSS
  2. shadcn/ui
  3. Vercel
  4. Apple
  5. Evil Charts
  6. Devouring Details
  7. Skiper UI
  8. Making Software
  9. shadcncraft
Approved by
静水流深 / LMX
1602
Command Palette

Search for a command to run...