TCP vs UDP

Transport Layer · 30 min

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
TCP will hold back data that has already arrived in order to deliver data that has not. UDP will not. Reliability, ordering, the handshake, the window and the extra twelve header bytes are all the price of that single promise, and every place UDP is chosen is a place where the promise costs more than it is worth.

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.

Ask the application one question and the choice answers itself: is a byte that arrives late still worth having? If yes, take TCP and pay for the handshake, the header and the stalls. If a late byte is worse than no byte at all, take UDP, and accept that loss, reordering and duplication are now yours to handle or to ignore.
Connectionless and connection orientedA connectionless protocol keeps no state about the other end: each datagram stands alone and the first one carries data. A connection oriented protocol exchanges segments first to agree starting sequence numbers and window sizes, and both ends then hold a control block per connection for as long as it lives.
Byte stream vs message orientedTCP treats what you write as an unstructured run of bytes and is free to merge two writes into one segment or split one write across three, so one send is not one recv. UDP preserves the boundary: one sendto becomes exactly one datagram and exactly one recvfrom, of exactly that length.
Head of line blockingData that arrived correctly cannot be delivered because earlier data has not arrived. It is created by the ordering guarantee, not by the network, so it exists in TCP and cannot exist in UDP. It is the specific cost that QUIC was designed to remove.

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.

1 · The application writesFive send calls of 40 bytes. UDP puts M1 on the wire immediately. TCP cannot: it has to open a connection first.
2 · TCP pays a round tripSYN seq=1000, then SYN-ACK seq=5000 ack=1001, then ACK seq=1001 ack=5001. One full RTT gone before M1 leaves.
3 · The network does the same thing to bothM3 is dropped. M5 overtakes M4. IP treats the two protocols identically, so nothing that follows is caused by the network.
4 · UDP hands up whatever landsM1, M2, then M5, then M4. Four of five, in the wrong order, boundaries intact, and no error is reported to anybody.
5 · TCP holds the gap openM4 and M5 are buffered, M3 is retransmitted, then all 200 bytes are delivered in order as one stream. The stall is the price.

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.

PropertyTCPUDPWhat 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.

FieldSizeIn TCP it buysIn UDP
Source port2 bytesPresent in both. Process-to-process delivery is the one job they share.
Destination port2 bytesPresent in both.
Checksum2 bytesPresent in both. Detects corruption; does not repair it.
Length2 bytesabsentUDP only: header plus data, so the minimum legal value is 8. TCP needs no length because a stream has no message to measure.
Sequence number4 bytesorderingabsent
Acknowledgement number4 bytesreliabilityabsent
Data offset, reserved, 8 control flags2 bytesthe connection — SYN, ACK, FIN, RST, PSH, URG and the two ECN bitsabsent
Window size2 bytesflow controlabsent
Urgent pointer2 bytesValid 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.

ApplicationTransportThe reason, which is the part that is marked
Web: HTTP/1.1 and HTTP/2TCP 80 and 443A 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, POP3TCP 25, 143, 110A message is worth nothing if it is nearly right, and nobody cares whether it took 200 ms longer.
Remote shell: SSHTCP 22A 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: FTPTCP 21 control, 20 dataA 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 queryUDP 53, with TCP 53 as fallbackOne 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.
DHCPUDP 67 server, 68 clientThe 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: RTPUDP, dynamic portsA 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 IPTVUDP, often multicastSame deadline argument, plus one sender feeding thousands of receivers, which only multicast can do and TCP cannot.
Multiplayer game stateUDPA 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, NTPUDP 69, 161, 123Short 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 QUICUDP 443Reliability, 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.

