Rabikant

Posted on March 9th

Getting Started with WebSocket in Springboot

"Lets learn about WebSocket in Springboot"

Setting up a Spring Boot Project

Let’s begin with creating a new Spring Boot project. You can use the Spring Initializr or create the project manually, but we will be showing you an example of how it is created manually. In this tutorial in particular, we will concentrate on the creation of the project manually with Gradle.

  1. Create a directory for your project:

    mkdir SpringWebSocketApp
    cd SpringWebSocketApp
    
  2. Initialize a Gradle project:

    gradle init --type java-application
    
  3. Update your build.gradle file to include Spring Boot dependencies:

    plugins {
        id("org.springframework.boot") version "3.1.4"
        id("io.spring.dependency-management") version "1.1.3"
    }
    
    group = "com.example"
    version = "0.0.1-SNAPSHOT"
    java.sourceCompatibility = JavaVersion.VERSION_17
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        // Core Spring Boot dependencies
        implementation("org.springframework.boot:spring-boot-starter")
    
        // Web and WebSocket support
        implementation("org.springframework.boot:spring-boot-starter-web")
        implementation("org.springframework.boot:spring-boot-starter-websocket")
    
        // Test dependencies
        testImplementation("org.springframework.boot:spring-boot-starter-test")
    }
    
    tasks.withType<Test> {
        useJUnitPlatform()
    }
    
  4. Refresh the Gradle project to download dependencies.

Setting up of the application environment

The main packages and classes are:

  • WebsocketDemoApplication: The simplest point through which the Spring Boot app can be executed.
  • WebSocketHandler: Handles WebSocket events.
  • WebSocketConfig: Loads WebSocket endpoints.

Application Entry Point

Create WebsocketDemoApplication.java in the src/main/java/springboot directory:

package springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebsocketDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebsocketDemoApplication.class, args);
    }    
}

This class initializes the Spring Boot application.

WebSocket Handler

WebSocketHandler is the Java class that is responsible for WebSocket communication of this Spring Boot web application. To that extent it extends TextWebSocketHandler which is a simple way of handling text messages over WebSocket. Let’s break it down into smaller pieces:

Class Definition and Logging

package springboot;

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class WebSocketHandler extends TextWebSocketHandler {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);
}

Key Points:

  • @Component: Annotates the class so the Spring framework could automatically pick during application loading for further instantiation.
  • Logger: The Logger instance is used to record such event as connections, messages and disconnections. Debugging and tracking WebSocket operations require logging to be enabled.

Handling a New Connection

@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    logger.info("Connected: " + session.getId());
}

Key Points:

  • afterConnectionEstablished: Occuring when a client has initially connected through the WebSocket protocol.
    • WebSocketSession: Stands for the class that represents single and direct cooperation with a client. It stores markings such as the session identifier, or the connection status.
  • Logging: Enters a log message with the session identification to signal successful connection.

Handling Incoming Messages

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    logger.info("Message received: " + message.getPayload());
    session.sendMessage(new TextMessage("Echo: " + message.getPayload()));
}

Key Points:

  • handleTextMessage:
    • Fired when the program receives a text-only message from the client.
    • TextMessage: Holds the message that the client is sending out.
  • Logging:
    • Storing the body of the message, which was passed by the client.
  • Echoing Messages:
    • Responds to the client with the same message prefixed with "Echo: ".
    • session.sendMessage(): Returns a response to the client through the active session available with it.

Handling Disconnections

@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
    logger.info("Disconnected: " + session.getId());
}

Key Points:

  • afterConnectionClosed:
    • Triggered when a client disconnects from the WebSocket.
    • CloseStatus: Provides details about the reason for the disconnection (e.g., normal closure, error, etc.).
  • Logging:
    • Logs the session ID to indicate which client has disconnected.

Summary of Core Functionalities

  1. Establishing Connections: The handler also makes a note and responds when a client initiates a connection.
  2. Processing Messages: Thus the handler records received messages and sends a confirmation.
  3. Closing Connections: The handler logs the disconnection events and cleanup as well hence the logs.

