Reading — step 1 of 5
Read
Close Handshake
Why does WebSocket bother with a close handshake when either side could just close the TCP socket? Because "the connection ended" and "the conversation ended" are different facts. A bare TCP teardown can mean a crash, a network drop, or an attacker truncating the stream mid-message. The CLOSE exchange makes clean shutdown distinguishable: both sides know all data was delivered and the ending was intentional.
Endpoint A -> CLOSE frame (opcode 0x8, code + reason)
Endpoint B -> CLOSE frame in reply
TCP teardown (RFC 6455: the server SHOULD close TCP first)
After sending CLOSE you may not send data frames anymore; after the exchange completes, the socket comes down.
The close payload, byte by byte
A CLOSE frame's payload is optional, but if present it is:
bytes 0-1 : status code, big-endian u16
bytes 2+ : UTF-8 reason string (human-readable, optional)
The whole payload obeys the control-frame cap of 125 bytes, so the reason has at most 123 bytes. Code 1000 with reason "OK" is the 4 bytes 03 e8 4f 4b.
Standard codes (RFC 6455 §7.4.1):
| Code | Meaning |
|---|---|
| 1000 | Normal closure |
| 1001 | Going away (tab closed, server shutting down) |
| 1002 | Protocol error |
| 1003 | Unsupported data type |
| 1007 | Invalid payload (e.g. broken UTF-8 in a TEXT message) |
| 1008 | Policy violation |
| 1009 | Message too big |
| 1011 | Server encountered an internal error |
| 3000–3999 | Registered for libraries/frameworks |
| 4000–4999 | Private application use |
The codes that never touch the wire
Three codes are reserved as local signals only — an endpoint MUST NOT send them, but your API reports them:
- 1005 — no status code was present. A CLOSE frame with an empty payload is perfectly legal; the receiving side reports 1005 to application code.
- 1006 — abnormal closure. TCP died with no CLOSE frame at all (the half-open laptop from last lesson). Nothing was received; the library synthesizes 1006 locally.
- 1015 — the TLS handshake failed before a WebSocket ever existed; again purely a local report.
Ill-formed payloads exist too, and a parser must reject them: a 1-byte payload is a protocol error (a status code is two bytes — one byte is a truncated code), and a reason that fails UTF-8 validation is invalid (the RFC requires close reasons to be UTF-8).
Your exercise
Parse close payloads with exactly those five behaviors. The classic mistake the grader catches is byte order: the code is big-endian, so payload 03e8 must print code=1000 — read it little-endian and you'll print code=59395. The edge cases each have one exact expected line: single byte ab → ERR truncated; payload 03e8c328 → ERR invalid-utf8 (0xc3 opens a 2-byte UTF-8 sequence but 0x28 is not a continuation byte); and 03e8636166c3a9 → code=1000 reason=café — decode the reason as UTF-8, not latin-1, or the accent mangles.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…