Skip to content
Lesson 5 of 29

Step 1 of 3 · Reading · ~3 min

The Real RESP Wire Protocol

RESP Protocol Fundamentals

RESP Array Parsing — The Real Wire Protocol

Everything up to now has used a shortcut: space-separated text commands. That was fine for learning RESP replies, but it is not how any real Redis client — redis-cli, redis-py, ioredis — actually talks to a server. Real clients send requests as RESP arrays of bulk strings. This lesson replaces your toy line parser with the real thing.

Why space-splitting breaks

Space-separated parsing cannot represent a value that itself contains a space, a newline, or arbitrary binary bytes (image data, serialized objects, etc.). SET note "hello world" is already ambiguous with naive splitting. Real Redis sidesteps this entirely by never guessing where a value ends — every piece of data is prefixed with its exact byte length.

The RESP array format

A client request is encoded as:

*<N>\r\n
$<len1>\r\n<arg1>\r\n
$<len2>\r\n<arg2>\r\n
...
$<lenN>\r\n<argN>\r\n

SET foo bar becomes:

*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n

Read it as: "an array of 3 elements, first element is a 3-byte bulk string SET, second is a 3-byte bulk string foo, third is a 3-byte bulk string bar."

Writing a binary-safe parser

The key discipline: never split on whitespace or newlines to find where a bulk string ends — trust the length prefix. Read exactly len bytes, then consume the trailing \r\n unconditionally.

python

Note that read_line is only ever used for the headers (*N and $len), which are guaranteed to be plain ASCII digits with no embedded CRLF. The payload itself is read with a fixed-size read(length), never scanned for delimiters — that's what makes it binary-safe.

Wiring it into your dispatcher

Once parsed, args[0] is your command name and args[1:] are its arguments — feed them into the same HANDLERS dispatch table you already built for PING/ECHO/etc. Nothing about command execution changes; only how you get from "bytes on the wire" to "a list of strings" changes.

Edge cases

  • Empty arrays (*0\r\n) should probably be ignored or produce no reply, mirroring how the passive-connection keepalive works in real Redis.
  • Argument values may be empty strings ($0\r\n\r\n) — that's valid and different from null ($-1\r\n).
  • Don't assume the whole request arrives in one read() call if you're working over a real socket later — for now, with a buffered stdin stream, reading byte-by-byte (or line-by-line for headers) keeps things correct even if not maximally fast.

This parser is the real foundation the rest of the course builds on: every future command — lists, hashes, transactions, pub/sub — arrives as exactly this array-of-bulk-strings shape.

Up nextSET & GET — Key-Value StorageString Commands

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…