Skip to content
academia.sh

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.

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:

SRTT(1α)SRTT+αR\text{SRTT} \leftarrow (1-\alpha)\,\text{SRTT} + \alpha R

RTTVAR(1β)RTTVAR+βSRTTR\text{RTTVAR} \leftarrow (1-\beta)\,\text{RTTVAR} + \beta \lvert \text{SRTT} - R \rvert

RTO=SRTT+4RTTVAR\text{RTO} = \text{SRTT} + 4 \cdot \text{RTTVAR}

Here RR is the latest measurement, α=1/8\alpha = 1/8, and β=1/4\beta = 1/4. 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:

max throughput=windowRTT\text{max throughput} = \frac{\text{window}}{\text{RTT}}

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 2k2^k. 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 window/RTT\text{window}/\text{RTT}; 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-WAIT is 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.

Start typing to search.

↑↓ Esc navigate · open · close