Websockets with Tomcat
January 05, 2022Time to read: 4 minute(s)
What is Websocket ?
It is communication protocol, which provides full duplex/bi-directional communication built on top of TCP protocol. The Websocket API is standardized by W3C committe and has wide support in almost all the browsers. For more details about the protocol check this
This post is about how to write server side websockets using spring and how to connect it using bare JavaScript. We will not be using extra 3rd party websocket libraries.
How to connect from Browser?
Let’s look at Websocket API now (Check original MDN article here). This is javascript of side socket initialization and usage.
// Create new connection
const socket = new WebSocket('ws://localhost:8080');
// Connection opened
socket.addEventListener('open', function (event) {
// Send messages
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
Server Side Web Socket Setup
Let’s create backend server for websockets. We will use popular spring boot based setup.
Copy paste this link in new browser tab to generate new spring boot project.
Click on “Generate” button.
Extract the zip file and change directory to project foloder
type ./gradlew clean build
in the terminal to verify that setup is building without errors.
Next we need to make few changes to our project to support websocket endpoint.
- Add Text based websocket handler. The
TextWebSocketHandler
extendsAbstractWebSocketHandler
which calls thehandleTextMessage
method when new message is received.
@Component
public class WebsocketHandler extends TextWebSocketHandler {
List<WebSocketSession> sessions = new CopyOnWriteArrayList<WebSocketSession>();
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
throws InterruptedException, IOException {
System.out.println("Message Received. message.payLoad: " + message.getPayload());
// Send message to all the connected clients
for (WebSocketSession webSocketSession : sessions) {
webSocketSession.sendMessage(new TextMessage("Hello " + message.getPayload() + "!"));
}
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// Add new session to list of all the sessions.
// This list will be used for broadcasting messages
sessions.add(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// Once client has disconnected, remove it from broadcast list
sessions.remove(session);
}
}
- Add Configuration file to enable web sockets and bind API endpoint
helloAll
to our text based web socket handler
package com.example.websocket;
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 {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new WebsocketHandler(), "/helloAll");
}
}
Cliet Side Implementation
- Create new file call
index.html
atsrc/main/resources/static
directory. - Copy the following code to the index.html
<html>
<head>
<title>Web Socket Demo</title>
</head>
<body>
<div>
<input id="input-msg" type="text" />
<button id="send-btn">Send Message</button>
<ul id="messages"></ul>
</div>
<script>
var connected = false;
function setConnected(val) {
connected = val;
}
function connect() {
ws = new WebSocket('ws://localhost:8080/helloAll');
ws.onmessage = function (data) {
showGreeting(data.data);
}
setConnected(true);
}
function disconnect() {
if (ws != null) {
ws.close();
}
setConnected(false);
console.log("Disconnected");
}
function showGreeting(message) {
let msgList = document.querySelector("#messages");
let newMsg = document.createElement('li');
newMsg.innerHTML = message;
msgList.appendChild(newMsg);
}
window.onload = function () {
connect();
document.querySelector('#send-btn').onclick = function () {
if (connected) {
ws.send(document.querySelector("#input-msg").value);
} else {
console.error("Websocket is not connected. Please connect first")
}
}
}
</script>
</body>
</html>
How to test the demo
- Go to
websocket
directory. - Type
./gradlew bootRun
to start the spring boot application. - Open new browser window and paste
http://localhost:8080/index.html
in the tab - Type your name and click on “Send Message” button. You will get reply from server.
- Now open another tab and do the same. You should see the previous tab is getting messages from latest tab as well. That’s our goal achieved.
Points to Ponder
- Does websocket holds the thread at server side ?
- Can websocket based application scale? If yes how ?
- Why most of the other examples show message broker based setup ?
- Is it blocking or non-blocking socket connection ?
I will update this very post with answers soon.
Complete code is available on the Github, here
Written by Prakash Borkar (@zeoneo) who lives and works in Hyderabad India building useful things. Follow on LinkedIn