const net = require("node:net");
net.createServer((socket) => {
socket.on("data", (chunk) => {
const msg = JSON.parse(chunk.toString());
handle(msg);
});
}).listen(4000);
It breaks because TCP is a byte stream with no message boundaries. TCP does not keep the sender's writes separate:
- One write can arrive split across several
dataevents. - Several writes can arrive merged into one
dataevent.
The code often works on localhost with small messages. It then fails under load or across real networks. There, JSON.parse gets half a message, or two messages glued together.
The fix is framing at the application layer. Framing means marking where each message starts and ends. Two common choices:
- A delimiter, such as newline-delimited JSON. You buffer incoming data and split it on
\n. - A length prefix. Each message starts with its length, for example in 4 bytes. You wait until that many bytes have arrived.
Keep one buffer per connection. Parse only complete frames.
Protocols like HTTP, WebSocket and database wire protocols already do this framing for you.
- TCP is a byte stream, so writes and reads don't line up.
- One message can be split across data events, or several messages can be merged into one.
- The code works locally but fails under load.
- Fix it with delimiter or length-prefix framing and a buffer for each connection.
Read more