WebSockets
IntermediateWebSocket provides a full-duplex, persistent connection over a single TCP connection — enabling the server to push data to the client without polling, ideal for chat, live feeds, and collaborative apps.
Overview
HTTP is inherently request-response: the client always initiates, the server responds, and the connection may close. For real-time applications (chat, live dashboards, collaborative editing, gaming), this model is inefficient — clients must repeatedly poll for updates. WebSocket solves this with a persistent, bidirectional channel. It starts with an HTTP/1.1 Upgrade handshake and then upgrades the connection to the WebSocket protocol (RFC 6455). Once established, either side can send a frame at any time with minimal overhead (2–10 byte header vs HTTP's 500+ bytes). The connection stays open until explicitly closed. WebSocket works over port 80 (ws://) or 443 (wss:// — WebSocket over TLS). For high-scale servers, WebSocket connections are long-lived — a 10,000 concurrent connection server must manage memory and file descriptors carefully, favoring non-blocking I/O (Spring WebFlux, Netty) over thread-per-connection models.
WebSocket Handshake and Frame Format
WebSocket upgrades an HTTP connection. The client sends an Upgrade request; the server responds with 101 Switching Protocols. From that point, the connection is no longer HTTP — it speaks the WebSocket binary frame protocol.
// WebSocket Upgrade handshake:
// Client → Server:
// GET /chat HTTP/1.1
// Host: ws.example.com
// Upgrade: websocket
// Connection: Upgrade
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== ← random base64 nonce
// Sec-WebSocket-Version: 13
//
// Server → Client:
// HTTP/1.1 101 Switching Protocols
// Upgrade: websocket
// Connection: Upgrade
// Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= ← SHA-1 of key+"GUID"
//
// ═══ Connection is now WebSocket — HTTP is gone ═══
// WebSocket frame header (2–10 bytes):
// ┌────┬────────┬────────────────────────┐
// │FIN │ Opcode │ Payload Length + Mask │
// └────┴────────┴────────────────────────┘
// Opcodes: 0x1=text, 0x2=binary, 0x8=close, 0x9=ping, 0xA=pong
// Client→Server frames are masked (4-byte XOR mask); Server→Client are not
// Java WebSocket server (Spring Boot):
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatHandler(), "/chat").setAllowedOrigins("*");
}
}
public class ChatHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
session.sendMessage(new TextMessage("Echo: " + message.getPayload()));
}
}Scaling WebSockets and Comparison with SSE
WebSocket connections are stateful and long-lived — each connection holds a session object in memory. Scaling across multiple server instances requires a shared pub/sub layer (Redis, Kafka) to route messages to the correct server holding each client's connection.
// Scaling WebSockets — sticky sessions OR pub/sub fan-out:
//
// Problem: Client A connected to Server 1, Client B to Server 2
// Server 1 wants to send message to Client B → doesn't have its session
//
// Solution — Redis pub/sub fan-out:
// Server 1 publishes to Redis channel "room:123"
// Server 2 subscribes to "room:123" → receives message → sends to Client B's session
// Spring Boot WebSocket + Redis pub/sub:
@Autowired StringRedisTemplate redis;
// Send to Redis when message arrives:
redis.convertAndSend("room:" + roomId, jsonMessage);
// Subscribe and forward to local WebSocket sessions:
redis.listenTo(ChannelTopic.of("room:" + roomId), (message, pattern) -> {
localSessions.forEach(session -> session.sendMessage(new TextMessage(message)));
});
// WebSocket vs Server-Sent Events (SSE):
// ┌─────────────────┬─────────────────┬──────────────────────┐
// │ Property │ WebSocket │ SSE │
// ├─────────────────┼─────────────────┼──────────────────────┤
// │ Direction │ Full-duplex │ Server → Client only │
// │ Protocol │ WebSocket (ws) │ HTTP │
// │ Reconnect │ Manual │ Automatic │
// │ Binary support │ Yes │ No (text only) │
// │ Proxy-friendly │ Sometimes │ Yes (plain HTTP) │
// │ Use case │ Chat, gaming │ Live feed, progress │
// └─────────────────┴─────────────────┴──────────────────────┘Key Points to Remember
- 1WebSocket upgrades an HTTP/1.1 connection to a persistent, full-duplex channel via HTTP 101.
- 2Frame header is 2–10 bytes — far lower overhead than HTTP for frequent small messages.
- 3wss:// = WebSocket over TLS (use always in production).
- 4Long-lived connections require careful resource management — prefer non-blocking I/O (Netty, WebFlux).
- 5Scale-out requires sticky sessions or a pub/sub layer (Redis, Kafka) to route messages across servers.
- 6SSE is simpler for server-push-only use cases (live feeds, progress) — uses plain HTTP.
Interview Questions
Sign in to ask AriaHow does a WebSocket connection get established?
What is the difference between WebSocket and HTTP long polling?
How would you scale a WebSocket-based chat application to multiple server instances?
When would you choose SSE over WebSocket?
What happens to WebSocket connections behind a load balancer that terminates sessions?
Ask Aria about WebSockets
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.