Implementing a Spring Websocket Server and Client

Share this article

Implementing a Spring Websocket Server and Client

This article guides you through the implementation of a WebSocket server and client based on the Spring Framework. It demonstrates full duplex communication and how the server can push messages to the client. You will learn a little about how WebSockets work, as well as the STOMP messaging format used for communication between the server and the client.

To get the most out of his article, you need good knowledge of Java, along with some exposure to the Spring Framework, especially Spring MVC. The source code for the server that we are oing to implement is available on GitHub.

Cliff’s Notes On Websockets, STOMP, and Spring

To get you up and running with Websocket on the JVM, here’s a quick intro to what we’ll need later in the article.

Introduction to WebSockets

WebSocket is a full-duplex communications protocol layered over TCP. It is typically used for interactive communication between a user’s browser and a back-end server. An example would be a chat server with real-time communications between the server and the connected clients. Another example would be a Stock Trading application where the server sends stock price variations to subscribed clients without an explicit client request.

A WebSocket is a communication channel which uses TCP as the underlying protocol. It is initiated by the client sending a HTTP request to the server requesting a connection upgrade to WebSocket. If the server supports WebSockets, the client request is granted and a WebSocket connection is established between the two parties. After this connection is established, all communication happens over the WebSocket and the HTTP protocol is no longer used.

This article does a very good job of explaining the details of how WebSockets work.

STOMP over WebSocket

The WebSocket communication protocol itself does not mandate any particular messaging format. It is up to the applications to agree upon the format of the messages exchanged. This format is referred to as the subprotocol. (Kinda similar to how the web browser and web server have agreed to using the HTTP protocol over TCP sockets.)

One commonly used format is the STOMP protocol (Streaming Text Oriented Message Protocol) used for general purpose messaging. Various message oriented middle-ware (MOM) systems such as Apache ActiveMQ, HornetQ and RabbitMQ support STOMP.

The Spring Framework implements WebSocket communication with STOMP as the messaging protocol.

Spring Support for WebSockets

Spring Framework provides support for WebSocket messaging in web applications. Spring version 4 includes a new module spring-websocket, which is used to add WebSocket support to the server and the client.

The Spring Framework Guide includes a detailed HOWTO demonstrating a sample server implementation. However, it is not necessary to have read that guide; we cover all the details required for understanding the implementation in this article.

One shortcoming of the Spring Framework Guide is that no information is presented on implementing a Java/Spring Client to communicate with the server. We address that issue in this article. As you will see, the implementation requires correct incantations to get it working just right.

Spring Chat Server Implementation

With the prerequisites out of the way, it is time to implement the chat server. Here’s a rundown of how it works:

  • The Spring chat server creates an HTTP endpoint (/chat), which is used for establishing the WebSocket channel.
  • Once the channel is established, the server and client exchange messages over the channel.
  • While the server listens to and responds to client messages, the client can also register a callback for “push” messages from the server. This allows the server to send notifications to the client as and when required without an explicit client request.

The message “push” from the server to the client is what makes the WebSocket communication different from normal HTTP interaction.

Build Configuration

The Maven dependencies required for the server are shown below. The spring-boot-starter-websocket dependency includes the required libraries for server side implementation of WebSockets.

The remaining org.webjars dependencies are jQuery, Bootstrap and client-side libraries for WebSocket and STOMP messaging required by the HTML/Javascript chat client. These Javascript dependencies are not used by the Java WebSocket server implementation. They are required for the front-end client which is completely separate from the back-end application. These dependencies can safely be removed from the POM if the HTML/Javascript client is not used or packaged separately from the Spring Application.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>webjars-locator</artifactId>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>sockjs-client</artifactId>
  <version>1.0.2</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>stomp-websocket</artifactId>
  <version>2.3.3</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>bootstrap</artifactId>
  <version>3.3.7</version>
</dependency>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>jquery</artifactId>
  <version>3.1.0</version>
</dependency>

Message Controller

In Spring MVC, decorating a class with the @Controller annotation marks the class as a web controller. This works in conjuntion with the @RequestMapping decoration to mark a method as an HTTP endpoint. In a similar way, the annotation @MessageMapping is available with Spring Framework Messaging to register the method as a message listener.

For our simple chat server, the response echoes the message received from client and adds a couple of fields. For a real-world application, this method is where the business logic is implemented. Here is the implementation of the method:

@MessageMapping("/chat/{topic}")
@SendTo("/topic/messages")
public OutputMessage send(
        @DestinationVariable("topic") String topic, Message message)
        throws Exception {
    return new OutputMessage(message.getFrom(), message.getText(), topic);
}

