How does a WebSocket sender split one message across several frames, and what does each frame carry?
answer
- type stated once, at the start
- FIN marks only the final frame
- later frames reuse one opcode
- no total length on the wire
- one message at a time per connection
basics
~20 sThe first frame carries the real opcode with FIN clear; every later frame carries opcode %x0 with FIN clear; the last carries opcode %x0 with FIN set. Each frame declares its own length, so the sender never needs the total in advance.
solid answer
~40 sFragmentation is signalled by two header fields working together. The **first** frame of the message carries its type — `%x1` for text or `%x2` for binary — with `FIN` clear. Every **subsequent** frame carries the continuation opcode `%x0`: clear `FIN` for the middle frames, set `FIN` on the final one. The type is therefore stated once and never repeated. Because each frame declares only its own length, a sender may begin emitting before it knows how large the whole message is — useful when the payload is being produced or read as it goes. Fragments of one message may not be interleaved with another message's on the same connection unless an extension defines that, so a connection carries one message at a time.
code
http · 8 lines01 03 48 65 6c
# FIN=0, opcode %x1 (text) -> message begins, 3 bytes
00 03 6c 6f 20
# FIN=0, opcode %x0 (continuation) -> more of the same message
80 02 21 21
# FIN=1, opcode %x0 (continuation) -> message ends herego deeper
Recall the shape: first frame carries the type with FIN clear, every later frame carries opcode %x0, and only the last frame has FIN set.
Explain why the type appears once and why no total length exists on the wire — it is what lets a sender start writing before it knows how big the message will be.
Argue the occupancy point: one huge frame holds the connection until its last byte, while fragments leave gaps where a control frame can be sent. Choose fragment sizes accordingly.
Decide a house policy for maximum frame and message size across your services, since the limit governs both memory exposure on receivers and how long one sender can occupy a connection.
## The three-frame pattern A fragmented WebSocket message is a run of frames with a strict shape: | position | FIN | opcode | |---|---|---| | first frame | 0 | the message type: `%x1` text or `%x2` binary | | middle frames (zero or more) | 0 | `%x0` continuation | | final frame | 1 | `%x0` continuation | An **unfragmented** message is the degenerate case of the same rule: one frame with `FIN` set and a non-zero opcode. That is what almost all traffic looks like. Two consequences fall straight out of the table. First, the message's type appears **exactly once**, on the opening frame — a middle frame that repeated `%x1` would be starting a new message, not continuing one. Second, a continuation frame with nothing open is a protocol error: `%x0` refers to a message the previous frames began, and with no such message the receiver has nothing to attach it to. ## Why a sender fragments Fragmentation is permitted, not required, and a sender chooses it for concrete reasons: - **The total size is not known yet.** Each frame declares only its own length, so a sender producing output as it goes can start writing immediately instead of buffering the whole message to measure it. - **The payload is too large to hold at once.** A sender streaming a large blob out of storage can emit it in bounded pieces without ever holding all of it in memory. - **Something else needs the wire.** This is the operationally important one: a control frame may be sent **between** the frames of a fragmented message, but never inside a frame. Splitting a large payload creates the gaps where a liveness probe or a close can get out. A receiver cannot generally tell *why* a sender fragmented, and the boundaries carry no application meaning — they are a transport decision. ## Rules the sender must keep 1. **State the type once.** Only the first frame carries `%x1` or `%x2`; the rest carry `%x0`. 2. **Set FIN exactly once, on the last frame.** Until it arrives the message is open. 3. **Do not interleave two messages.** Fragments of one message must not be interleaved with fragments of another on the same connection unless a negotiated extension defines how, so a connection carries one data message at a time and a large one blocks the next. 4. **Deliver fragments in order.** They travel in the order the sender wrote them; the protocol adds no sequence number to reorder by, and none is needed over a single ordered connection. 5. **Never fragment a control frame.** A close, ping or pong is always a single frame with `FIN` set. ## A worked sequence A sign controller sends the text `Hello` as two frames instead of one: ```http 01 03 48 65 6c # FIN=0, opcode %x1 (text), 3 bytes: "Hel" 80 02 6c 6f # FIN=1, opcode %x0 (continuation), 2 bytes: "lo" ``` `0x01` is FIN clear with opcode `%x1`; `0x80` is FIN set with opcode `%x0`. Nothing on the wire says the message totals five bytes — that fact only exists once the receiver has seen the frame with `FIN` set. ## What this costs and what it buys Each extra frame costs another header — two bytes at minimum, four more if the frame is masked and large enough to need an extended length. Splitting a 4 MB payload into 64 KB pieces adds a few hundred bytes of framing in total, which is nothing; splitting a 200-byte payload into 20 pieces is pure waste. The real trade-off is not bytes but **occupancy**. A single enormous frame holds the connection for as long as it takes to write, and nothing else can be sent in the meantime, because the next frame's header can only start after the current frame's last payload byte. The same payload sent as fragments yields the wire between pieces. That is why a sender that must keep a liveness probe answerable, or must be able to close promptly, fragments its large payloads rather than sending them whole — the gaps are the entire benefit.
- What does a receiver do with a continuation frame when no message is open?Fail the connection. Opcode `%x0` means "more of the message the earlier frames started", so with nothing open it refers to nothing, and the receiver has no way to know what type the payload even is. The matching close status code is 1002, protocol error.
- Can a sender fragment a ping or a close frame?No. Control frames must not be fragmented and must fit in 125 payload bytes, so each one is a single frame with FIN set. That guarantee is what lets a receiver act on a control frame the moment it has read it, without waiting for anything else.
- Does the receiver learn where the sender placed the fragment boundaries?Not in any way it should act on. The boundaries are a transport decision by the sender and carry no application meaning; a sender may pick them by buffer size, by production rate or not fragment at all, and the same message may be framed differently on the next send.
saying these in an interview costs you the question
- Repeats the text or binary opcode on every frame
- Thinks FIN is set on the first frame instead of the last
- Expects a total message length somewhere in the header
- Believes two messages can be interleaved frame by frame
- Says control frames may be fragmented like data frames