Master WebSocket: From Handshake to Real‑Time Messaging with Java & Spring
This tutorial explains WebSocket’s full‑duplex protocol, compares it with HTTP polling, outlines typical real‑time use cases, details the handshake and frame structure, and provides complete client‑side JavaScript and Spring Boot server examples to help developers quickly implement bi‑directional communication.
What is WebSocket
WebSocket is a TCP‑based protocol that provides full‑duplex communication between a web client and a server after an HTTP handshake upgrades the connection.
Key Features
Server can push messages to the client without the client having to poll.
Low latency and reduced network overhead compared with the HTTP request/response model.
Typical Use Cases
Real‑time chat, multiplayer games, stock market tickers, IoT device monitoring.
Handshake Process
The client sends an HTTP request containing the headers Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key and Sec-WebSocket-Version. The server validates the key, computes Sec-WebSocket-Accept, and replies with 101 Switching Protocols.
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13 HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=Data Frame Structure
FIN (1 bit): final fragment flag.
Opcode (4 bits): payload type (0x1 text, 0x2 binary, 0x8 close, 0x9 ping, 0xA pong).
Mask (1 bit): client‑to‑server frames are masked.
Payload Length (7, 7+16, or 7+64 bits): length of the payload, up to 2^64‑1 bytes.
Masking‑key (32 bits, present only if Mask=1).
Payload data: the actual message bytes.
Connection Lifecycle
Client initiates the handshake request.
Server responds with 101 Switching Protocols.
The TCP connection is upgraded to the WebSocket protocol.
Bidirectional data exchange occurs using frames.
Either side may close the connection with a close frame.
Client‑Side Example (HTML + JavaScript)
<!DOCTYPE html>
<html>
<body>
<input type="text" id="messageInput" placeholder="Enter message">
<button onclick="sendMessage()">Send</button>
<div id="messages"></div>
<script>
const socket = new WebSocket('ws://localhost:8080/ws');
socket.addEventListener('open', () => log('Connection opened'));
socket.addEventListener('message', e => log('Received: ' + e.data));
socket.addEventListener('close', () => log('Connection closed'));
socket.addEventListener('error', err => log('Error: ' + err.message));
function sendMessage() {
const msg = document.getElementById('messageInput').value;
socket.send(msg);
log('Sent: ' + msg);
}
function log(msg) {
const p = document.createElement('p');
p.textContent = msg;
document.getElementById('messages').appendChild(p);
}
</script>
</body>
</html>Server‑Side Example (Spring Boot)
Add the WebSocket starter dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.7.14</version>
</dependency>Enable WebSocket and register a handler:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/ws")
.setAllowedOrigins("*");
}
}Implement a simple text‑message handler that broadcasts received messages to all connected sessions:
public class MyWebSocketHandler extends TextWebSocketHandler {
private static final Set<WebSocketSession> sessions =
Collections.synchronizedSet(new HashSet<>());
@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
System.out.println("[Handler] New connection: " + session.getId());
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
String payload = message.getPayload();
System.out.println("[Handler] Received: " + payload);
for (WebSocketSession s : sessions) {
if (s.isOpen() && !s.equals(session)) {
try {
s.sendMessage(new TextMessage("Broadcast: " + payload));
} catch (Exception e) {
System.out.println("[Handler] Send failed: " + e.getMessage());
}
}
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
sessions.remove(session);
System.out.println("[Handler] Closed: " + session.getId());
}
}Implementation Steps Summary
Client sends an HTTP request with Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key, and Sec-WebSocket-Version.
Server validates the key, computes Sec-WebSocket-Accept, and returns 101 Switching Protocols.
The TCP connection is upgraded; both sides may now exchange frames.
Data frames are exchanged; client frames are masked, server frames are not.
Either side can close the connection by sending a close frame.
Illustrative Frame Diagram
Senior Tony
Former senior tech manager at Meituan, ex‑tech director at New Oriental, with experience at JD.com and Qunar; specializes in Java interview coaching and regularly shares hardcore technical content. Runs a video channel of the same name.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
