Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Establishing Full-Duplex Real-Time Connections Using WebSocket and Spring Boot

Tech May 8 3

Protocol Fundamentals

WebSocket defines a standardized method for opening persistent, bidirectional channels over a single Transmission Control Protocol (TCP) socket. After an initial negotiation phase, both the client and backend can independently exchange data streams without adhering to traditional request-response cycles or repeatedly tearing down transport connections.

Network Behavior Comparison

  • Lifecycle Management: Conventional hypertext transfer shuts down after each transaction. WebSocket maintains an active tunnel until explicitly released.
  • Transmission Direction: Standard web APIs enforce strict client-initiated patterns. WebSocket permits simultaneous dual-directional messaging.
  • Underlying Transport: Both architectures rely on standard TCP infrastructure, eliminating the need for additional network layers.

Optimal Deployment Contexts

Applications requiring low-latency feedback loops frequently adopt this specification. Common implementations include live collaborative editing tools, financila ticker engines, interactive gaming backends, and synchronous audience polling interfaces.

Backend Integration Workflwo

The following steps outline a production-ready approach for embedding WebSocket capabilities within a Spring Boot application using the Jakarta WebSocket API.

1. Frontend Connection Manager

A minimal client interface initializes the socket, routes inbound payloads to a rendering container, and handles lifecycle boundaries.


<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Socket Client UI</title>
</head>
<body>
  <input id="msgInput" type="text" placeholder="Enter transmission..." />
  <button id="sendBtn">Transmit</button>
  <button id="disconnectBtn">Terminate Session</button>
  <div id="feedLog"></div>

  <script>
    let transportLayer = null;
    const uniqueId = 'node_' + Math.random().toString(16).slice(2);
    const serviceUrl = `ws://localhost:8080/ws/${uniqueId}`;

    if ('WebSocket' in window) {
      transportLayer = new WebSocket(serviceUrl);
    } else {
      alert('Runtime environment lacks WebSocket support.');
    }

    transportLayer.onerror = () => appendStatus('Communication link failed.');
    transportLayer.onopen = () => appendStatus('Channel initialized.');
    transportLayer.onmessage = (dataEvent) => appendStatus(dataEvent.data);
    transportLayer.onclose = () => appendStatus('Transport severed.');

    window.addEventListener('beforeunload', () => {
      if (transportLayer?.readyState === WebSocket.OPEN) transportLayer.close();
    });

    function appendStatus(message) {
      const container = document.getElementById('feedLog');
      container.innerHTML += `[${new Date().toLocaleTimeString()}] ${message}<br/>`;
    }

    document.getElementById('sendBtn').addEventListener('click', () => {
      const buffer = document.getElementById('msgInput').value.trim();
      if (buffer && transportLayer.readyState === WebSocket.OPEN) {
        transportLayer.send(buffer);
        document.getElementById('msgInput').value = '';
      }
    });

    document.getElementById('disconnectBtn').addEventListener('click', () => {
      transportLayer.close();
    });
  </script>
</body>
</html>

2. Build System Dependency

Incorporate the official starter module into your project manifest to enable automatic configuration scanning.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

3. Endpoint Controller

The server handler intercepts lifecycle hooks and routes messages. Thread-safe storage mechanisms ensure stable tracking across concurrent user sessions.

package com.example.socket;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@ServerEndpoint("/ws/{participantId}")
public class ConnectionHandler {

    private static final ConcurrentHashMap<String, Session> activeChannels = new ConcurrentHashMap<>();
    private static final AtomicInteger sessionCounter = new AtomicInteger(0);

    @PostConstruct
    public void init() {
        System.out.println("WebSocket handler registered");
    }

    @OnOpen
    public void establish(Session session, @PathParam("participantId") String participantId) {
        int total = sessionCounter.incrementAndGet();
        System.out.printf("Node %s connected. Total active: %d%n", participantId, total);
        activeChannels.put(participantId, session);
    }

    @OnMessage
    public void receive(String payload, @PathParam("participantId") String participantId) {
        System.out.printf("[%s] Inbound: %s%n", participantId, payload);
        // Additional routing or persistence logic belongs here
    }

    @OnClose
    public void release(@PathParam("participantId") String participantId) {
        activeChannels.remove(participantId);
        sessionCounter.decrementAndGet();
        System.out.printf("Node %s detached.%n", participantId);
    }

    public void broadcast(String announcement) throws IOException, InterruptedException {
        activeChannels.forEach((id, session) -> {
            if (session.isOpen()) {
                try {
                    session.getBasicRemote().sendText(announcement);
                } catch (IOException e) {
                    System.err.printf("Drop message to %s: %s%n", id, e.getMessage());
                }
            }
        });
    }
}

4. Scanner Configuration

Spring Boot requires a dedicated exporter bean to detect server-side annotations and bind them to embedded servlet containers.

package com.example.config;

import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class SocketBootstrapConfig {

    @Bean
    public ServerEndpointExporter deployEndpoints() {
        return new ServerEndpointExporter();
    }
}

5. Periodic Distribution Scheduler

Automated tasks can push synthetic updates at fixed intervals, demonstrating how backend state propagates to all connected clients.

package com.example.tasks;

import com.example.socket.ConnectionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
public class DataFeedScheduler {

    private static final Logger logger = LoggerFactory.getLogger(DataFeedScheduler.class);
    private final ConnectionHandler hub;

    public DataFeedScheduler(ConnectionHandler hub) {
        this.hub = hub;
    }

    @Scheduled(cron = "0/5 * * * * *")
    public void emitHeartbeat() {
        String snapshot = "Global update: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
        try {
            hub.broadcast(snapshot);
        } catch (Exception ex) {
            logger.warn("Distribution cycle interrupted", ex);
        }
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.