The Transport Layer
The network layer delivers datagrams to a machine, unreliably and in any order. Applications want something quite different.
The organising fact is that the transport layer turns a host-to-host, best-effort service into a process-to-process service with whatever guarantees the application needs, and the only tools it has are numbers in a header and timers.
Port numbers give process-to-process delivery. Sequence numbers give ordering and duplicate detection. Acknowledgements plus timers give reliability. Window sizes give flow and congestion control.
Nothing else is available, because the layer runs only at the endpoints and cannot inspect or influence the network in between.
The second organising fact is that flow control and congestion control are different problems with similar mechanisms. Flow control protects the receiver from a fast sender. Congestion control protects the network from all senders together. TCP implements both with windows, and confusing them is a reliable way to lose marks.
The third is that TCP infers congestion from loss, because the network gives it no other signal. That inference is a design choice with consequences, notably on wireless links where loss is often corruption rather than congestion.
1. Multiplexing and Sockets
A port number identifies a process on a host, and the pair of an IP address and a port is a socket.
UDP demultiplexes on the destination port alone, so all datagrams to one port reach one socket regardless of sender.
TCP demultiplexes on all four values: source address, source port, destination address, destination port. That is why a server can hold thousands of connections on port 80, each a distinct socket.
Well-known ports run from 0 to 1023, registered ports to 49151, and the rest are ephemeral, assigned to clients temporarily.
A listening socket and a connected socket are different objects. The server's listening socket is identified by its own address and port alone, while each accepted connection creates a new socket identified by all four values.
2. UDP
UDP adds only four fields to IP: source port, destination port, length and checksum.
It offers no connection, no reliability, no ordering and no congestion control, and that minimalism is exactly why it is used.
The header is 8 bytes against TCP's 20, and there is no handshake, so a request and reply take one round trip instead of two.
The checksum is optional in IPv4 and mandatory in IPv6, and it covers a pseudo-header including the IP addresses, which lets the receiver detect misdelivery.
UDP suits applications that supply their own reliability or do not want any: DNS, DHCP, streaming media, online games, and anything where a late packet is worse than a lost one.
QUIC is the modern demonstration of this. It runs reliability, ordering and congestion control over UDP in user space, precisely so those mechanisms can evolve without waiting for operating system kernels to change.
3. TCP Connection Management
The header is 20 bytes without options and up to 60 with them.
The sequence number counts bytes, not segments, which is the fact most often needed in numerical questions.
The acknowledgement number is cumulative, naming the next byte expected, so one acknowledgement confirms everything before it.
Connection setup is a three-way handshake. The client sends SYN with its initial sequence number; the server replies with SYN and ACK carrying its own; the client acknowledges.
Two exchanges would not suffice, because both directions need their initial sequence numbers established and acknowledged, and a delayed duplicate SYN from an old connection could otherwise open a phantom connection.
Connection release takes four steps, since each direction closes independently: FIN, ACK, FIN, ACK.
The closing side then waits in TIME_WAIT for twice the maximum segment lifetime. This ensures the final acknowledgement arrives, and that no old segment from this connection can appear in a new one using the same port pair.
The TCP Header Fields
| Field | Size | Purpose |
|---|---|---|
| Source and destination port | 16 bits each | Identify the two processes |
| Sequence number | 32 bits | Byte offset of the first data byte |
| Acknowledgement number | 32 bits | Next byte expected, cumulative |
| Data offset | 4 bits | Header length in 4-byte words |
| Flags | 6 bits | URG, ACK, PSH, RST, SYN, FIN |
| Window | 16 bits | Receiver's advertised buffer space |
| Checksum | 16 bits | Covers header, data and a pseudo-header |
| Urgent pointer | 16 bits | Offset of urgent data, rarely used |
The 16-bit window field is the constraint that forced window scaling, since 65,535 bytes cannot fill any modern long-distance link.
RST aborts a connection immediately without the four-step release, and is what a host sends when a segment arrives for a port nobody is listening on.
PSH asks the receiver to deliver buffered data to the application at once rather than waiting for more, which is what makes interactive protocols responsive.
4. Reliability and Timers
A sender retransmits a segment when its timer expires, and choosing that timer is delicate. Too short causes needless retransmission; too long wastes time on real losses.
The round-trip time is estimated by exponential averaging.
with conventionally.
The variation is tracked separately as DevRTT with weight , and the timeout is
Including the deviation matters because a stable network deserves a tight timeout while a variable one needs slack, and a fixed multiplier of the mean cannot provide both.
Karn's algorithm forbids sampling the round-trip time from a retransmitted segment, because the acknowledgement might refer to either transmission and the sample would be meaningless. Timeouts are instead doubled on each retransmission.
Fast retransmit resends a segment after three duplicate acknowledgements without waiting for the timer, since three duplicates strongly suggest one segment was lost while later ones arrived.
5. Flow Control
The receiver advertises a window stating how much buffer space remains, and the sender never has more unacknowledged data outstanding than that.
This is flow control: protecting a slow receiver from a fast sender. It says nothing about the network.
Silly window syndrome occurs when the receiver advertises tiny windows as its application consumes a few bytes at a time, so the sender transmits many minimal segments with 40 bytes of header each.
Clark's solution is receiver-side: advertise zero until either half the buffer or one full segment is free.
Nagle's algorithm is the sender-side counterpart: while unacknowledged data is outstanding, buffer small writes and send them as one segment when the acknowledgement arrives.
Nagle interacts badly with delayed acknowledgements, since each waits for the other, producing a characteristic delay that interactive applications disable Nagle to avoid.
6. Congestion Control
TCP maintains a congestion window and sends the minimum of it and the receiver's advertised window.
Slow start begins with a congestion window of one segment and doubles it every round trip, which despite the name is exponential growth.
On reaching the slow start threshold, congestion avoidance takes over, increasing the window by one segment per round trip, which is linear.
The two phases together are additive increase, and the response to loss is multiplicative decrease.
A timeout is treated as severe congestion. The threshold is set to half the current window and the window drops to one segment, restarting slow start.
Three duplicate acknowledgements are treated as mild congestion, since segments are still getting through.
TCP Tahoe treats both the same, dropping to one segment in either case.
TCP Reno adds fast recovery: on three duplicate acknowledgements it halves the window and continues in congestion avoidance rather than restarting slow start.
The steady-state throughput of a long-lived connection is approximately for loss rate , which shows why long-distance connections with even slight loss perform poorly.
7. Worked Examples
Example 1. A TCP connection has a maximum segment size of 1 KB. The slow start threshold begins at 8 segments. Trace the congestion window over 12 round trips, with a timeout after the 6th and three duplicate acknowledgements after the 10th, under Reno.
Round 1: window is 1, slow start.
Round 2: doubles to 2. Round 3: 4. Round 4: 8.
The window has reached the threshold of 8, so congestion avoidance begins.
Round 5: 9, increasing linearly. Round 6: 10.
A timeout occurs after round 6.
The threshold becomes half the current window, that is 5, and the window drops to 1, restarting slow start.
Round 7: 1. Round 8: 2. Round 9: 4.
Round 10: doubling would give 8, but the threshold is 5, so the window becomes 5 and congestion avoidance resumes.
Three duplicate acknowledgements occur after round 10.
Under Reno, this is fast recovery: the threshold becomes half of 5, which rounds to 2, and the window becomes 2, continuing in congestion avoidance rather than restarting.
Round 11: 2. Round 12: 3.
Under Tahoe the outcome after round 10 would differ: the window would drop to 1 and slow start would restart, giving 1 then 2 for rounds 11 and 12.
The whole point of fast recovery is visible here. Reno reaches 3 where Tahoe reaches 2, because it recognises that segments still arriving mean the pipe is not empty.
Example 2. Successive round-trip samples are 100, 120, 90 and 110 milliseconds. Starting from an estimate of 100 and a deviation of 10, compute the timeout after all four samples with and .
Apply both recurrences per sample.
Sample 100: estimate becomes . Deviation becomes .
Sample 120: estimate becomes . Deviation uses the previous estimate, so , giving .
Sample 90: estimate becomes . Deviation uses , giving .
Sample 110: estimate becomes . Deviation uses , giving .
The timeout is milliseconds.
Note the safety margin. The timeout sits about 40 percent above the mean, which is what the deviation term buys: on a stable link the deviation would shrink and the timeout would tighten automatically.
Example 3. Why is a two-way handshake insufficient for TCP connection establishment?
Suppose the protocol were SYN followed by SYN-ACK, with data then flowing.
Consider a duplicate SYN from an old, closed connection that was delayed in the network and arrives late.
The server sees a connection request, allocates state, and replies with SYN-ACK.
The client is not opening a connection and discards the reply, but the server is now holding a half-open connection consuming resources.
Worse, if old data segments from that earlier connection also arrive, the server may accept them as belonging to this new connection, since their sequence numbers fall in the expected range.
The third message closes the hole. The server commits only after the client acknowledges the server's own initial sequence number.
A client that never sent the SYN will not acknowledge, so the server times out and releases the state.
The essential point is that each direction needs its sequence number both delivered and acknowledged, which requires three messages when one of them can be combined.
Randomising initial sequence numbers strengthens this further, making it improbable that an attacker or a stale segment guesses a valid number.
Example 4. A 10 Gbps link carries a TCP connection. How long before the 32-bit sequence number space wraps around, and why does that matter?
The sequence space is bytes, which is about 4.29 gigabytes.
At 10 gigabits per second, the byte rate is 1.25 gigabytes per second.
Wraparound time is seconds.
Why this matters: the maximum segment lifetime is conventionally taken as 2 minutes.
A segment delayed in the network for even a few seconds could reappear after the sequence numbers have wrapped, landing inside the current valid window and being accepted as new data.
The protection is the timestamp option, which adds a monotonically increasing value to each segment, so a segment from an earlier lap of the sequence space is recognised by its stale timestamp and discarded.
This is called protection against wrapped sequence numbers, and it is mandatory on any high-speed link.
Note how this compounds with window scaling. Both options exist because TCP's original 16-bit and 32-bit fields were sized for links thousands of times slower than today's.
Example 5. A connection has a round-trip time of 50 milliseconds, a maximum segment size of 1460 bytes and a loss rate of 0.01 percent. Estimate throughput, then recompute at 1 percent loss.
Use .
At : , so .
bytes per second, times 100 gives 2.92 megabytes per second, about 23 megabits per second.
At : , so .
Throughput becomes 292 kilobytes per second, about 2.3 megabits per second, a tenfold reduction.
A hundredfold increase in loss cost a tenfold drop in throughput, which is the square root relation made concrete.
The practical consequence is severe on long paths. Doubling the round-trip time halves the throughput regardless of available bandwidth, which is why a connection from India to the United States can crawl on a link with gigabits to spare.
This is also the argument for modern congestion control algorithms such as BBR, which estimate bandwidth and round-trip time directly rather than inferring congestion from loss.
Example 6. For each application, choose UDP or TCP and justify: a DNS query, a file transfer, a live video call, a bulk database replication.
A DNS query uses UDP. The request and response each fit in one datagram, so a handshake would triple the cost of the exchange, and the application simply retries on no answer.
Large DNS responses fall back to TCP, since UDP truncation forces it, which is why both are provisioned.
A file transfer uses TCP. Every byte must arrive and order matters, so building reliability into the application would mean reimplementing TCP badly.
A live video call uses UDP. A retransmitted frame arriving after its display time is useless, so the application prefers to conceal the loss and continue.
TCP's in-order delivery is actively harmful here, because one lost segment stalls delivery of everything behind it, producing a freeze rather than a glitch.
Bulk database replication uses TCP. Reliability is essential, the transfer is long enough for congestion control to reach a good rate, and ordering simplifies the receiving logic considerably.
The general rule: choose TCP when every byte matters and lateness is acceptable, and UDP when timeliness matters and some loss is acceptable.
Summary
The transport layer turns host-to-host best effort into process-to-process service with only header numbers and timers.
UDP demultiplexes on the destination port; TCP on all four address and port values, which is why one server port supports thousands of connections.
UDP has an 8-byte header, no handshake and no guarantees, which suits DNS, streaming, gaming and anything preferring loss to delay.
TCP sequence numbers count bytes and acknowledgements are cumulative. The three-way handshake exists because each direction's initial sequence number must be delivered and acknowledged, and because a delayed duplicate SYN would otherwise create a half-open connection. Release takes four steps and TIME_WAIT lasts twice the maximum segment lifetime.
Timeout is the estimated round-trip time plus four deviations, with weights 0.125 and 0.25. Karn's algorithm bars sampling from retransmitted segments and doubles the timeout instead. Fast retransmit acts on three duplicate acknowledgements.
Flow control protects the receiver and congestion control protects the network. Silly window syndrome is fixed by Clark's rule at the receiver and Nagle's algorithm at the sender, which interacts badly with delayed acknowledgements.
Slow start doubles the window per round trip, congestion avoidance adds one. A timeout halves the threshold and resets the window to one; three duplicate acknowledgements do the same under Tahoe but trigger fast recovery under Reno, which halves the window and stays in congestion avoidance.
Throughput is about MSS over RTT times one over the square root of the loss rate, so a hundredfold loss increase costs a tenfold throughput drop, and doubling the round-trip time halves throughput regardless of bandwidth.
At 10 Gbps the 32-bit sequence space wraps in about 3.4 seconds, far inside the maximum segment lifetime, which is why the timestamp option is mandatory on fast links.
The 16-bit window field is what forced window scaling, RST aborts a connection without the four-step release, and PSH forces immediate delivery to the application.
Choose TCP when every byte matters and lateness is tolerable, and UDP when timeliness matters and loss is tolerable, since TCP's in-order delivery turns a single loss into a stall of everything behind it.