The Message Controller method accepts a POJO (a Plain Old Java Object) into which a message is de-serialized from JSON. The @DestinationVariable annotation is used to capture the template variable topic from the destination. The following Message class is defined by the server to read messages from the client.

public class Message {

    private String from;
    private String text;

    // adding getters and setters here
}

To send responses back to the client, the message controller method uses a @SendTo annotation specifying the client-side queue to which the response is to be sent. The value returned from the method is serialized to JSON and sent to the specified destination. In our case, we define the response message as follows. Note that the input message field text is just copied to the response message field message. In your application, you can do whatever additional processing required and also include more fields in the request or response classes.

public class OutputMessage {

    private String from;
    private String message;
    private String topic;
    private Date time = new Date();

    // add getters and setters here
}

Configuring Spring

The application is configured for WebSockets using a @Configuration class which extends the AbstractWebSocketMessageBrokerConfigurer and provides implementation for the following methods.

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic");
    config.setApplicationDestinationPrefixes("/app");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/chat").setAllowedOrigins("*").withSockJS();
}

Note the following points:

  • The application destination prefix is set to “/app“. This means WebSocket mappings are prefixed with “/app” to obtain the real value.
  • The endpoint “/chat” is registered for starting the WebSocket protocol handshake.
  • The method setAllowedOrigins("*") is used to CORS-enable the server so cross-origin clients can connect to the server. If your requirements do not need cross-origin client connection, you can remove this invocation.

A client and server communicating via WebSockets

Web Browser Client

A sample web application written in HTML and Javascript is included in the code – it implements a simple front-end for the chat application. A snapshot of the application is shown below.

Browser Chat Client

Connecting and Registering Push Callback

The application uses the SocksJS library for dealing with the WebSocket connection, and the stomp-websocket library for the STOMP support. The connection code below creates a WebSocket channel and uses it to create the STOMP client. Once the WebSocket is connected, a callback is registered for push messages from the server. The message is de-serialized from JSON and displayed to the user.

var wsocket = new SockJS('/chat');
var client = Stomp.over(wsocket);
client.connect({}, function(frame) {
    client.subscribe('/topic/messages', function (message) {
        showMessage(JSON.parse(message.body));
    });
});

Sending Messages to the Server

The client sends messages to the server using send(). The message is in JSON and in the same format as expected by the message controller at the server end.

client.send("/app/chat/" + topic, {}, JSON.stringify({
    from: $("#from").val(),
    text: $('#text').val(),
}));

Building and Running the Application

The server is implemented as a Spring Boot application and includes an embedded web server. Build the application using Maven.

mvn clean package

Run the server specifying the port the web server should listen on if required.

java -Dserver.port=9090 -jar target/chat-server-0.1.0.jar

Once the server is started, open the Chat application at http://<hostname>:9090/

Java/Spring Chat Client

While an HTML/Javascript client is useful for demonstrating WebSocket usage in the browser, a Java client is useful for interacting with the server from within an application. Maybe you have a Forex Trading application which needs to report updated prices to all connected applications. Or maybe you want to subscribe to published messages to get update notifications. Whatever the use case, it is useful to learn how to interact with a WebSocket server from within a Java application.

Set up the underlying transports and create a SockJS client. The SockJS client module within Spring implements the sockjs protocol (here, the JS is just part of the name – no JavaScript involved) as a fallback option in case WebSocket is not supported on the (browser) client. This allows applications to use WebSockets but to fall back to other alternatives when necessary at run time – without needing to change application code. An alternative transport is Ajax/XHR streaming used by Internet Explorer 8 and 9.

WebSocketClient simpleWebSocketClient = new StandardWebSocketClient();
List<Transport> transports = new ArrayList<>(1);
transports.add(new WebSocketTransport(simpleWebSocketClient));
SockJsClient sockJsClient = new SockJsClient(transports);

The SockJsClient is used to create a STOMP client which supports the messaging protocol used.

WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);

And of course we need to map JSON to POJOs and back.

stompClient.setMessageConverter(new MappingJackson2MessageConverter());

Connect to the server and once the connection is made, a message handler is registered to listen for push messages from the server.

String url = "ws://localhost:9090/chat";
StompSessionHandler sessionHandler = new MyStompSessionHandler();
StompSession session = stompClient.connect(url, sessionHandler).get();

Subscribe to messages from the server as follows:

