Message framing
Design a newline protocol
Define one command per line with explicit encoding, replies, errors, and connection closure.
10 minute lesson
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