https://a.storyblok.com/f/270183/1368x665/fba399d139/java-websockets_24.png

How to Create a WebSocket Server with the Java API for WebSockets

Published on July 5, 2021

Time to read: 7 minutes

This article was updated in August 2025

WebSocket is a protocol that allows a server and a client (like a browser or mobile app) talk to each other in real time. Unlike RESTful HTTP, where the client has to keep checking in for updates (a process called polling), WebSocket keeps a constant, two-way connection open. That means the server can send updates to the client whenever something changes, so there is no need to wait for the client to ask.

In this tutorial, we’ll walk you through how to implement a WebSocket server in Java using the Jakarta WebSocket API (JSR 356).

Prerequisites

The Java API for WebSockets

Introduced in Java EE 7, the Java API for WebSockets, or JSR 356, is a specification that Java developers can use in order to integrate WebSockets into their applications.

It allows developers to write their WebSocket-based application completely independent of their container's implementation. For this guide, you will be using Tomcat. However, because you will be using JSR 356, any other web container that supports Jakarta EE 9 or later should work.

Create the Project

You will use Gradle to initialize a new Java application. You can use the following command to create a directory for your project, navigate to that directory, and initialize the application:

mkdir websocket-java-api cd websocket-java-api

Create a new Gradle project:

  1. Run gradle init --type=java-application command

  2. Select Groovy as script language

  3. Select JUnit Jupiter as a testing framework

  4. Leave default Project name

  5. Leave default Source package

Add the Java WebSocket API Dependency

Add the following dependency to the dependencies block of app/build.gradle file:

implementation 'jakarta.websocket:jakarta.websocket-api:2.1.1'

Jakarta EE 9+ uses the jakarta.websocket namespace instead of javax.websocket, and Tomcat 10+ follows this convention.

Create the WebSocket Endpoint

WebSocket messages can be both text and binary. You're going to create an endpoint that can handle both of these messages.

The @ServerEndpoint annotation is used to decorate a class and declare it as a WebSocket endpoint. The annotation takes a path argument that defines where the endpoint will be accessible.

Create a new class called WebSocketEndpoint inside the src/main/java/websocket/java/api folder and add the following code:

import jakarta.websocket.server.ServerEndpoint;
import jakarta.websocket.OnMessage;

@ServerEndpoint("/socket")
public class WebSocketEndpoint {
    @OnMessage
    public String handleTextMessage(String message) {
        System.out.println("New Text Message Received");
        return message;
    }

    @OnMessage(maxMessageSize = 1024000)
    public byte[] handleBinaryMessage(byte[] buffer) {
        System.out.println("New Binary Message Received");
        return buffer;
    }
}

The @OnMessage annotation links Java methods to incoming text or binary WebSocket messages. The method signature determines which type of message it handles. For example, String is used for text and byte[] is for binary.

In this example, you're creating an echo server that returns the same message it receives. The maxMessageSize parameter on the binary handler allows the server to accept large payloads such as image files. You’ll test this using a browser-based client later in the tutorial.

Create a Client to Test Your Application

You will need to create a client to test your WebSocket server. You will want to test sending both text and binary messages. This can be accomplished with a simple application written in HTML and JavaScript.

Create the webapp folder inside of the src/main folder.

Add the following to index.html inside of the src/main/webapp folder:

<html>
<head>
    <style>
        #messages {
            text-align: left;
            width: 50%;
            padding: 1em;
            border: 1px solid black;
        }
    </style>
    <title>Sample WebSocket Client</title>
</head>
<body>
<div class="container">
    <div id="messages" class="messages"></div>
    <div class="input-fields">
        <p>Type a message and hit send:</p>
        <input id="message"/>
        <button id="send">Send</button>

        <p>Select an image and hit send:</p>
        <input type="file" id="file" accept="image/*"/>

        <button id="sendImage">Send Image</button>
    </div>
