Two hosts exchange TCP segments one at a time — open a connection from either side, accept it, refuse it or reset it, send data and choose how much, lose or reorder segments on the way and retransmit them, and see which segment every ACK acknowledges. With TCP in short lines beside it, each linked to the RFC section behind it.
TCP numbers every byte it sends, and every ACK names the next byte it expects, so each acknowledgment
can be traced back to the segments it covers.
- Host A sends from left to right, Host B from right to left.
- Step plays six scenes, one after another:
- the handshake, data and close;
- a lost segment and its retransmission;
- segments arriving out of order;
- a refused connection and a second attempt;
- both sides closing at once;
- a connection reset.
- Each scene starts from two closed hosts, and its segments are numbered from #1.
- A read more link points at the RFC section behind a line;
RFC 9293 is today's TCP specification.
Reading the drawing
- The ladder in the middle is time running down. A segment is an arrow from the host that sent it to
the host that received it.
- A segment's card shows the flags,
seq, ack and data-len, the number of data bytes it carries. - A click on a card opens the whole header, the sequence numbers the segment occupies, and the
segments it acknowledges or is acknowledged by. The same links are drawn on the ladder as dotted
curves:
- green to the segments it acknowledges;
- purple to the one that acknowledges it.
- A segment on the wire has Deliver and Lose under its card, so segments can arrive in any
order, or not at all.
- Step moves one event at a time. Past the end of the story, it delivers the oldest segment still on
the wire.
- A host's buttons, Deliver and Lose take the story our own way: the rest of the story gives way to
what we do. The scene picker or Restart brings the story back.
Segments, connections and the byte stream
- Segment: a TCP header plus the data it carries, sent inside one IP packet.
- Connection: identified by four values: source IP, source port, destination IP, destination port.
- Byte stream: TCP numbers bytes, not segments; the application reads a stream with no message
boundaries.
- Reliable, in order: every byte is acknowledged, resent if lost, and handed to the application in
sequence.
- Header: 20 bytes without options, up to 60 with them.
Read more
- MSS (maximum segment size): the most data one segment carries, announced in the SYN; 1460 bytes on
Ethernet (1500 − 20 IP − 20 TCP).
Read more
The flags
- SYN: opens a connection and carries the initial sequence number.
- ACK: the acknowledgment number is valid; set on nearly every segment after the first SYN.
- FIN: the sender has no more data to send; the other direction stays open.
- RST: abort the connection now.
- PSH: the end of an application write: the receiver should pass the data up at once.
- URG: the urgent pointer is valid; almost never used today.
Read more
- ECE, CWR: congestion signals from ECN, a router marking packets instead of dropping them.
Read more
Opening a connection: the three-way handshake
Both hosts start CLOSED, and either one opens a connection with Initiate a connection, which sends
a SYN. The handshake is SYN, SYN-ACK, ACK.
Read more
- Accept the connection answers with a SYN-ACK.
- Refuse the connection answers with a reset, RST+ACK, which the connecting side reports as
connection refused. A real host refuses when no program listens on the port.
- A host answers only a segment that has arrived, so its buttons stay grey while the SYN is on the
wire.
Each of the three segments carries one number that matters:
- The SYN carries the client's initial sequence number, its ISS (the RFC also calls it the ISN).
- The SYN-ACK carries the server's own ISS, and
ack = the client's ISS + 1: a SYN takes one sequence
number, although it carries no data. - The final ACK has
ack = the server's ISS + 1. It takes no sequence number, so the first data byte
still starts at the client's ISS + 1. - Three segments because each side must send its ISS and see it acknowledged; the middle segment does
both.
The rest of the handshake's life:
- Random ISS: each host picks one for every connection, so that a segment from an old connection on
the same ports, or one an attacker guesses, cannot land inside the new connection's window.
Read more
- LISTEN: a server's socket waits for SYNs on its port.
- Lost SYN: the client resends it after a timeout, doubling the wait each time.
- Simultaneous open: two SYNs cross, and both sides answer with SYN-ACK. Rare, but legal.
- SYN flood: fake SYNs fill the server's table of half-open connections; SYN cookies keep no state
until the final ACK.
Read more
- TCP Fast Open: data on the SYN, for a client that has connected to this server before.
Read more
One connection between two fixed ports
The simulator holds one connection at a time, between Host A's port 51000 and Host B's port 8080, so
Initiate a connection stays grey until the current one has closed.
- A real client that opens a second connection gets a new source port from its operating system.
The two connections are independent: each has its own ISS and its own sequence numbers.
- A SYN sent a second time, from the same port and with the same ISS, belongs to the same connection:
- real TCP resends its SYN when no SYN-ACK arrives before a timer runs out; Retransmit does it here;
- the receiver recognises the ISS it already recorded, and answers with its SYN-ACK again.
Sending data
Send data hands TCP one write: a block of bytes from the application, as many as the data-len
field under it says, 4000 by default. Every data segment also carries an ACK for everything its host has
received.
- A write larger than one segment is cut up. A segment carries at most 1460 bytes here (the MSS), so
4000 bytes go out as 1460 + 1460 + 1080, one after another, without waiting for an ACK.
- Several small writes may share one segment.
- PSH marks the last segment of a write, telling the receiver to pass the data to the application
now. A write that fits in one segment always carries PSH; a longer one carries it only on its last
segment.
- Nagle's algorithm holds small writes back while earlier data is unacknowledged.
Read more
Acknowledge sends a pure ACK, with no data of its own. It lights up whenever a host owes one:
- data or a FIN has arrived;
- the SYN-ACK has completed the handshake;
- a segment came out of order or twice, and the host repeats what it still expects.
Sequence and acknowledgment numbers
A segment's seq is the number of its first data byte, and its data occupies data-len numbers from
there. The receiver's ack is the number of the next byte it expects: it has every byte before it.
- Host A, with ISS 1000, sends 100 bytes at seq 1001: they occupy 1001–1100, and its next segment
starts at 1101.
- Host B answers with ack 1101: everything up to 1100 arrived.
data-len is not a header field: the receiver works it out from the IP header's total length,
minus the IP and TCP headers.- SYN and FIN take one number each, although they carry no data, so that they can be acknowledged.
- A pure ACK takes no sequence number, so two ACKs in a row carry the same
seq, and nothing
acknowledges an ACK. - Cumulative ACK: an ACK acknowledges every byte before its number, so one ACK can cover several
segments. An ACK's card lists the segments it acknowledges for the first time.
- Piggybacking: an ACK rides on data going the other way, so it costs no segment of its own.
- Delayed ACK: the receiver waits a little, always less than half a second, and acknowledges at
least every second full segment.
Read more
- Wrap-around: the number is 32 bits and wraps to 0 after 4 GiB; fast links add timestamps to tell
laps apart.
Read more
- Relative numbers: Wireshark shows seq and ack counted from each side's ISS; the packets carry the
real ones.
The host panels show the variables behind these numbers, as RFC 9293 names them:
SND.UNA: the oldest number sent and not yet acknowledged;SND.NXT: the next number to send;RCV.NXT: the next number expected, which is what the host's own ACKs say.
Flow control: the receive window
- Receive window: how many more bytes the receiver has room for, sent on every segment.
- The sender's limit: bytes sent and not yet acknowledged may not exceed the window. Here it is
6000, so a second 4000-byte write waits for the first one's ACK.
- Zero window: the receiver is full; the sender probes until it opens again.
Read more
- Window scale: the field is 16 bits (64 KiB); an option in the SYN multiplies it for fast links.
Read more
- Congestion window: the sender's own limit, guessed from loss; it sends the smaller of the two.
Read more
Loss and retransmission
Lose, on a segment's card while it is on the wire, removes it. The receiver never sees it, and the
sender keeps counting its bytes as sent and unacknowledged.
- The segments after the lost one arrive out of order. The receiver holds them, and its ACK keeps
naming the first missing byte, so the sender gets duplicate ACKs.
- Retransmit resends the oldest unacknowledged segment. The new segment's card says which one it
resends, and selecting either draws an amber curve between them.
- When the missing bytes arrive, the held ones join them, and the next ACK jumps past all of them.
- Retransmission timeout (RTO): real TCP resends when no ACK arrives in time; the timer follows the
measured round-trip time.
Read more
- Fast retransmit: three duplicate ACKs trigger a resend without waiting for the timer.
Read more
- SACK: an option listing the blocks received past a gap, so only the missing ones are resent.
Read more
- Karn's rule: an ACK for a resent segment gives no round-trip sample, since it may answer either
copy.
Read more
Closing a connection
Close the connection sends a FIN, which takes one sequence number, like the SYN. A FIN closes one
direction only: the side that sent it may still receive.
Read more
- Four segments: FIN, ACK, FIN, ACK; the middle two often travel as one FIN-ACK.
Read more
- The first host to close goes through
FIN_WAIT_1, waiting for its FIN's ACK, and FIN_WAIT_2,
waiting for the other side's FIN. - The other host goes to
CLOSE_WAIT: the other side has closed, and this side's application has not
yet. It may still send data, and closes with its own FIN through LAST_ACK. - Stuck in
CLOSE_WAIT: the application never closed its socket, a bug in the program, not in TCP. CLOSING: both FINs crossed; each side waits for the ACK of its own.
TIME_WAIT
The side that closed first waits in TIME_WAIT after sending the last ACK. End TIME_WAIT ends it at
once.
- Why: to resend that ACK if it was lost, and to let stray segments of the connection die out before
the same four values are used again.
Read more
- How long: twice the maximum segment lifetime (2×MSL). The RFC's MSL is 2 minutes; Linux waits
60 seconds in all.
- Many TIME_WAITs: normal on a busy client that opens many short connections; each one holds a local
port for that time.
Resetting a connection
Reset the connection ends it at once with a RST at the exact sequence number the peer expects. The
peer drops the connection without a reply, and neither side goes through TIME_WAIT.
- A RST discards unsent data, and nothing acknowledges it.
- Who sends one: a host that gets a segment for a connection it does not have, or an application that
aborts.
Read more
- Here, data still on the wire when its connection was reset reaches a host with no connection, and
the same button sends the reset it owes.
- A RST counts only at the exact expected sequence number; one elsewhere in the window gets a
challenge ACK, against forged resets.
Read more
- Half-open connection: one side crashed and forgot it; the other learns when its next segment is
answered with RST.
Read more
Idle connections
An idle connection sends nothing: TCP has a heartbeat only when one is turned on.
- Keepalive: an optional probe after a long idle time, at least two hours by default.
Read more
- NAT and firewalls forget idle connections much sooner, which is why applications send their own
keepalives. See the
NAT Overload Simulator.
The state machine
- Eleven states, from CLOSED through LISTEN, SYN_SENT, SYN_RECEIVED and ESTABLISHED to the closing
ones above.
Read more
ss -tan (or netstat -tan) lists every connection on a Linux host with its state.