How It Works in Practice

  1. A client, issues a connection request to the WebSocket server.
  2. The afterConnectionEstablished method messages and logs the connection.
  3. A message is then passed from the client to the server.
  4. The handleTextMessage method actually writes the received text message to the log file and returns the text message to the originating client.
  5. When the client is disconnected, afterConnectionClosed method writes down the event.

Create WebSocketHandler.java in the same package and paste the given code:

package springboot;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class WebSocketHandler extends TextWebSocketHandler {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        logger.info("Connected: " + session.getId());
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        logger.info("Message received: " + message.getPayload());
        session.sendMessage(new TextMessage("Echo: " + message.getPayload()));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        logger.info("Disconnected: " + session.getId());
    }
}

WebSocket Configuration

The WebSocketConfig class is responsible for configuring WebSocket support in the Spring Boot application. It implements the WebSocketConfigurer interface, allowing customization of WebSocket handlers and endpoint mapping. Let's break this class into understandable sections:

1. Class Annotations

@Configuration
@EnableWebSocket

Key Points:

  • @Configuration:
    • Marks this class as a configuration class in Spring.
    • It defines beans and settings required by the application.
  • @EnableWebSocket:
    • Enables WebSocket support within the Spring application.
    • Ensures that WebSocket-related beans and infrastructure are initialized.

2. Class Declaration and Dependency Injection


public class WebSocketConfig implements WebSocketConfigurer {
    private final WebSocketHandler webSocketHandler;

    public WebSocketConfig(WebSocketHandler webSocketHandler) {
        this.webSocketHandler = webSocketHandler;
    }
}

Key Points:

  • Implements WebSocketConfigurer:
    • Provides methods to customize WebSocket configuration.
    • In this case, it allows mapping WebSocket handlers to specific endpoints.
  • WebSocketHandler Dependency:
    • The WebSocketHandler instance (defined earlier) is injected into the constructor.
    • Dependency Injection:
      • Spring automatically provides the WebSocketHandler bean (annotated with @Component) here.
      • This promotes loose coupling and enhances testability.

3. Overriding registerWebSocketHandlers

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry.addHandler(webSocketHandler, "/ws").setAllowedOrigins("*");
}

Create WebSocketConfig.java file and paste the following code:

package springboot;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    private final WebSocketHandler webSocketHandler;

    public WebSocketConfig(WebSocketHandler webSocketHandler) {
        this.webSocketHandler = webSocketHandler;
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler, "/ws").setAllowedOrigins("*");
    }
}

Key Points:

  • registerWebSocketHandlers Method: Oversees the WebSocket connections and relates then to the specific URL addresses.
  • WebSocketHandlerRegistry: Websockets, with specific endpoints detailed in WebSocket handlers, to which an object is registered.
  • Adding the Handler:
    • addHandler(webSocketHandler, "/ws"): Map the WebSocketHandler with the help of the HashMap with key /ws. As for WebSocket, clients can be connected through this URL.
    • setAllowedOrigins("*"): For cross at origin resource sharing (CORS) of the resources. The asterisk ( `` ) places demands at any location. Concerning production, it should only be allowed on the trusted domain for security purposes This should only do the production and should only allow the http request on the trusted domain for security purposes.

How It Works Together

  1. Enabling WebSocket Support: Annotated it with the @EnableWebSocket so that the application could understand WebSocket requests.
  2. Registering a WebSocket Handler: By default, the WebSocketHandler by its path is declared as /ws which shows the address that the clients connect to.
  3. CORS Policy: – The last call setAllowedOrigins("*") allows the WebSocket connection from any domain, which will be suitable for development.

Application Use Case

  • When a client connects to ws://localhost:8080/ws:
    1. The request is then forwarded to the WebSocketHandler alguns.IdClick is true.
    2. In WebSocketConfig it is ensured that, with help of the handler the messages and the connection are being processed.

