Why Hand‑Write WebSocket? Use Spring Boot + Netty‑SocketIO for Simpler Real‑Time Communication

This article walks through building a real‑time bidirectional communication system with Spring Boot 3.5.0 and the Netty‑SocketIO library, covering its core features, Maven setup, server implementation via CommandLineRunner, client HTML/JavaScript code, room support, and testing screenshots.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Why Hand‑Write WebSocket? Use Spring Boot + Netty‑SocketIO for Simpler Real‑Time Communication

Netty‑SocketIO is a Java open‑source implementation of the Socket.IO server built on the Netty framework and released under the Apache 2.0 license. It supports Socket.IO client versions 1.x‑4.x, xhr polling, WebSocket transport, namespaces and rooms, message acknowledgments, SSL, client storage options (Redisson, Hazelcast, in‑memory), distributed broadcast between nodes, OSGi, Spring integration, JPMS module information, and provides a lock‑free, thread‑safe design. The JAR runs on Java 8, while module metadata requires Java 11 or newer.

Environment : Spring Boot 3.5.0.

Dependency (add to pom.xml):

<dependency>
  <groupId>com.corundumstudio.socketio</groupId>
  <artifactId>netty-socketio</artifactId>
  <version>2.0.14</version>
</dependency>

Including this Maven artifact lets the project quickly build a high‑performance, stable Socket.IO server on top of Netty.

Server implementation : After the Spring application starts, a @Component that implements CommandLineRunner creates and configures the server.

@Component
public class SocketioServerRunner implements CommandLineRunner {
  private static final Map<String, SocketIOClient> clients = new ConcurrentHashMap<>();
  private static final Logger logger = LoggerFactory.getLogger("SocketIO.Server");

  @Override
  public void run(String... args) throws Exception {
    Configuration config = new Configuration();
    config.setHostname("localhost");
    config.setPort(8088);

    SocketIOServer server = new SocketIOServer(config);

    server.addConnectListener(client -> {
      Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
      String username = params.get("username").get(0);
      logger.info("[{}] connected: {}", username, client.getRemoteAddress());
      clients.put(username, client);
    });

    server.addDisconnectListener(client -> {
      Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
      String username = params.get("username").get(0);
      logger.info("[{}] disconnected: {}", username, client.getRemoteAddress());
      clients.remove(username);
    });

    server.addEventListener("message", String.class, (client, data, ackSender) -> {
      logger.info("Received message from [{}]: {}", client.getRemoteAddress(), data);
      client.sendEvent("message", "Server received your message at %s".formatted(
        DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now())));
    });

    server.startAsync();
  }
}

The server registers listeners for connection, disconnection, and a custom "message" event, logs activity, stores client sessions, and replies with a timestamped message.

Client implementation : The front‑end loads socket.io.js from a CDN and provides a simple UI for username entry, connection control, message display, and sending.

<script>
  let socket;
  function appendToMessageArea(message) {
    const list = document.querySelector("#messageList");
    const item = document.createElement("div");
    item.className = "message-item";
    item.textContent = message;
    if (list.firstChild) list.insertBefore(item, list.firstChild);
    else list.appendChild(item);
  }

  function connectServer() {
    const username = document.querySelector("#username").value;
    if (!username) { alert('Please enter a username'); return; }
    socket = io("ws://localhost:8088", { query: { username } });
    socket.on("connect", () => appendToMessageArea(`Connected to server`));
    socket.on("disconnect", () => appendToMessageArea(`Disconnected from server`));
    socket.on("message", data => appendToMessageArea(`${data}`));
  }

  function disconnectServer() {
    if (socket) { socket.disconnect(); appendToMessageArea(`Manually disconnected`); }
  }

  function sendMessage() {
    const contentNode = document.querySelector("#content");
    const message = contentNode.value.trim();
    if (message) {
      if (socket && socket.connected) {
        socket.emit("message", message);
        appendToMessageArea(`Me: ${message}`);
        contentNode.value = "";
      } else {
        appendToMessageArea(`Error: Not connected to server`);
      }
    }
  }
</script>

The HTML layout includes fields for username, connect/disconnect buttons, a dynamic message list, and a textarea with a send button.

Testing : After entering a username and clicking “Connect Server”, the UI shows connection status, messages can be sent, and the server console logs connection, disconnection, and received messages. Screenshots of the UI and console output are provided.

Room (chatroom) feature : By calling client.joinRoom("packroom") in the connect listener and client.leaveRoom("packroom") in the disconnect listener, the server aggregates clients into a named room. It broadcasts a welcome message on join and a leave notification on disconnect. The message listener now uses server.getRoomOperations("packroom").sendEvent(...) to broadcast messages to all participants in the room.

Client‑side code remains the same; the UI displays the broadcasted chatroom messages.

Final page effect (illustrated with screenshots): the chat interface shows real‑time messages, connection status, and room‑wide broadcasts.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaspring-bootwebsocketreal-time-communicationsocket.ionetty-socketio
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.