</div>
</body>
<script>
    const messageWindow = document.getElementById("messages");

    const sendButton = document.getElementById("send");
    const messageInput = document.getElementById("message");

    const fileInput = document.getElementById("file");
    const sendImageButton = document.getElementById("sendImage");

    const socket = new WebSocket("ws://localhost:8080/socket");
    socket.binaryType = "arraybuffer";

    socket.onopen = function (event) {
        addMessageToWindow("Connected");
    };

    socket.onmessage = function (event) {
        if (event.data instanceof ArrayBuffer) {
            addMessageToWindow('Got Image:');
            addImageToWindow(event.data);
        } else {
            addMessageToWindow(`Got Message: ${event.data}`);
        }
    };

    sendButton.onclick = function (event) {
        sendMessage(messageInput.value);
        messageInput.value = "";
    };

    sendImageButton.onclick = function (event) {
        let file = fileInput.files[0];
        sendMessage(file);
        fileInput.value = null;
    };

    function sendMessage(message) {
        socket.send(message);
        addMessageToWindow("Sent Message: " + message);
    }

    function addMessageToWindow(message) {
        messageWindow.innerHTML += `<div>${message}</div>`
    }

    function addImageToWindow(image) {
        let url = URL.createObjectURL(new Blob([image]));
        messageWindow.innerHTML += `<img src="${url}"/>`
    }
</script>
</html>

Embed and Configure Tomcat

Unlike Creating a WebSocket Server with Spring Boot, or Creating a WebSocket Server with the Spark Framework, there is initially no embedded server to run your application.

The Gretty plugin for Gradle can be used to embed a variety of containers.

First, apply the Gretty plugin to build.gradle file by adding this to your plugins block:

plugins {
    id 'org.gretty' version '4.1.7'
}

Second, you will need to configure Gretty to use Tomcat as the servlet container, and set the context path to / for the sake of simplicity.

Add the following block to build.gradle:

gretty {
    servletContainer = 'tomcat10'
    contextPath = '/'
}

Gretty 3.0.5+ supports tomcat10 for compatibility with Jakarta EE 9+ (jakarta.* namespaces).

Note that, by default, Gretty will use Jetty as the servlet container. This same guide will also run in Jetty, but that is the same container that both Spring Boot and the Spark framework embed and I wanted to show something different.

Start the Application

Your WebSocket server is now complete. Start your application using the gradle appRun command inside of the application's directory.

Make sure your JAVA_HOME is set to JDK 21 before running the application:

echo $JAVA_HOME

Leave the terminal window open.

You can access your application by opening http://localhost:8080 URL in the browser.

You will be greeted with the following page:

Sample JavaScript-enabled client for testing the WebSocket serverSample JavaScript-enabled client for testing the WebSocket serverThe "connected" message indicates that the JavaScript client was able to make a connection.

Try sending a text message by typing into the input field and clicking on the send button. Also try uploading an image. In both instances, you should see the same message and image echoed back.

Sample JavaScript-enabled client showing a text and binary message echoed back.Sample JavaScript-enabled client showing a text and binary message echoed back.

Conclusion

You have successfully created a WebSocket server using the Java API for WebSockets (JSR 356), embedded it in a Tomcat container with Gretty, and tested it with a simple HTML and JavaScript client.

This setup allows you to handle real-time two-way communication between your server and clients with both text and binary data.

Next steps could include adding authentication, handling more message types, or integrating with your existing Java applications.

Got any questions or comments? Join our thriving Developer Community on Slack, follow us on X (formerly Twitter), or subscribe to our Developer Newsletter. Stay connected, share your progress, and keep up with the latest developer news, tips, and events!

Share:

https://a.storyblok.com/f/270183/150x150/a3d03a85fd/placeholder.svg
Steve CrowVonage Alumni

Steve is a self-proclaimed Mathlete, and King of Snark. He is also a lover of Greyhounds, twisty puzzles, and European Board Games. When not talking math to non-math people, and Java to non-Java people, he can be found sipping coffee and hacking on code.