Explanation:

  • @EnableWebSocket: Enables WebSocket support.
  • registerWebSocketHandlers: Maps the WebSocket handler to the /ws endpoint.

Frontend Client

HTML File

This HTML file serves as a WebSocket client that can connect, send messages, and disconnect from a WebSocket server. Here's a detailed breakdown:

1. Basic HTML Structure

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket Client</title>
</head>
<body>
    <h1>WebSocket Client</h1>
    <button id="connect">Connect</button>
    <button id="send" disabled>Send Message</button>
    <button id="disconnect" disabled>Disconnect</button>
    <div class="logs" id="logs"></div>
</body>
</html>

Key Points:

  • Section:
    • Contains metadata and the title of the page.
    • Sets character encoding to UTF-8 and enables responsive design via the viewport tag.
  • Section:
    • Displays a heading and three buttons for WebSocket interaction: Connect, Send Message, and Disconnect.
    • Contains a

      with id="logs" to display connection logs and messages.

2. Styling the Client

<style>
    body {
        font-family: Arial, sans-serif;
        background-color: #f4f4f9;
        text-align: center;
        padding: 20px;
    }

    button {
        font-size: 16px;
        margin: 10px;
        padding: 10px 20px;
        background-color: #007BFF;
        color: white;
        border: none;
        border-radius: 5px;
        cursor: pointer;
    }

    button:hover {
        background-color: #0056b3;
    }

    .logs {
        margin-top: 20px;
        max-height: 200px;
        overflow-y: auto;
        border: 1px solid #ccc;
        padding: 10px;
        background-color: #f9f9f9;
        border-radius: 5px;
        text-align: left;
    }
</style>

Key Points:

  • Body Styling:
    • Sets a soft background color and centers the content.
    • Adds padding for spacing.
  • Button Styling:
    • Buttons are styled with a blue background, white text, and rounded corners.
    • Includes hover effects for better user interactivity.
  • Logs Styling:
    • The logs container has a border, background color, and a scrollable area for overflow.

3. JavaScript Logic for WebSocket Communication

<script>
    let socket;

    const connectButton = document.getElementById('connect');
    const sendButton = document.getElementById('send');
    const disconnectButton = document.getElementById('disconnect');
    const logsDiv = document.getElementById('logs');

    function logMessage(message) {
        const log = document.createElement('div');
        log.textContent = message;
        logsDiv.appendChild(log);
        logsDiv.scrollTop = logsDiv.scrollHeight;
    }
</script>

Key Points:

  • socket Variable:
    • A global variable to hold the WebSocket instance.
  • Buttons and Logs:
    • Buttons and the logs container are selected using their id attributes.
  • logMessage Function:
    • Dynamically appends a new log message to the logs container.
    • Automatically scrolls the log view to show the latest message.

4. Connecting to the WebSocket Server

connectButton.onclick = () => {
    socket = new WebSocket('ws://localhost:8080/ws');

    socket.onopen = () => {
        logMessage('Connected to server');
        connectButton.disabled = true;
        sendButton.disabled = false;
        disconnectButton.disabled = false;
    };

    socket.onmessage = event => logMessage(`Server: ${event.data}`);

    socket.onclose = () => {
        logMessage('Disconnected from server');
        connectButton.disabled = false;
        sendButton.disabled = true;
        disconnectButton.disabled = true;
    };

    socket.onerror = error => logMessage(`Error: ${error.message}`);
};

Key Points:

  • WebSocket Initialization:
    • A new WebSocket connection is created to ws://localhost:8080/ws.
  • onopen Event:
    • Triggered when the connection is successfully established.
    • Logs a success message and enables relevant buttons.
  • onmessage Event:
    • Logs any message received from the server.
  • onclose Event:
    • Triggered when the connection is closed.
    • Logs a disconnection message and updates the button states.
  • onerror Event:
    • Logs any errors encountered during communication.

5. Sending a Message to the Server

