Question bank — Socket programming — TCP server - client, UDP server - client in Python
Every answer below is a reason, not a bare yes/no — because on an exam or in an interview the reason is the mark.
First, the vocabulary these traps assume
Most traps below hinge on four words. We define them here, on this page, so you never have to leave to understand a trap.
The systems picture: how a byte stream fragments and coalesces
Almost every TCP trap ("add your own framing", "loop until b''") comes from one hidden mechanism: kernel send/receive buffers. Understand this diagram once and the traps become obvious.
A running narrative: one client's connect → sendall cycle
Before the traps, walk this concrete story once. It is the scaffold every trap plugs into.
True or false — justify
TCP guarantees that one send on the client becomes exactly one recv on the server.
send only copies bytes into the kernel's send buffer; the kernel slices them into segments on its own schedule, so one send can be split across many recvs or several sends merged into one. Boundaries are yours to add — see Message framing / length-prefix protocol.UDP guarantees that one sendto becomes exactly one recvfrom (if it arrives at all).
The listening socket returned by socket() is the same object you read and write client data on.
accept returns a new per-client socket (conn) named by the full 4-tuple; the original listening socket stays passive so it can accept more clients. Read/write on conn, not the listener.A recv returning b'' just means the peer had nothing to say this time.
sendall and send are interchangeable.
send may copy only part of your buffer (returning the count actually queued); sendall loops until every byte is queued (or raises). For anything beyond a few bytes, prefer sendall.The client must always bind to a port before it can talk.
bind; on connect/sendto the OS auto-assigns an ephemeral port (a temporary high-numbered port it recycles later). Only the side others must find — the server — needs a fixed, known port. See Ports and IP addressing.Calling connect() on a UDP socket performs a handshake with the server.
connect does no network handshake — it only fixes the default peer locally so you can use send/recv instead of sendto/recvfrom. Zero packets leave the machine. Contrast TCP three-way handshake.listen(5) means the server can handle at most 5 clients total.
5 is the backlog, the waiting-room depth for connections not yet accepted. As you accept and process them, more can queue. It caps pending connections, not total or concurrent ones.Two sockets can never use the same local port number at the same time.
(local IP, local port, remote IP, remote port). Many clients hit the server's single port 9000; each conn differs in the remote IP/port, so they coexist.SO_REUSEADDR lets two servers actively serve on the same port simultaneously.
SO_REUSEADDR mainly lets you bind a port stuck in TIME_WAIT from a prior instance, so a restart doesn't fail with "Address already in use." It does not let two live listeners share the address — that's a different option (see the SO_REUSEPORT edge case below). See TIME_WAIT and SO_REUSEADDR.Spot the error
c.send("hello") where c is a connected TCP socket.
str. This raises TypeError. Fix: c.send(b"hello") or c.send("hello".encode()), and .decode() on the receiving side.UDP server code that calls s.listen(5) then conn, addr = s.accept().
listen/accept belong to TCP (connection setup). UDP is connectionless — remove them and use recvfrom/sendto, which carry the address in each call.TCP server that calls s.recv(1024) directly on the listening socket s.
recv on the socket returned by accept (the per-client conn), not the listening socket. The listener only produces connections; it carries no application data.A loop while True: data = conn.recv(1024) that processes data but never checks for b''.
recv returns b'' on every future call and the loop busy-spins. Add if not data: break to treat it as EOF.Client sends b"PING" then immediately conn.recv expecting exactly b"PONG", assuming a full reply arrives in one call.
recv and accumulate until your framing says the message is complete (length-prefix or delimiter). See Message framing / length-prefix protocol.Server does s.bind(('127.0.0.1', 9000)) but the client connects to the machine's LAN IP.
127.0.0.1 (loopback) only accepts connections from the same machine. To accept from other hosts, bind to '0.0.0.0' (all IPv4 interfaces) or the specific LAN IP.Reusing the same UDP socket to sendto client A, then blindly sendto the reply to a hard-coded address instead of the addr from recvfrom.
addr that recvfrom returned, since no connection remembers the peer for you.Why questions
Why does a TCP server need both listen and accept when a client needs neither?
listen marks the socket passive (it opens the waiting room) and accept pulls one connection out of that queue, returning a dedicated socket. The client is active — it dials with connect, which needs no queue.Why does UDP have no accept/connect/listen in the basic flow?
sendto rather than being established once by a handshake.Why does accept return a new socket instead of reusing the listener?
Why is the 1024 in recv(1024) a maximum, not a fixed size?
recv just drains whatever bytes currently sit in the kernel receive buffer, up to 1024. It might return fewer (buffer had less) — the number is a buffer cap, never a message length.Why do we prefer UDP for things like DNS lookups, gaming, and video calls?
Why does restarting a TCP server too fast give "Address already in use"?
SO_REUSEADDR tells the OS it's safe to bind over that lingering state. See TIME_WAIT and SO_REUSEADDR.Why can a single-threaded server that just loops accept→handle→accept starve clients?
accepted in the backlog queue. To serve many at once you need select / asyncio for concurrent servers or threads/processes.Edge cases
What happens if a UDP client sends "A", "B", "C" and one is lost in the network?
What does the server read if a TCP client does three sends of "A","B","C" back-to-back?
recv returning b"ABC", or a split like b"AB" then b"C" — the kernel buffered them adjacently with no boundaries. Framing is the only way to recover three messages.What happens if you recv on a TCP socket whose peer has crashed without closing cleanly?
ConnectionResetError/timeout depending on OS keepalives. Set timeouts or application-level heartbeats to detect dead peers.What does binding a server to port 0 do?
getsockname(). Handy for tests, but clients must then be told which port was chosen.Is UDP truly reliable on localhost?
What happens if the server never calls accept but clients keep connecting past the backlog?
What is the difference between SO_REUSEADDR and SO_REUSEPORT, and why does it matter?
SO_REUSEADDR mainly lets you rebind a port lingering in TIME_WAIT (restart quickly); it does not grant two live listeners the same port. SO_REUSEPORT (Linux) does let multiple sockets bind the same IP:port simultaneously, and the kernel load-balances incoming connections across them — the real tool for multi-process concurrent servers. Confusing the two is a classic interview trap.How does binding an IPv6 server socket differ from IPv4 on a dual-stack host?
'::' (all IPv6 interfaces), the analogue of IPv4's '0.0.0.0'. On many dual-stack systems a socket bound to '::' also accepts IPv4 clients via IPv4-mapped addresses (shown as ::ffff:1.2.3.4) unless the IPV6_V6ONLY option is set. So '::' may or may not cover IPv4 depending on that flag — never assume; set IPV6_V6ONLY explicitly if you care.Recall One-line self-test before you close this page
Say aloud: TCP = stream (kernel buffers, no boundaries → frame it), recv b'' = FIN/EOF; UDP = datagrams, boundaries kept, may drop/reorder; client gets an ephemeral port, rarely binds; accept births a new socket named by the 4-tuple; REUSEADDR ≠ REUSEPORT.
If any clause surprised you, revisit that trap above.
Related: UDP vs TCP tradeoffs · Message framing / length-prefix protocol · TCP three-way handshake · Ports and IP addressing · TIME_WAIT and SO_REUSEADDR · select / asyncio for concurrent servers · DNS