Hello, hello, hello
Before any data flows, TCP's three-way handshake synchronises both sides: the client sends SYN, the server answers SYN+ACK, the client confirms with ACK. Closing takes four steps, because each direction is shut separately. Every TCP connection on your machine is a little state machine.
States and transitions
const TCP = {
CLOSED: { listen: ["LISTEN", null], connect: ["SYN_SENT", "SYN"] },
LISTEN: { SYN: ["SYN_RECEIVED", "SYN+ACK"] },
SYN_SENT: { "SYN+ACK": ["ESTABLISHED", "ACK"] },
SYN_RECEIVED: { ACK: ["ESTABLISHED", null] },
ESTABLISHED: { close: ["FIN_WAIT_1", "FIN"], FIN: ["CLOSE_WAIT", "ACK"] },
FIN_WAIT_1: { ACK: ["FIN_WAIT_2", null] },
FIN_WAIT_2: { FIN: ["TIME_WAIT", "ACK"] },
CLOSE_WAIT: { close: ["LAST_ACK", "FIN"] },
LAST_ACK: { ACK: ["CLOSED", null] },
};
function createSocket() {
return {
state: "CLOSED",
handle(event) {
const t = TCP[this.state]?.[event];
if (!t) throw new Error(`${event} not allowed in ${this.state}`);
this.state = t[0];
return t[1]; // the segment to send back, or null
},
};
}
function connect(client, server) {
const log = [];
server.handle("listen");
let segment = client.handle("connect");
let from = "client";
while (segment) {
log.push(`${from} → ${segment}`);
const to = from === "client" ? server : client;
from = from === "client" ? "server" : "client";
segment = to.handle(segment);
}
return log;
}