endButton.onclick = () => {
    const message = 'Hello Server!';
    socket.send(message);
    logMessage(`You: ${message}`);
};

Key Points:

  • Message Sending:
    • Sends a predefined message (Hello Server!) to the WebSocket server.
  • Log Update:
    • Logs the sent message in the logs container.

6. Disconnecting from the Server

disconnectButton.onclick = () => socket.close();

Key Points:

  • Closing the Connection: Disconnects WebSocket using the close() method of WebSocket instance that has been created when the WebSocket request was initially made.
  • Triggering onclose: The exiting `onclose” event handler logs the disconnection; the ‘disable=’ attribute uploads button states.

How It Works

  1. Connect: Connecting happens by pressing the “Connect” button where it is creates WebSocket connection with the server. This makes the accessibility of the “Send Message” button possible as well as the “Disconnect” button.
  2. Send Message:”Send Message’’ just delivers a predecided message to the server only. The reason for that is the response from the server that is output in the logs The XML file therefore needs to be validated before being processed by a parser.
  3. Disconnect: Pressing the “Disconnect” button makes WebSocket connection become unavailable. Connect button to enable it Working user Updates and avails content to make the Connect button active again.

Create an index.html file in the src/main/resources/static directory:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket Client</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            text-align: center;
            padding: 20px;
        }

        button {
            font-size: 16px;
            margin: 10px;
            padding: 10px 20px;
            background-color: #007BFF;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }

        button:hover {
            background-color: #0056b3;
        }

        .logs {
            margin-top: 20px;
            max-height: 200px;
            overflow-y: auto;
            border: 1px solid #ccc;
            padding: 10px;
            background-color: #f9f9f9;
            border-radius: 5px;
            text-align: left;
        }
    </style>
</head>
<body>
    <h1>WebSocket Client</h1>
    <button id="connect">Connect</button>
    <button id="send" disabled>Send Message</button>
    <button id="disconnect" disabled>Disconnect</button>

    <div class="logs" id="logs"></div>

    <script>
        let socket;

        const connectButton = document.getElementById('connect');
        const sendButton = document.getElementById('send');
        const disconnectButton = document.getElementById('disconnect');
        const logsDiv = document.getElementById('logs');

        function logMessage(message) {
            const log = document.createElement('div');
            log.textContent = message;
            logsDiv.appendChild(log);
            logsDiv.scrollTop = logsDiv.scrollHeight;
        }

        connectButton.onclick = () => {
            socket = new WebSocket('ws://localhost:8080/ws');
            socket.onopen = () => {
                logMessage('Connected to server');
                connectButton.disabled = true;
                sendButton.disabled = false;
                disconnectButton.disabled = false;
            };
            socket.onmessage = event => logMessage(`Server: ${event.data}`);
            socket.onclose = () => {
                logMessage('Disconnected from server');
                connectButton.disabled = false;
                sendButton.disabled = true;
                disconnectButton.disabled = true;
            };
            socket.onerror = error => logMessage(`Error: ${error.message}`);
        };

        sendButton.onclick = () => {
            const message = 'Hello Server!';
            socket.send(message);
            logMessage(`You: ${message}`);
        };

        disconnectButton.onclick = () => socket.close();
    </script>
</body>
</html>

Running the Application

  1. Start the Spring Boot app:

    ./gradlew bootRun
    
  2. Browse index.html.
  3. Test the WebSocket connection:
    • Click Connect: Sends the data of connecting the WebSocket server.
    • Click Send Message: A message to the server is sent.
    • Click Disconnect: Closes the connection.

Code Flow

  1. Client: Starts a WebSocket connection.
  2. Server: Manages the connection through WebSocketHandler.
  3. Message Exchange: Client’s messages received by the server are reflected or echoed back.
  4. Logs: Server and browser consoles contain records on the interaction events.

Complete Code

The project is available on our GitHub : https://github.com/piehostHQ/Springboot-websocket

Comments

Leave a comment.

Share your thoughts or ask a question to be added in the loop.