TCP and UDP are not a good protocol and a bad one. They are the two answers to a question about deadlines, and QUIC exists to prove it: given a reason to want reliability without TCP’s ordering stalls and setup cost, engineers rebuilt reliability on top of UDP rather than accept the package as sold. The properties in the first table are separable. They only arrive bundled because TCP bundled them in 1981.

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 askThe answerThe trap
Header sizesTCP 20 bytes minimum, 60 with options; UDP 8 bytes fixedquoting a range for UDP — UDP has no options and no variable part
The 12-byte differencesequence 4 + acknowledgement 4 + offset and flags 2 + window 2listing 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 teardown3-way handshake, 4-way teardownsaying the teardown is also three — each side sends its own FIN and gets its own ACK
Handshake sequence numbersSYN seq=x; SYN-ACK seq=y ack=x+1; ACK seq=x+1 ack=y+1ack=x — a SYN consumes one sequence number even with no data, and so does a FIN
What an ack number meansthe next byte the receiver expects, not the last byte it gotOff by one in every subsequent answer if you get this wrong.
How loss is detectedretransmission timeout, or 3 duplicate ACKs triggering fast retransmitsaying every segment is acknowledged individually — TCP acknowledges cumulatively
Flow control vs congestion controlreceiver 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 maximaGo-Back-N 2ᵐ − 1; Selective Repeat 2ᵐ / 22ᵐ for Go-Back-N — one number must stay unused
Message boundariesUDP preserves them; TCP does not, so frame it yourselfAssuming one send becomes one recv on TCP. It works on localhost and fails in production.
Broadcast and multicastUDP only; a connection has exactly two endpointsclaiming TCP multicast exists
Port rangeswell known 0–1023, registered 1024–49151, ephemeral 49152–65535ephemeral starting at 1024 — and note Linux actually uses 32768–60999
DNS and QUICDNS uses UDP 53, and TCP 53 for truncated responses and zone transfers; QUIC is reliable over UDP 443DNS is UDP only, and QUIC is unreliable because UDP is
They share more than they differ inBoth are transport protocols, both address processes with 16-bit ports, both carry a checksum, both ride on best-effort IP and neither can stop a packet being dropped. Start an answer there and the differences land as consequences instead of as a memorised list.
Twelve bytes, four promisesSequence number buys ordering, acknowledgement number buys reliability, the flag bits buy the connection, the window buys flow control. The urgent pointer is the fifth TCP-only field, and it is cancelled by UDP’s Length. Congestion control buys nothing in the header at all, because cwnd lives only in the sender and is never transmitted.
Reliability is an endpoint decisionThe routers treat both protocols identically. Everything TCP does differently happens in two hosts, which is why QUIC could rebuild reliability, ordering and congestion control over UDP in user space and get faster rather than slower.

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.

OpenSSH
TCP 22, and the stall you can feel

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.

Google Public DNS 8.8.8.8
UDP 53 first, TCP 53 when the answer will not fit

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.

WebRTC
UDP for the media, because a late frame is a useless frame

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.

Cloudflare · HTTP/3
QUIC over UDP 443, which is the whole argument in one product

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.

