Message framing
Send length-prefixed messages
Frame binary or multiline data with an explicit length and parse it without reading past the message.
10 minute lesson
Newline framing breaks down when the payload can contain newlines: binary data, file contents, multiline text. A length prefix removes the problem. A length prefix lets the payload contain any byte, including newlines.
The sender writes a fixed-size header holding the payload size, then the payload itself. The receiver first reads the fixed-size length, then exactly that many payload bytes. No delimiter, no escaping.
Encode one four-byte length and payload:
const payload = Buffer.from('hello')
const frame = Buffer.alloc(4 + payload.length)
frame.writeUInt32BE(payload.length, 0)
payload.copy(frame, 4)
socket.write(frame)
writeUInt32BE stores the length big-endian, most significant byte first. That’s network byte order, the convention wire protocols use for multi-byte integers. Both sides must agree: a big-endian writer paired with a little-endian reader turns length 5 into 83,886,080.
Capture the bytes and confirm:
console.log(frame.toString('hex'))
// 0000000568656c6c6f
The first four bytes represent 5 in network byte order, followed by 68 65 6c 6c 6f — “hello”.
Parse without reading past the message
The receiver accumulates a Buffer and loops. Build a parser that waits for the complete frame:
let pending = Buffer.alloc(0)
socket.on('data', chunk => {
pending = Buffer.concat([pending, chunk])
while (pending.length >= 4) {
const size = pending.readUInt32BE(0)
if (size > 1_000_000) return socket.destroy()
if (pending.length < 4 + size) break
handleMessage(pending.subarray(4, 4 + size))
pending = pending.subarray(4 + size)
}
})
The while loop matters: one chunk may contain several complete frames, and each must be handled. The break matters too: if the payload hasn’t fully arrived, stop and wait for the next data event instead of consuming a partial frame.
The size check is not decoration. Reject impossible or excessive lengths before allocating memory or waiting for payload data. A single hostile 4-byte header claiming a 4 GB payload would otherwise make your server buffer forever for a message that never comes — pick a ceiling your protocol actually needs and destroy the connection past it.
Lesson completed