Message framing

Design a newline protocol

Define one command per line with explicit encoding, replies, errors, and connection closure.

TCP gives you a byte stream with no message boundaries. The simplest fix is a newline protocol: one message per line, terminated by \n.

This is how a lot of real protocols work. SMTP, POP3, and HTTP/1.1 headers are all line-based text. Lines are easy to type, easy to log, and easy to debug with netcat.

A small text protocol still needs grammar. Decide the encoding, the commands, and the replies before writing any parser code. We will use UTF-8 lines ending in \n, commands PING and ECHO text, and numeric replies.

Write the protocol before the parser:

PING          -> 200 PONG\n
ECHO hello    -> 200 hello\n
anything else -> 400 unknown command\n

The numeric code plays the same role as HTTP status codes. A client can branch on 200 versus 400 without parsing the human-readable part after it.

Test the contract by hand

The whole point of a text protocol is that you can be the client. Test the examples with nc 127.0.0.1 4000:

nc 127.0.0.1 4000
PING
200 PONG
ECHO hello
200 hello
DELETE
400 unknown command

You type a line, the server answers with a line. The written contract tells both endpoints when one message ends: at the \n.

The limit you must set

Set a maximum line length, and make it part of the contract:

maximum line length: 4096 bytes
on violation: 400 line too long, then close

Here’s why. Your server buffers bytes until it sees \n. A client must not be able to grow the server buffer forever by withholding a newline. Without the limit, one shell one-liner piping /dev/zero into nc exhausts your server’s memory.

One more decision while you’re writing the grammar: what happens to \r? Clients like telnet send \r\n line endings. Either strip a trailing \r before parsing or document that bare \n is required. Leaving it ambiguous produces “works with my client, fails with yours” bugs that waste an afternoon.

Lesson completed