Lesson 15 / 18
TCP
Three-way handshake, sequence and acknowledgement numbers, computing the retransmission timer, flow control, and the connection state machine.
Contents
The previous lesson established the four-tuple that names a flow. Naming says nothing about whether the flow will be reliable: the network layer can lose a packet, reorder it, deliver the same packet twice, and feed the receiver faster than it can process.
TCP (Transmission Control Protocol) takes on all four of these problems at once and offers the application a single abstraction: an ordered, loss-free, duplicate-free byte stream. This lesson’s question is which mechanisms keep that promise.
Promises Made
| Promise | How it is kept |
|---|---|
| Delivery | Unacknowledged data is retransmitted |
| Order | Every byte gets a sequence number, the receiver reorders |
| No duplication | A second copy at the same sequence number is discarded |
| Flow control | The receiver announces how many bytes it can accept |
| Integrity | The checksum is computed together with the pseudo header |
What is not promised must also be known. TCP does not preserve message boundaries: an application’s three separate write calls can arrive on the other side merged into a single read. It gives no upper bound on delay; in the event of loss, retransmission noticeably increases delay. Nor does it guarantee that the other side has processed the data; an acknowledgement shows only that the data has reached the transport layer’s buffer.
Header
The TCP header is 20 bytes without options.
| Field | Size | Function |
|---|---|---|
| Source port | 2 bytes | Sending endpoint |
| Destination port | 2 bytes | Receiving endpoint |
| Sequence number | 4 bytes | The position of this segment’s first byte in the stream |
| Acknowledgement number | 4 bytes | The number of the next byte expected |
| Data offset | 4 bits | Header length (in 32-bit words) |
| Flags | 8 bits | SYN, ACK, FIN, RST, PSH, URG, and congestion bits |
| Window | 2 bytes | The number of bytes the receiver can accept |
| Checksum | 2 bytes | Computed over the header, payload, and pseudo header |
| Urgent pointer | 2 bytes | Meaningful only together with the URG flag |
| Options | 0–40 bytes | MSS, window scale, timestamp, selective acknowledgement |
Computing the checksum over the pseudo header means the source and destination IP addresses are also factored in. The purpose is detecting a segment delivered to the wrong machine. A side effect of this, as seen in the NAT and PAT lesson, is that an address translator must recompute the checksum.
Three-Way Handshake
The connection is established before any data is sent. Setup takes three segments:
| Step | Direction | Flags | Carries |
|---|---|---|---|
| 1 | Client → Server | SYN | The client’s initial sequence number, MSS, and options |
| 2 | Server → Client | SYN + ACK | The server’s initial sequence number, acknowledgement of the client’s |
| 3 | Client → Server | ACK | Acknowledgement of the server’s sequence number |
The necessity of three steps is symmetric: each direction is established separately. TCP is bidirectional; each direction has its own sequence-number series. The client announces its own number and has it acknowledged, and the server does the same. Because one of the two symmetric exchanges (the server’s acknowledgement together with its own announcement) is merged into a single segment, the total comes to three segments.
The initial sequence number is not zero; it is chosen at random. The reason is twofold. Delayed segments from an old connection must not be mixed into a new connection established with the same four-tuple. The second is security: if the number were predictable, an outside party could inject segments into the flow that appear valid.
The handshake carries a round-trip-time cost. At least one full round trip is waited out before any data is sent; in short exchanges this cost can make up the largest part of the total time.
Sequence and Acknowledgement Numbers
TCP assigns a sequence number not to the segment but to the byte. A segment’s sequence number is the position, within the stream, of the first byte it carries.
A flow with initial number 1000 and MSS 1460:
| Segment | Sequence number | Range covered |
|---|---|---|
| 1 | 1000 | 1000 – 2459 |
| 2 | 2460 | 2460 – 3919 |
| 3 | 3920 | 3920 – 5379 |
| 4 | 5380 | 5380 – 6839 |
The acknowledgement number is the number of the next byte expected, and it is cumulative: an acknowledgement of 6840 means everything up to 6839 has been received.
Cumulative acknowledgement has a consequence. If the second segment is lost and the third and fourth arrive, the receiver is forced to hold the acknowledgement number at 2460 — because there is a gap starting at 2460. It cannot report that it received the third and fourth segments.
This shortcoming is fixed by the selective acknowledgement option: the receiver additionally lists the contiguous ranges it has received. The sender can then retransmit only the segment that is actually missing; without the option, it may be forced to retransmit everything after the gap.
The SYN flag in the handshake and the FIN flag at closing each consume one sequence number. Even though they carry no data, they need to be acknowledged; consuming a sequence number is what makes that possible.
Retransmission
Every segment sent is kept in a buffer until it is acknowledged. If no acknowledgement arrives, the segment is retransmitted. How long to wait is decided by the retransmission timeout (RTO).
A fixed duration cannot be chosen: the round-trip time on the same connection varies along the way. If the duration is chosen too short, unnecessary retransmissions occur and load an already congested network further; if it is chosen too long, loss is noticed late and throughput drops.
The solution is to produce a running estimate from measured round-trip times:
Here is the latest measurement, , and . The second term accounts for the variability of the duration: on a stable path RTO approaches the measurement, on a fluctuating path it moves away from it.
ALPHA, BETA, K = 1 / 8, 1 / 4, 4 measurements = [100.0, 104.0, 96.0, 180.0, 102.0, 98.0] # ms srtt = measurements[0] rttvar = measurements[0] / 2 rto = srtt + K * rttvar print(f"{'measurement':>11s} {'SRTT':>8s} {'RTTVAR':>8s} {'RTO':>8s}") print(f"{measurements[0]:11.2f} {srtt:8.2f} {rttvar:8.2f} {rto:8.2f}") for r in measurements[1:]: rttvar = (1 - BETA) * rttvar + BETA * abs(srtt - r) srtt = (1 - ALPHA) * srtt + ALPHA * r rto = srtt + K * rttvar print(f"{r:11.2f} {srtt:8.2f} {rttvar:8.2f} {rto:8.2f}")
measurement SRTT RTTVAR RTO
100.00 100.00 50.00 300.00
104.00 100.50 38.50 254.50
96.00 99.94 30.00 219.94
180.00 109.95 42.52 280.01
102.00 108.95 33.87 244.44
98.00 107.58 28.14 220.15
The 180 ms jump in the fourth row alone has pushed the average to 110 ms, and pushed the variability from 30 to 42.5; RTO rises from 220 ms to 280 ms. With the following stable measurements, the value comes back down.
When a timeout occurs, RTO doubles and the segment is retransmitted. On consecutive timeouts the duration grows exponentially. This behavior prevents retransmission traffic from making the problem worse when the network is not responding.
There is a second mechanism for cases where waiting out the timeout is expensive. When the receiver gets a segment that is out of order, it repeats the same acknowledgement number; if the sender receives the same acknowledgement three times, it retransmits the segment without waiting for the timeout. This is called fast retransmit and works together with the congestion behavior in the next lesson.
Flow Control
If the sender transmits faster than the receiver can process, the receiver’s buffer overflows and data is dropped. Flow control prevents this: the receiver announces, with every acknowledgement, how many bytes it can accept. This value is called the receive window.
The amount of data the sender can transmit without acknowledgement cannot exceed the window. This has a direct throughput consequence:
for window, rtt in ((65535, 0.020), (65535, 0.100), (262144, 0.100)): print(f"window {window:7d} bytes, RTT {rtt*1000:5.0f} ms -> max {window*8/rtt/1e6:8.2f} Mb/s")
window 65535 bytes, RTT 20 ms -> max 26.21 Mb/s window 65535 bytes, RTT 100 ms -> max 5.24 Mb/s window 262144 bytes, RTT 100 ms -> max 20.97 Mb/s
The window field is 16 bits; its largest value is 65,535 bytes. On a high-latency path this value caps throughput at 5 Mb/s — regardless of the connection’s capacity. The problem is solved with the window scale option: a shift amount is announced during the handshake, and the window value is multiplied by . The largest shift is 14, which takes the window to just under a gibibyte.
When the receiver’s buffer fills, a window of zero is announced and the sender stops. When the receiver frees up space, it sends a window update; if this update is lost, the connection locks up. To prevent this, the sender regularly sends one-byte probe segments.
Flow control watches only the receiver’s capacity. The path’s capacity is a separate problem and is the subject of the next lesson.
State Machine
A TCP endpoint transitions among defined states.
| State | Meaning |
|---|---|
| CLOSED | No connection |
| LISTEN | The server is waiting for incoming requests |
| SYN-SENT | The client has sent SYN and is waiting for a reply |
| SYN-RECEIVED | The server received SYN and sent SYN+ACK |
| ESTABLISHED | Data exchange can take place |
| FIN-WAIT-1 / FIN-WAIT-2 | The wait of the side that initiated closing |
| CLOSE-WAIT | The other side closed, the local application has not closed yet |
| LAST-ACK | The final acknowledgement is awaited |
| TIME-WAIT | Closing is complete, delayed segments are awaited |
Closing, unlike setup, takes four segments, and the two directions are closed separately. When one side sends FIN, it announces only that the data it has to send is finished; the other side can continue sending data. This state is called half-closed.
Connections that stay in CLOSE-WAIT for a long time are the signature of an application
bug: the other side has closed, and the local application has forgotten to close the
socket.
The TIME-WAIT state is held for a period on the side that initiated closing. It has two
reasons. If the final acknowledgement is lost, the other side repeats the FIN, and the
state needs to remain in place so it can be answered. The second is preventing delayed
segments belonging to this connection from mixing into a new connection established with
the same four-tuple.
A server getting an “address in use” error when it restarts stems from this state; the
SO_REUSEADDR option from the previous lesson loosens this restriction.
The RST flag follows a separate path: it terminates the connection instantly, without a handshake. This is the response given to a segment arriving at a closed port and to connections whose state has become corrupted.
Summary
- TCP offers an ordered, loss-free, duplicate-free byte stream; it does not preserve message boundaries and gives no upper bound on delay.
- The handshake is three segments because the two directions are established separately; the initial sequence number is chosen at random to prevent old segments from mixing in and to prevent injection.
- The sequence number is assigned to the byte; the acknowledgement is cumulative and cannot advance once a gap forms — selective acknowledgement fixes this shortcoming.
- RTO is computed from a running average and its variability; it doubles on timeout, and three duplicate acknowledgements trigger fast retransmit.
- The receive window limits throughput by ; at 65,535 bytes and 100 ms this limit is 5.24 Mb/s and is overcome with the window scale option.
- Closing is four segments and the directions are closed separately;
TIME-WAITis held for a lost final acknowledgement and for delayed segments.
Next Step
Flow control protects the receiver’s capacity; it says nothing about the path’s capacity. If the receiver announces a one-megabyte window, the sender will fill it regardless of the network’s condition. The next lesson covers, by calculation, how the sender estimates the network’s capacity, how it reads loss as a signal, and the window’s effect on throughput.
To keep your progress and take notes, Log in
My notes
Log in to take notes.