Notice what all four have in common. Nobody chose a protocol because it was faster or safer in the abstract; each one answered the deadline question for its own traffic and took the consequences. That is the answer an interviewer is listening for, and it is why “TCP is reliable, UDP is fast” sounds like a memorised line even when both halves are true.

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?
They share the same job of getting data from one process to another over best-effort IP, and both use 16-bit port numbers and a checksum to do it. TCP then adds four promises: a connection set up by a three-way handshake before any data, reliability through acknowledgement and retransmission, ordering through sequence numbers, and a byte-stream abstraction with no message boundaries, plus flow control and congestion control on top. UDP adds none of them, so its first packet is data, a lost datagram is gone silently, and one sendto is exactly one datagram with its boundary preserved. The cost of TCP is a 20-byte header against 8, a round trip before any data moves, and head of line blocking when a segment is lost.
Walk me through the three-way handshake with the sequence numbers.
The client picks an initial sequence number x and sends a SYN carrying seq = x. The server picks its own y and replies with a SYN-ACK carrying seq = y and ack = x + 1. The client completes it with an ACK carrying seq = x + 1 and ack = y + 1, and that ACK may already carry data. Both acknowledgement numbers are one higher than the sequence number they answer because a SYN consumes one sequence number even though it carries no payload, and a FIN does the same at teardown. Three segments open a connection; closing it takes four, because each side sends its own FIN and gets its own ACK.
A UDP checksum arrives as all zeros. What does that mean?
Over IPv4 it means the sender chose not to compute one, because the UDP checksum is optional there and zero is the agreed way of saying “not checked”. That creates an oddity worth knowing: if the real computed checksum happens to be zero, it is transmitted as all ones instead, since the two are equivalent in one’s complement arithmetic and only one of them can mean “absent”. Over IPv6 the checksum is mandatory, because IPv6 dropped the header checksum that IPv4 had, so a zero there is a protocol error and the datagram is discarded. Either way a failed checksum means the datagram is silently dropped, which the application cannot distinguish from an ordinary loss.
TCP is a byte stream. What does that actually mean for my code?
It means TCP does not know your messages exist. It is free to merge two of your writes into one segment or split one write across three, so a receiver that calls recv once has no guarantee of getting one message, or a whole message, or only one message. The sequence numbers count bytes, not messages, which is the same fact visible in the header. UDP is the opposite: one sendto becomes one datagram and one recvfrom of exactly that length, boundary intact. The practical consequence is that a TCP application has to carry its own notion of where a message ends, and code that skips it usually works on localhost and fails the moment there is a real network underneath.
What is head of line blocking, and whose fault is it?
It is data that arrived correctly being withheld from the application because earlier data has not arrived. If segment three is lost while four and five reach the receiver, four and five sit in the reassembly buffer untouched, because delivering them would break the ordering guarantee. It is nobody’s fault in the sense of a bug: it is the ordering promise working exactly as specified, which is why UDP cannot have it and TCP cannot avoid it. It is the specific problem QUIC set out to solve, by keeping ordering within each stream but not across streams, so a loss on one request no longer stalls the others sharing the connection.
What is the difference between flow control and congestion control?
Flow control protects the receiver and congestion control protects the network in between. Flow control is explicit: the receiver puts its free buffer space into the window field of every acknowledgement, so the sender is told directly how much it may have outstanding. Congestion control is inferred: nothing in any header tells the sender how busy the path is, so it maintains its own congestion window and adjusts it from evidence, growing while acknowledgements keep arriving and shrinking sharply on loss. Treating the two as one thing is the standard slip, and the giveaway question is which of the two appears in the header, because only one of them does.
Why does DNS use UDP, and when does it use TCP?
A lookup is one small question and one small answer, so a three-way handshake would spend a full round trip before the question could even be asked, and a resolver doing this thousands of times per second would also hold thousands of connection control blocks. UDP costs one datagram out and one back, and the resolver handles a lost reply by asking again. It switches to TCP in two cases: when the answer does not fit in what the client said it could accept, in which case the server sets the truncated bit and the client repeats the whole query over TCP, and for zone transfers between name servers, which are bulk and must be exact. Both use port 53, and they are separate endpoints because the TCP and UDP port spaces are independent.
If UDP is unreliable, why would anyone use it for a video call?
Because a retransmission arrives too late to be worth having. Audio is packetised every 20 ms or so, and a packet recovered one round trip later has already missed the instant it was supposed to be played, so the receiver would throw it away after waiting for it. Concealing a small gap sounds and looks better than pausing everything to fetch data that is already stale. There is a second reason people miss: TCP’s congestion control would cut the sending rate sharply on the first loss, which for a live stream means the picture freezing rather than degrading. UDP lets the application decide to lower the bitrate instead, which is the right response for media.
QUIC runs over UDP but it is reliable. Explain that.
Reliability is a property of what the two endpoints do, not of the wire, so it can be implemented anywhere. QUIC puts acknowledgements, retransmission, ordering and congestion control in user space above UDP rather than in the kernel above IP. It gains two things by doing so. Ordering is kept per stream instead of per connection, so a loss affecting one HTTP request no longer stalls the others, and the TLS handshake is folded into the transport handshake, so a new connection costs one round trip instead of two or three. UDP was chosen because it is the only transport that middleboxes already pass and that can be upgraded by shipping a new browser, rather than by changing every operating system on the path.
For an m-bit sequence number field, what is the maximum window size for Go-Back-N and for Selective Repeat?
Go-Back-N allows at most 2ᵐ − 1 and Selective Repeat at most 2ᵐ / 2. Go-Back-N leaves one number unused so that a window of retransmissions can never look identical to a window of fresh frames when acknowledgements are lost. Selective Repeat is stricter because its receiver buffers out-of-order frames and therefore has a window of its own; if the sender window were more than half the number space, the receiver’s window could overlap the previous one and it would accept a retransmitted old frame as a new one. So for m = 3 the answers are 7 and 4, never 8 and 8. TCP is closest to Go-Back-N because it acknowledges cumulatively, but with the SACK option it behaves like Selective Repeat.
Which one is actually faster?
For a single small exchange, UDP, clearly: no handshake, so the answer is back in one round trip instead of two, and 12 fewer header bytes each way. For a long bulk transfer the honest answer is usually TCP, which surprises people. A UDP sender with no congestion control writes as fast as the application will let it, overruns the narrowest link on the path, and loses a large fraction of what it sends, so its useful throughput collapses even though its raw sending rate looks high. TCP deliberately slows down to find the rate the path can actually carry. So “UDP is faster” is true about latency and overhead and often false about throughput, and saying which you mean is the difference between a good answer and a slogan.

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.

