Core CS · Computer Networks
Late but correct, or on time but approximate
This is the most predictable question in the subject and the easiest one to answer badly. Both protocols hand your bytes to IP and both use port numbers. Every other difference between them comes out of one decision, and this lesson makes you watch that decision happen to the same five messages twice.
Lose message 3, then watch each protocol react →01 The idea
IP loses packets, and the two protocols disagree about whose problem that is
The network layer gives you almost nothing. IP is best effort: it will try to move a packet from one host to another, and if a queue overflows or a link errors it drops the packet and tells nobody. Packets can arrive out of order, because two packets of the same flow can take different paths. They can arrive twice. Nothing below the transport layer promises otherwise. Everything you think of as a working network is built on top of that by the two ends.
The transport layer is where that building happens, and it starts with one job both protocols share. IP delivers to a host; an operating system runs hundreds of processes. So both TCP and UDP put a 16-bit source port and a 16-bit destination port at the front of every header, and the pair of address-plus-port is what identifies one end of one conversation. The conversation itself is named by all four together — source IP, source port, destination IP, destination port — which is why two browser tabs can talk to the same server on port 443 at once and neither is confused with the other. Both also carry a 16-bit checksum over the payload and a few fields borrowed from the IP header. In the IPv4 header the Protocol field says which of the two is inside: 6 for TCP, 17 for UDP.
After that they part company completely. UDP adds one more field, a length, and stops. It is a thin wrapper that gets a message from a process on one host to a process on another, and if the message is lost, duplicated or overtaken, that is the application’s business. There is no connection, so the first packet a UDP sender puts on the wire is data. There is no acknowledgement, so nobody at either end ever learns that a datagram went missing.
TCP makes four promises instead, and pays for each one. It is connection oriented, so a three-segment handshake and a full round trip are spent before the receiving application can be handed anything. It is reliable, so every byte is acknowledged and anything unacknowledged is sent again. It is ordered, so a sequence number on every segment lets the receiver put a scrambled arrival back into the order it was written. And it is a byte stream, so what the receiver reads is one continuous run of bytes with no record of how the sender split its writes. On top of those it runs flow control, which stops a fast sender from overrunning a slow receiver, and congestion control, which stops it from overrunning the network in between.
Everything TCP is criticised for is one of those promises presenting its bill. The handshake costs a round trip before any data moves. The header is 20 bytes rather than 8. And the ordering promise creates head of line blocking: if segment three is lost, segments four and five may be sitting safely in the receiver’s memory, but the receiver is not allowed to hand them up, because doing so would deliver bytes out of order. Correct data waits for missing data. That is not a bug in an implementation; it is the guarantee working exactly as specified.
02 Worked example
Five messages, one lost, two out of order
One scene, used for the whole lesson. An application writes five messages of 40 bytes each, M1 M2 M3 M4 M5, and the network does two things to them: it drops M3 at a congested router, and it lets M5 overtake M4 because the two took different paths. This is the scene the console in section 04 runs, and every number in the cheat sheet is checked against it. Read the five nodes left to right.
Slow down on the highlighted node, because it is the one that decides how you talk about this topic. The routers did not know which protocol they were carrying. The same drop and the same reordering happened in both runs. Every difference between node 4 and node 5 is created at the two endpoints, not on the wire. Reliability is not a property of a network; it is a property of what two hosts agree to do about a network that is not reliable. Say that sentence in an interview and the rest of the answer writes itself.
Now the numbers, because this is where the trade-off stops being a list. Each message is 40 bytes of data. Over IPv4 a UDP datagram carries a 20-byte IP header and an 8-byte UDP header, so 68 bytes cross the wire to move 40 bytes of data: 40 / 68 = 58.8% payload. A TCP segment carries the same 20-byte IP header and a 20-byte TCP header, so 80 bytes cross the wire for the same 40: 40 / 80 = 50.0% payload. That gap of 12 bytes per message is small. The gap that actually matters is the three segments of handshake and the four of teardown, which carry no data at all, and the retransmission, which carries the same 40 bytes a second time.
The sequence numbers in node 2 are worth memorising as a shape rather than as values. The client picks an initial sequence number, here x = 1000, and its SYN carries seq = 1000. The server picks its own, y = 5000, and its SYN-ACK carries seq = 5000 and ack = 1001. The client’s final ACK carries seq = 1001 and ack = 5001. Both acknowledgement numbers are one more than the sequence number they answer, and that is not an accident: a SYN consumes one sequence number even though it carries no data, and so does a FIN. The acknowledgement number always names the next byte the sender of that acknowledgement is waiting for, never the last byte it received.
So the client’s first data byte is number 1001, and the five messages occupy M1 = 1001..1040, M2 = 1041..1080, M3 = 1081..1120, M4 = 1121..1160, M5 = 1161..1200. Check it two ways, because that is where marks go: 1200 − 1001 + 1 = 200 by counting, and 5 × 40 = 200 by formula. If those disagree you have an off-by-one in the initial sequence number. Notice what the sequence numbers count: bytes, not messages. TCP has no idea there were five messages. That is the byte-stream promise, visible in the numbering itself.
03 Mechanics
The table, the two headers, and the choosing rule
The first table is the answer to the question as it is actually asked. Learn it as rows, not as two lists, because the interviewer will pick one row and push on it. The right-hand column is what turns each row from a fact into an answer: it says what the property costs the side that has it.
| Property | TCP | UDP | What it costs, and who pays |
|---|---|---|---|
| Connection | connection oriented: 3-way handshake before data, 4-way teardown after |
connectionless: the first packet on the wire is data |
One full round trip of latency before TCP can send a byte, plus a control block and timers held at both ends for the life of the connection. |
| Reliability | acknowledged and retransmitted until delivered or the connection is torn down |
none: a lost datagram is gone and neither end is told |
A copy of every unacknowledged byte stays in the sender’s buffer, and a retransmission arrives at least one round trip late. |
| Ordering | sequence number on every segment; the receiver reassembles in order |
none: datagrams are handed up in arrival order |
Head of line blocking. Correctly received bytes wait in the receiver’s buffer for a segment that has not come. |
| Message boundaries | not preserved: a byte stream, so one send is not one recv |
preserved: one send is exactly one datagram is exactly one recv |
TCP applications must carry their own framing. Sending a length before each message is the usual fix, and forgetting it is the classic first-socket bug. |
| Header size | 20 bytes minimum, 60 maximum with options |
8 bytes, fixed, always |
12 bytes per segment, which is 30% overhead on a 40-byte message and 0.8% on a 1,460-byte one. It matters for small frequent messages and almost nowhere else. |
| Flow control | receiver advertises a window in every ACK; the sender may not exceed it |
none: a fast sender can overrun a slow receiver |
The sender idles whenever the window is smaller than the path can carry, which is where a badly tuned buffer silently caps throughput. |
| Congestion control | slow start, congestion avoidance, fast recovery; cwnd shrinks on loss |
none: it sends whenever the application writes |
TCP deliberately slows itself down when the path is busy. A UDP application that does not implement something equivalent is taking bandwidth from everyone who does. |
| Error checking | 16-bit checksum, mandatory |
16-bit checksum, optional over IPv4, mandatory over IPv6 |
Both detect corruption; only TCP does anything about it. UDP silently discards a datagram whose checksum fails, which looks exactly like a loss. |
| Speed | slower to start, and stalls on loss |
no setup, no stalls, less header |
The honest version: for one small exchange UDP wins outright. For a long bulk transfer over a busy path TCP is usually faster, because a UDP sender with no congestion control drives the path into loss and its own useful throughput collapses. |
| Broadcast and multicast | unicast only |
unicast, broadcast and multicast |
A connection has exactly two endpoints by definition, so there is no such thing as a TCP handshake with a group. Anything that has to reach many receivers at once has to be UDP, which is why DHCP discovery broadcasts and IPTV multicast both run over it. |
Now the two headers, field by field, because the size difference is where the trade-off stops being a list of adjectives and becomes twelve countable bytes. Read the third column: every field TCP has and UDP does not is buying exactly one of the promises in the table above.
| Field | Size | In TCP it buys | In UDP |
|---|---|---|---|
| Source port | 2 bytes | Present in both. Process-to-process delivery is the one job they share. | |
| Destination port | 2 bytes | Present in both. | |
| Checksum | 2 bytes | Present in both. Detects corruption; does not repair it. | |
| Length | 2 bytes | absent | UDP only: header plus data, so the minimum legal value is 8. TCP needs no length because a stream has no message to measure. |
| Sequence number | 4 bytes | ordering | absent |
| Acknowledgement number | 4 bytes | reliability | absent |
| Data offset, reserved, 8 control flags | 2 bytes | the connection — SYN, ACK, FIN, RST, PSH, URG and the two ECN bits | absent |
| Window size | 2 bytes | flow control | absent |
| Urgent pointer | 2 bytes | Valid only when URG is set. Effectively dead in practice, but it is 2 of the 20 bytes and it is asked about. | absent |
| The arithmetic. Shared fields: two ports and a checksum, 2 + 2 + 2 = 6 bytes. UDP adds only Length: 6 + 2 = 8. TCP adds sequence, acknowledgement, offset-and-flags, window and urgent pointer: 6 + 4 + 4 + 2 + 2 + 2 = 20. The difference is 20 − 8 = 12 bytes. Subtract field by field rather than trusting the total, because one detail falls out that is worth carrying: TCP’s own fields come to 4 + 4 + 2 + 2 + 2 = 14, not 12, and UDP’s own Length is 2, so the urgent pointer and the Length field cancel each other. What is left is 14 − 2 = 12, and those 12 bytes are four fields buying four named promises: sequence 4, acknowledgement 4, offset-and-flags 2, window 2. | |||
The promise with no header field, which is the one people forget. Count the four again: sequence buys ordering, acknowledgement buys reliability, the flag bits buy the connection, the window buys flow control. Congestion control is not in that list, because it costs no header bits at all. The congestion window cwnd is a variable inside the sender, inferred from loss and delay, and it is never transmitted to anybody. The sender may have at most min(rwnd, cwnd) bytes unacknowledged at any moment: rwnd is the receiver’s advertised window, which it was told, and cwnd is its own estimate of the path, which it had to work out. That is exactly why congestion control is the guarantee people leave out when they build reliability over UDP: there is no field to remind them.
Two limits that come straight out of field widths. The TCP data offset is 4 bits and counts 32-bit words, so the largest header it can describe is 15 × 4 = 60 bytes, leaving at most 40 bytes for options. The window field is 16 bits, so an unaided receiver can advertise at most 65,535 bytes; the window scale option (RFC 7323) allows a left shift of up to 14, so the effective window can reach 65,535 × 2¹⁴, about 1 GB, which is what makes long fat paths usable. On the UDP side the Length field is 16 bits, so a datagram tops out at 65,535 bytes including its own 8-byte header; over IPv4 the whole IP datagram must also fit in 65,535, so the largest UDP payload you can actually send is 65,535 − 20 − 8 = 65,507 bytes. On an ordinary 1,500-byte Ethernet MTU the practical TCP segment size is 1,500 − 20 − 20 = 1,460 bytes, which is the number behind almost every throughput calculation you will be asked for.
Congestion control is the one property in the table that has to be watched over time rather than described, so here it is round by round. Take MSS = 1,460 bytes, an initial cwnd of 1 MSS and ssthresh = 8 MSS.
Slow start, then congestion avoidance, then a timeout. In slow start every acknowledged segment adds one MSS, so cwnd doubles every round trip: round 1 sends 1, round 2 sends 2, round 3 sends 4, round 4 sends 8. cwnd has now reached ssthresh, so the growth changes shape. Congestion avoidance adds one MSS per round trip rather than per acknowledgement: round 5 sends 9, round 6 sends 10. That is 1 + 2 + 4 + 8 + 9 + 10 = 34 segments in six round trips. Now a timeout at the end of round 6: ssthresh becomes half the current window, 10 / 2 = 5, and cwnd collapses to 1. Round 7 sends 1, round 8 sends 2, round 9 sends 4, and doubling again would overshoot, so round 10 sends 5 and congestion avoidance resumes from there. At cwnd = 10 MSS the sender has 10 × 1,460 = 14,600 bytes outstanding per round trip, so on a 100 ms path that is 14,600 bytes / 0.1 s = 146,000 bytes per second, which is 146,000 × 8 = 1,168,000 bits per second, about 1.17 Mbit/s. Watch the units there: throughput is quoted in bits and windows in bytes, and mixing them is the single most common arithmetic slip on this topic. Two real-world differences worth naming in one clause: RFC 6928 raised the initial window from 1 to 10 MSS, and Linux ships CUBIC rather than this Reno curve, so a real connection reaches full speed far faster than the shape above. The shape is still what gets examined.
Sliding window, and the two limits that are asked as a pair. With an m-bit sequence number field there are 2ᵐ distinct numbers to work with. Go-Back-N allows a sender window of at most 2ᵐ − 1: leaving one number unused is what stops a full window of retransmissions from looking like a full window of new frames. Selective Repeat allows at most 2ᵐ / 2, because the receiver buffers out-of-order frames and its own window must not be able to overlap the previous one, or a retransmitted old frame is accepted as a new frame. So for m = 3 the answers are 7 and 4, not 8 and 8. TCP is neither in its pure form: it acknowledges cumulatively like Go-Back-N, but with the SACK option (RFC 2018) the sender learns exactly which blocks arrived and resends only those, which is Selective Repeat behaviour.
Which brings the whole thing back to one question. Not which protocol is better, and not a list of applications to memorise, but this: would the application rather have a byte late and correct, or on time and approximate? Everything below is that question answered by real software, and the reason is what earns the mark, not the name.
| Application | Transport | The reason, which is the part that is marked |
|---|---|---|
| Web: HTTP/1.1 and HTTP/2 | TCP 80 and 443 | A page with one byte wrong is not a slightly worse page, it is a broken script. Nothing here has a deadline that a retransmission would miss. |
| Email: SMTP, IMAP, POP3 | TCP 25, 143, 110 | A message is worth nothing if it is nearly right, and nobody cares whether it took 200 ms longer. |
| Remote shell: SSH | TCP 22 | A dropped character is a different command. The stream must be exact and in order, and the session lasts long enough for a handshake to be irrelevant. |
| File transfer: FTP | TCP 21 control, 20 data | A file that is 99.99% correct is a corrupt file. Bulk transfer is also exactly where congestion control earns its place. Do not attach these two ports to SFTP: that is a file-transfer subsystem carried inside SSH, so it runs on TCP 22 with no separate data connection. |
| DNS query | UDP 53, with TCP 53 as fallback | One small question and one small answer. A handshake would triple the cost of resolving a name. TCP is used when the response is truncated or for a zone transfer. |
| DHCP | UDP 67 server, 68 client | The client has no IP address yet and is broadcasting to a server it cannot name. TCP cannot broadcast, so the question does not arise. |
| Voice and video calls: RTP | UDP, dynamic ports | A frame retransmitted one round trip later arrives after its playout instant and is thrown away. Concealing a 20 ms gap beats freezing the picture to fetch it. |
| Live streaming and IPTV | UDP, often multicast | Same deadline argument, plus one sender feeding thousands of receivers, which only multicast can do and TCP cannot. |
| Multiplayer game state | UDP | A lost position update is superseded by the next one 33 ms later. Resending it would place the player where they used to be. |
| Also UDP: TFTP, SNMP, NTP | UDP 69, 161, 123 | Short request-response exchanges where the application does its own retries, or where an extra round trip would spoil the measurement being taken. |
| HTTP/3 over QUIC | UDP 443 | Reliability, ordering and congestion control rebuilt above UDP in user space, precisely to escape TCP’s head of line blocking and its setup cost. The trade-off is a design choice, not a law. |
The port ranges, which get quoted wrongly more than anything else here. IANA divides the 16-bit port space into three: well known 0 to 1023, which on Unix only a privileged process may bind; registered 1024 to 49151, assigned to named applications on request; and dynamic, private or ephemeral 49152 to 65535, which is where a client’s own source port comes from. Two things to be careful about. The TCP and UDP port spaces are separate, so TCP 53 and UDP 53 are different endpoints that happen to serve the same protocol. And real systems do not follow the ephemeral range: Linux defaults to 32768 to 60999, which you can read with sysctl net.ipv4.ip_local_port_range. Quote the IANA ranges in an interview and name the Linux difference in a clause; that clause is worth more than the numbers.
05 Cheat sheet
Twelve answers to have ready on the morning
Every row is something you can be asked to state in under ten seconds. The right-hand column is the specific wrong answer that gets given, not a general caution.
| What they ask | The answer | The trap |
|---|---|---|
| Header sizes | TCP 20 bytes minimum, 60 with options; UDP 8 bytes fixed | quoting a range for UDP — UDP has no options and no variable part |
| The 12-byte difference | sequence 4 + acknowledgement 4 + offset and flags 2 + window 2 | listing TCP’s own fields and stopping at 14 — the urgent pointer 2 is cancelled by UDP’s Length 2, and the ports and checksum are in both |
| Setup and teardown | 3-way handshake, 4-way teardown | saying the teardown is also three — each side sends its own FIN and gets its own ACK |
| Handshake sequence numbers | SYN seq=x; SYN-ACK seq=y ack=x+1; ACK seq=x+1 ack=y+1 | ack=x — a SYN consumes one sequence number even with no data, and so does a FIN |
| What an ack number means | the next byte the receiver expects, not the last byte it got | Off by one in every subsequent answer if you get this wrong. |
| How loss is detected | retransmission timeout, or 3 duplicate ACKs triggering fast retransmit | saying every segment is acknowledged individually — TCP acknowledges cumulatively |
| Flow control vs congestion control | receiver protection vs network protection; in flight = min(rwnd, cwnd) | Using the two names interchangeably. One is advertised to you, the other you infer. |
| Sliding window maxima | Go-Back-N 2ᵐ − 1; Selective Repeat 2ᵐ / 2 | 2ᵐ for Go-Back-N — one number must stay unused |
| Message boundaries | UDP preserves them; TCP does not, so frame it yourself | Assuming one send becomes one recv on TCP. It works on localhost and fails in production. |
| Broadcast and multicast | UDP only; a connection has exactly two endpoints | claiming TCP multicast exists |
| Port ranges | well known 0–1023, registered 1024–49151, ephemeral 49152–65535 | ephemeral starting at 1024 — and note Linux actually uses 32768–60999 |
| DNS and QUIC | DNS uses UDP 53, and TCP 53 for truncated responses and zone transfers; QUIC is reliable over UDP 443 | DNS is UDP only, and QUIC is unreliable because UDP is |
06 Where & why
Four real systems, and the reason each one chose
None of this is a teaching abstraction. Each of these four is a system you have already used today, and in each case the choice of transport is visible in how the software behaves when the network is bad.
A remote shell is a byte stream where a dropped character is a different command, so TCP is the only sane choice and the handshake is irrelevant across a session that lasts an hour. The interesting part is what you feel: when one segment is lost, your terminal freezes for a moment even though the keystrokes you typed afterwards already reached the server. That is head of line blocking, live on your own screen. It is also why mosh exists, which carries the same session over UDP and repaints the screen from the latest state instead of replaying a stream.
A name lookup is one small question and one small answer, and a three-way handshake would cost a round trip before the question could even be asked. So the query goes out as a single UDP datagram and the resolver retries on its own if no reply comes. When the response is too large for the buffer the client advertised, the server sets the TC truncated bit and the resolver repeats the whole query over TCP; zone transfers between name servers are always TCP. Saying “DNS is UDP” and stopping is the standard half-answer.
Every browser video call carries audio and video as RTP over UDP. Audio is typically packetised every 20 ms, so a frame recovered one round trip later has already missed the instant it was meant to be played and is discarded on arrival. Concealing the gap sounds better than pausing to fetch it. The reliability that does exist is selective and application-made: the codec sends key frames and the sender adapts its bitrate, which is congestion control rebuilt for media rather than congestion control skipped.
HTTP/2 multiplexes many requests over one TCP connection, so one lost segment stalls every request sharing it, not only the one that lost data. QUIC moves reliability, ordering and congestion control into user space above UDP and keeps ordering per stream, so a loss on one request no longer blocks the others. It folds the TLS handshake into the transport handshake as well, so a new connection costs one round trip instead of two or three. UDP was chosen not because reliability was unwanted but because it was the only transport a new design could actually be deployed on.
07 Interview questions
What they ask, and the follow up that separates candidates
Almost every interview that touches networking opens with question one, which means almost every candidate has an answer ready and almost none survives the second question. The pattern is that they let you list the differences, then pick the one you sounded least sure about and ask you to justify it. Answer with a mechanism and a cost, never with an adjective.
What is the difference between TCP and UDP?
Walk me through the three-way handshake with the sequence numbers.
A UDP checksum arrives as all zeros. What does that mean?
TCP is a byte stream. What does that actually mean for my code?
What is head of line blocking, and whose fault is it?
What is the difference between flow control and congestion control?
Why does DNS use UDP, and when does it use TCP?
If UDP is unreliable, why would anyone use it for a video call?
QUIC runs over UDP but it is reliable. Explain that.
For an m-bit sequence number field, what is the maximum window size for Go-Back-N and for Selective Repeat?
Which one is actually faster?
08 Practice problems
Six to work on paper
Four of these six are arithmetic, so write the units next to every number as you go. Two of them turn on counting segments that carry no data at all, which is the thing that is easy to leave out of a total and impossible to leave out of a real connection.