WebSockets | Networking - Wyatt's Notes
Overview
Section titled “Overview”WebSockets (RFC 6455) provide full-duplex, bidirectional communication over a single TCP connection. Unlike HTTP, which follows a request-response model, WebSocket allows either side to send data at Any time after the connection is established. This makes WebSockets the protocol of choice for Real-time applications: chat, collaboration, financial tickers, live dashboards, gaming, and IoT Device control.
WebSockets start as an HTTP upgrade. The client sends a regular HTTP request with an Upgrade: websocket header. If the server agrees, it responds with 101 Switching ProtocolsAnd The connection becomes a WebSocket from that point forward.
HTTP Upgrade Handshake
Section titled “HTTP Upgrade Handshake”Client Request
Section titled “Client Request”GET /chat HTTP/1.1Host: example.com:8080Upgrade: websocketConnection: UpgradeSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Sec-WebSocket-Version: 13Sec-WebSocket-Protocol: chat, superchatSec-WebSocket-Extensions: permessage-deflate; client_max_window_bitsOrigin: https://example.comKey headers:
- Upgrade: websocket — signals the protocol upgrade request
- Connection: Upgrade — required by HTTP/1.1 to indicate a connection-level upgrade
- Sec-WebSocket-Key — base64-encoded 16-byte random value (used in the handshake validation)
- Sec-WebSocket-Version — must be
13(the current WebSocket protocol version) - Sec-WebSocket-Protocol — optional; lists application-level subprotocols the client supports
- Sec-WebSocket-Extensions — optional; lists protocol-level extensions the client supports
Server Response
Section titled “Server Response”HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=Sec-WebSocket-Protocol: chatSec-WebSocket-Extensions: permessage-deflate; client_max_window_bitsHandshake Validation
Section titled “Handshake Validation”The Sec-WebSocket-Accept value is computed as follows:
- Take the value of
Sec-WebSocket-Keyfrom the client request:dGhlIHNhbXBsZSBub25jZQ== - Concatenate the globally unique UUID
258EAFA5-E914-47DA-95CA-C5AB0DC85B11 - Compute SHA-1 hash of the concatenated string
- Base64-encode the hash
## Verify the handshake computationKEY="dGhlIHNhbXBsZSBub25jZQ=="GUID="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"ACCEPT=$(printf "%s%s" "$KEY" "$GUID" | openssl dgst -sha1 -binary | base64)echo "$ACCEPT"## Output: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=This mechanism prevents a regular HTTP client from accidentally establishing a WebSocket connection, And prevents a WebSocket client from accidentally connecting to a non-WebSocket server.
sequenceDiagram participant C as Client participant S as Server C->>S: HTTP GET /chat (Upgrade: websocket) Note right of S: Validate Sec-WebSocket-Key<br/>Compute Sec-WebSocket-Accept S->>C: 101 Switching Protocols Note over C,S: TCP connection is now a WebSocket C->>S: WebSocket frame (text data) S->>C: WebSocket frame (text data) S->>C: WebSocket frame (text data) C->>S: WebSocket frame (close) S->>C: WebSocket frame (close)WebSocket Framing
Section titled “WebSocket Framing”After the handshake, communication uses a binary frame format:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len | Extended payload length ||I|S|S|S| (4) |A| (7) | (16/64) ||N|V|V|V| |S| | (if payload len==126/127) || |1|2|3| |K| | |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +| Extended payload length continued, if payload len == 127 |+ - - - - - - - - - - - - - - - +-------------------------------+| |Masking-key, if MASK set to 1 |+-------------------------------+-------------------------------+| Masking-key (continued) | Payload Data |+-------------------------------- - - - - - - - - - - - - - - - +: Payload Data continued ... :+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +| Payload Data (continued) |+---------------------------------------------------------------+Frame Fields
Section titled “Frame Fields”| Field | Bits | Description |
|---|---|---|
| FIN | 1 | 1 = final frame of message, 0 = more frames follow |
| RSV1, RSV2, RSV3 | 3 | Reserved for extensions. Must be 0 unless an extension is negotiated. |
| Opcode | 4 | Frame type (see opcodes table) |
| MASK | 1 | 1 = payload is masked (must be 1 for client-to-server frames) |
| Payload length | 7 | Length of payload: 0-125 = actual length, 126 = next 2 bytes, 127 = next 8 bytes |
| Extended payload length | 16/64 | Actual payload length when payload length is 126 or 127 |
| Masking key | 32 | 4-byte key used to unmask the payload (if MASK=1) |
| Payload data | var | The actual data |
Fragmentation
Section titled “Fragmentation”A single WebSocket message can be split across multiple frames. The first frame has FIN=0 and the Opcode indicates the message type. Subsequent frames have FIN=0 and opcode=0x0 (continuation). The Final frame has FIN=1 and opcode=0x0.
Frame 1: FIN=0, opcode=0x1 (text), payload="Hello "Frame 2: FIN=0, opcode=0x0 (continuation), payload="World"Frame 3: FIN=1, opcode=0x0 (continuation), payload="!"Control frames (ping, pong, close) MUST NOT be fragmented. They must fit in a single frame with FIN=1.
Masking
Section titled “Masking”Why Client-to-Server Frames Are Masked
Section titled “Why Client-to-Server Frames Are Masked”Masking prevents cache poisoning attacks. A malicious client could craft a WebSocket frame that Looks like an HTTP request or response and inject it into a shared cache (e.g., a CDN or proxy). Masking ensures that the payload on the wire differs from the actual payload, making it infeasible To craft a frame that matches a specific HTTP pattern.
The RFC 6455 specification requires all client-to-server frames to be masked. Server-to-client Frames MUST NOT be masked.
XOR Masking Algorithm
Section titled “XOR Masking Algorithm”The masking key is 4 bytes. Each byte of the payload is XORed with the corresponding byte of the key (cycling through the key):
j = i MOD 4transformed[i] = original[i] XOR mask[j]Example:
Payload: 0x48 0x65 0x6c 0x6c 0x6f (Hello)Mask key: 0x37 0xfa 0x21 0x3dMasked: 0x7f 0x9f 0x4d 0x55 0x58
Verification:0x48 XOR 0x37 = 0x7f (i=0, j=0)0x65 XOR 0xfa = 0x9f (i=1, j=1)0x6c XOR 0x21 = 0x4d (i=2, j=2)0x6c XOR 0x3d = 0x55 (i=3, j=3)0x6f XOR 0x37 = 0x58 (i=4, j=0, wraps)Intuition
Section titled “Intuition”WebSockets are like a phone call versus sending letters. HTTP is like sending letters back and forth - each message requires a new request and response. WebSockets are like picking up the phone - once connected, either party can speak at any time without waiting for the other to finish. The upgrade handshake is like calling someone and asking “Can we switch from letters to a phone call?” The server agrees, and from then on, the connection stays open for instant two-way communication. This is essential for real-time applications like chat, gaming, and live feeds where you need immediate updates without the overhead of establishing a new connection for each message.
Cross-References
Section titled “Cross-References”- HTTP - How the WebSocket handshake upgrades from an HTTP connection
- HTTP/2 and HTTP/3 - How HTTP/2 Server Push and streams relate to WebSocket real-time patterns
- TLS - How WSS (WebSocket Secure) wraps WebSocket connections in TLS