TCP vs UDP: Streams Against Datagrams
What each transport protocol gives you and costs you, shown with a loopback test: TCP merged two sends into one read, UDP kept them as two messages, and UDP raised no error sending to a closed port.
TCP is a connection-oriented protocol that provides a reliable, ordered byte stream with retransmission, flow control and congestion control, while UDP sends independent datagrams with no connection and no delivery guarantees. The detail interviewers expect: TCP has no message boundaries, so applications need framing, and UDP suits traffic like voice, games and DNS where late data is useless.
TCP provides a reliable, ordered byte stream with connection setup, flow control and congestion control. UDP sends independent datagrams with no connection and no guarantees. In a test, two TCP sends arrived as one read (abcdef) and two UDP sends arrived as two (abc, def), and UDP sending to a closed port reported nothing.
- TCP gives a reliable, ordered byte stream with a connection handshake, acknowledgements, retransmission, flow control and congestion control, while UDP adds only ports and a checksum to IP.
- The minimum header is 20 bytes for TCP and 8 bytes for UDP.
- TCP does not preserve message boundaries: in a loopback test, two sends of abc and def arrived as one read of abcdef, while UDP delivered them as two datagrams.
- Sending a UDP datagram to a closed port returned normally with no error, while a TCP connect to a closed port fails at once with ConnectionRefusedError.
- DNS usually runs over UDP because a query and response fit in single small datagrams, avoiding a connection round trip, and it falls back to TCP for large responses.
- QUIC, the base of HTTP/3, builds reliability with sequence numbers, acknowledgements and retries on top of UDP.
01Two different promises
Both protocols run on top of IP, which delivers packets on a best-effort basis: packets can be lost, duplicated, delayed or reordered. TCP builds a reliable service on top of that. UDP adds almost nothing: ports and a checksum (optional over IPv4, mandatory over IPv6).
The choice is not just speed against safety. It is a choice of what the application wants to deal with. TCP hands you a clean stream and does the recovery work for you. UDP hands you whatever arrived, and anything beyond that is your job.
02What TCP adds
TCP sets up a connection with a handshake, numbers every byte, and the receiver acknowledges what it gets. Missing data is retransmitted, out-of-order data is reordered before delivery, and duplicates are dropped. Flow control stops a fast sender from overwhelming a slow receiver, using the receiver's advertised window. Congestion control slows the sender when the network is overloaded.
All of this costs something. There is a round trip to connect. A lost packet stalls everything behind it until it is retransmitted (head-of-line blocking). And the headers are larger: 20 bytes for TCP and 8 for UDP at minimum.
03What UDP gives
A UDP socket just sends datagrams to an address and port. There is no connection, no retransmission, no ordering, and no congestion control. Each datagram is delivered whole or not at all (the checksum catches corruption). Because there is no setup, the first message can go out immediately, and there is no connection state to hold on the server. That makes it good for small request-response exchanges such as DNS, for broadcast and multicast, and for traffic where late data is worthless.
The flip side: if you need reliability on UDP, you build it, with sequence numbers, acknowledgements and retries. Protocols such as QUIC do exactly that, on top of UDP, so they can be deployed without changing the operating system's TCP.
04Stream versus datagram: a test
import socket, time
# TCP: set up a loopback connection, then two sends
ls = socket.socket(); ls.bind(('127.0.0.1', 0)); ls.listen()
c = socket.create_connection(ls.getsockname())
conn, _ = ls.accept()
c.sendall(b'abc'); c.sendall(b'def')
# server side, one read:
time.sleep(0.1) # let both segments arrive
print(conn.recv(100)) # b'abcdef'
# UDP: two sockets, then two sends
u1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); u1.bind(('127.0.0.1', 0))
u2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = u1.getsockname()
u2.sendto(b'abc', addr); u2.sendto(b'def', addr)
print(u1.recvfrom(100)[0], u1.recvfrom(100)[0]) # b'abc' b'def'Over loopback, the TCP server's single recv returned b'abcdef'. TCP has no concept of message boundaries; it is one continuous stream, so applications add their own framing, such as a length prefix or a delimiter. UDP returned two reads, b'abc' and b'def', each one a whole datagram. The TCP result depends on timing (it could also have returned b'abc' first), so never write code that assumes one send equals one recv.
05Silent failure with UDP
import socket
u2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
u2.sendto(b'x', ('127.0.0.1', 9)) # nothing listening on port 9The call returned normally with no exception. On a TCP connect to a closed port the same test would have failed at once with ConnectionRefusedError. With UDP the sender usually learns nothing; an ICMP "port unreachable" message may come back, but it is not reliable and many systems do not report it for unconnected sockets. If your application needs to know whether a message arrived, you need acknowledgements of your own, with timeouts.
06Choosing between them
Use TCP when every byte must arrive in order and you do not want to write recovery logic: web pages, APIs, email, file transfer, databases. Use UDP when fresh data matters more than complete data or when you want your own control: live video and voice calls (a late audio frame is useless, so retransmitting it is wasted effort), online games sending position updates, DNS queries, and as the base for QUIC and HTTP/3.
A good question to ask is what you would do with a packet that arrives 500 ms late. If you would throw it away, UDP is a candidate. If you would wait for it, you probably want TCP.
07Common interview questions
Q: Main difference between TCP and UDP?
A: TCP is connection-oriented and reliable, with ordering and congestion control. UDP is connectionless and unreliable, with low overhead.
Q: Why is DNS usually UDP?
A: A query and response fit in single small datagrams, so avoiding the connection setup saves a round trip. It falls back to TCP for large responses.
Q: Does TCP preserve message boundaries?
A: No. It is a byte stream, so applications need framing.
Q: Why do video calls prefer UDP?
A: Retransmitting old frames only adds delay. Skipping a lost frame is better than stalling.
Q: Can UDP be reliable?
A: Yes, if the application or protocol (QUIC, for example) adds acknowledgements and retransmission.
Q: Header sizes?
A: 20 bytes minimum for TCP, 8 bytes for UDP.