Message framing

Send length-prefixed messages

Frame binary or multiline data with an explicit length and parse it without reading past the message.

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

Take this course offline

Get every free book, course edition, and software download.

Get the download library →