session.subscribe(topic, new StompFrameHandler() {
    @Override
    public Type getPayloadType(StompHeaders headers) {
        return ServerMessage.class;
    }

    @Override
    public void handleFrame(StompHeaders headers,Object payload) {
        System.err.println(payload.toString());
    }
});

Send messages to the server as follows:

ClientMessage msg = new ClientMessage(userId, line);
session.send("/app/chat/java", msg);

The complete source code for the client can be downloaded here.

Summary

Let’s review.

We started with a brief introduction to WebSockets and STOMP. STOMP is a messaging sub-protocol running over WebSockets which provides facilities such as topic subscriptions.

The Spring Framework (used for the WebSocket server) provides modules for WebSocket both for the server as well as the client. The modules are easily configured with annotations defined by Spring. Spring also handles serialization and deserialization of messages to and from POJOs.

The Web browser client shows the interaction with the server and how push messages from the server can be received and processed. Finally the Spring Client illustrates how to communicate with the WebSocket server and subscribe to message topics.

Frequently Asked Questions (FAQs) about Implementing Spring WebSocket Server and Client

What is the role of STOMP in Spring WebSocket?

STOMP, or Simple Text Oriented Messaging Protocol, is a messaging protocol that defines a format for data exchange using a message broker. In the context of Spring WebSocket, STOMP is used to handle messaging between the client and server. It provides an interoperable wire format that allows STOMP clients to communicate with any STOMP message broker to provide easy and widespread messaging interoperability among many languages, platforms and brokers.

How can I secure my Spring WebSocket application?

Securing your Spring WebSocket application involves several steps. First, you need to configure Spring Security to enable WebSocket security. This can be done by extending the AbstractSecurityWebSocketMessageBrokerConfigurer class and overriding the configureInbound method. You can then specify the security constraints for your application. Additionally, you can use the @PreAuthorize annotation to secure your message handling methods.

How can I handle errors in Spring WebSocket?

Error handling in Spring WebSocket can be achieved by implementing a custom ErrorHandler. This handler can be registered with the WebSocket connection manager. When an error occurs, the handleTransportError method of your custom ErrorHandler will be called. You can then implement your own logic to handle the error, such as logging the error or sending an error message to the client.

How can I test my Spring WebSocket application?

Testing a Spring WebSocket application can be done using the Spring Test framework. This framework provides the TestWebSocketClient class, which can be used to send and receive WebSocket messages in a test environment. You can use this class to simulate client behavior and verify that your server handles WebSocket messages correctly.

How can I handle disconnections in Spring WebSocket?

Handling disconnections in Spring WebSocket can be done by implementing a SessionDisconnectEvent handler. This handler will be called when a WebSocket session is closed. You can then implement your own logic to handle the disconnection, such as cleaning up resources or notifying other clients.

How can I send messages to specific users in Spring WebSocket?

Sending messages to specific users in Spring WebSocket can be done using the SimpMessagingTemplate class. This class provides the convertAndSendToUser method, which can be used to send a message to a specific user. You need to provide the username and the destination of the message, as well as the message itself.

How can I handle large messages in Spring WebSocket?

Handling large messages in Spring WebSocket can be done by configuring the message size limits. This can be done by setting the maxTextMessageBufferSize and maxBinaryMessageBufferSize properties of the WebSocket configuration. These properties determine the maximum size of text and binary messages that can be handled by the WebSocket connection.

How can I use Spring WebSocket with a load balancer?

Using Spring WebSocket with a load balancer requires some additional configuration. You need to ensure that WebSocket connections are sticky, meaning that all messages for a particular WebSocket session are always sent to the same server. This can be achieved by configuring your load balancer to use session-based stickiness.

How can I handle backpressure in Spring WebSocket?

Handling backpressure in Spring WebSocket can be done by implementing a custom BackPressureStrategy. This strategy can be used to control the rate at which messages are sent to the client, preventing the client from being overwhelmed by too many messages.

How can I integrate Spring WebSocket with other technologies?

Integrating Spring WebSocket with other technologies can be done using the STOMP protocol. STOMP provides an interoperable wire format that allows STOMP clients to communicate with any STOMP message broker. This means that you can use Spring WebSocket to communicate with applications written in other languages or using other technologies, as long as they support STOMP.

Jay SridharJay Sridhar
View Author

Software Architect and Founder of Novixys Software. Full Stack Developer with over 15 years experience. Experienced within Financial, Security, Publishing and Pharamaceutical industries. Currently working with Big Data.

nicolaipSpringwebsocket
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week