The header that grew

Easy
A captured TCP segment has a data offset field reading 8, and it travels over Ethernet with a 1,500-byte MTU behind a 20-byte IPv4 header. Give the TCP header length in bytes, how many of those bytes are options, and the largest payload this segment can carry. Then give the same three numbers for a UDP datagram on the same link.
Follow-up
Two of the three UDP numbers need no arithmetic at all, and working out why is the point of the problem. One of the three TCP numbers is not 1,460, even though 1,460 is the figure everyone quotes for Ethernet.
Show the hint
The data offset counts 32-bit words, not bytes, so convert it before you subtract anything from 1,500.

Count the bytes on the wire

Easy
An application sends 5 messages of 40 bytes each over IPv4 with no IP or TCP options. Compute the total bytes crossing the wire for UDP, and for TCP counting the 3 handshake segments, the 5 data segments and the 4 teardown segments. Ignore the acknowledgements coming back. Give each total and its payload efficiency as a percentage.
Follow-up
Twelve extra header bytes per message accounts for only 60 of the difference, and the real gap is several times that. The bulk of it is segments that carry no payload whatsoever, so the answer depends on counting segments before you count bytes.
Show the hint
Fix the per-segment overhead first: the IPv4 header is a fixed 20 bytes here, and it is charged to every segment including the ones with an empty payload.

Three in a row

Medium
Same connection and same byte numbering as section 02, but this time M2, M3 and M4 are all lost and only M5 arrives. State the acknowledgement number the receiver sends when M5 arrives, how many duplicate ACKs the sender has collected, whether fast retransmit can fire, and how many bytes the application has been given at the moment immediately before the retransmission timer expires.
Follow-up
More losses do not produce more duplicate ACKs. Work out how many segments actually reach the receiver after the gap opens, because that count, not the number of losses, is what decides whether fast retransmit is possible at all.
Show the hint
A duplicate ACK is generated by a segment that arrives, never by one that is lost, and the acknowledgement number always names the next byte the receiver still owes the application.

The window that will not open

Medium
A receiver advertises a window of 4,000 bytes. The sender’s congestion window is 10,000 bytes and the round-trip time is 100 ms. State how many bytes may be unacknowledged at any instant, then compute the maximum throughput in bytes per second and in megabits per second. Say which of the two windows you would have to change to go faster, and who owns it.
Follow-up
The two hosts are joined by a gigabit link and the link speed appears nowhere in the answer. Work out what the sender is actually waiting for, and you will see why doubling the link speed changes this number by nothing at all.
Show the hint
A sender may have at most one window of unacknowledged bytes outstanding and cannot send more until an acknowledgement comes back, which takes exactly one round trip. Keep bytes and bits in separate columns.

One game, three kinds of traffic

Medium
A multiplayer game sends 30 position updates per second per player, and the same client also has to deliver chat messages and an in-game purchase confirmation. Choose a transport for each of the three kinds of traffic and give the one-clause reason for each. Then compute how many position updates the client misses if a single lost segment stalls a shared stream for one retransmission timeout of 200 ms.
Follow-up
Two of the three choices are obvious and the third is the interesting one, because a single application needs opposite guarantees at the same time. Deciding once for the whole program is the wrong shape of answer, so say what each kind of traffic loses under the other choice.
Show the hint
Ask the deadline question separately for each kind of traffic rather than for the application as a whole, and work out the gap between two position updates in milliseconds before you do the division.

Rebuild it in user space

Hard
You must add just enough reliability to a UDP-based file transfer that a lost datagram is recovered, without giving up UDP’s message boundaries. List the minimum set of fields you would add to your own header, give each one a size in bytes, and name the TCP field it corresponds to. Then name the one guarantee you have deliberately not rebuilt and state what it does to the network when a thousand of your clients run at once.
Follow-up
The header you arrive at is close to a subset of TCP’s, which is the point: the question is never whether reliability is possible over UDP, only which of TCP’s promises you are choosing to skip. The guarantee you leave out will not show up as a hole in your field list when you check your work, which is exactly why it is the easy one to forget.
Show the hint
Work backwards from the TCP run in the console: every step where TCP recovered something from the loss names the header field that made that recovery possible.