Introduction
An application reaches TCP and UDP through the operating system's socket interface, which first appeared in Berkeley UNIX and is now on nearly every system. A socket is an endpoint, named by a file descriptor, through which a process sends and receives data. The calls are system calls: the transport protocol itself runs in the kernel, so connect starts a three-way handshake, send copies bytes into the kernel's send buffer, and close eventually sends a FIN.
The essentials are which calls a client and a server make and in what order, which calls block, what accept returns, how TCP and UDP sockets differ, and how many sockets exist when a server has several clients. All of these follow from the transport layer underneath (Transport Layer: UDP and TCP Basics).
Prerequisites
- Transport Layer: UDP and TCP Basics: ports, connections, the handshake and release, byte streams versus datagrams.
Core concepts
The calls
| Call | Who | What it does |
|---|---|---|
socket(domain, type, protocol) | Both | Create an endpoint; SOCK_STREAM for TCP, SOCK_DGRAM for UDP. Returns a descriptor |
bind(s, addr) | Server (client optional) | Attach a local IP address and port |
listen(s, backlog) | TCP server | Mark the socket passive; the kernel queues up to backlog connections |
accept(s) | TCP server | Take a completed connection from the queue; returns a new socket for it (blocks if none) |
connect(s, addr) | TCP client | Run the three-way handshake to addr (blocks until done or refused); binds an ephemeral port if none was bound |
send / write, recv / read | Connected sockets | Move bytes to or from the kernel buffers |
sendto, recvfrom | UDP | Send to, or receive with, an explicit peer address per datagram |
shutdown(s, how) | Both | Close one direction (for example stop sending: FIN) while still receiving |
close(s) | Both | Release the descriptor; for TCP, start connection release |
Call sequence for TCP
SERVER CLIENT
s = socket(STREAM)
bind(s, port 80)
listen(s, backlog)
c = accept(s) ... blocks ... t = socket(STREAM)
<-- SYN ----------- connect(t, server:80) (blocks)
--- SYN+ACK ------>
<-- ACK ----------- connect returns
accept returns c
recv(c) <-- data ---------- send(t, request)
send(c, reply) --- data ---------> recv(t)
close(c) --- FIN ----------> recv(t) returns 0 (end of stream)
<-- FIN ----------- close(t)The handshake is done by the kernels: connect returns, and the connection waits in the listening socket's queue, even before the server calls accept. accept only hands over a connection that is already established.
Call sequence for UDP
Server: socket(DGRAM), bind(port), then a loop of recvfrom and sendto. Client: socket(DGRAM), then sendto and recvfrom. There is no listen, accept or handshake. A UDP socket may call connect, but that only records a default peer (so plain send and recv work and datagrams from other sources are filtered); nothing is sent on the network.

Two sockets on the server
The listening socket stays bound to the server port and only produces connections. Each accept returns a connected socket identified by the full 4-tuple (server IP, server port, client IP, client port). All connected sockets share the server's local port; they differ in the client's address or port. A server with open client connections therefore holds TCP sockets on that port.
Blocking
| Call | Blocks until |
|---|---|
accept | A completed connection is in the queue |
connect (TCP) | The handshake completes, or fails (RST: connection refused; no reply: timeout) |
recv / read | At least one byte is available, or the peer has closed (returns 0) |
send / write | There is room in the kernel send buffer (not until the peer receives it) |
recvfrom (UDP) | A datagram arrives |
Non-blocking mode and select or poll let one thread wait on many sockets at once.
Mathematical explanation: stream semantics
TCP is a byte stream, so the boundaries between send calls are not preserved: two sends of 3 bytes may arrive as one recv of 6 bytes, or one send of 6 bytes as two recv calls. A receiver that expects bytes must loop until it has . UDP keeps boundaries: each recvfrom returns exactly one datagram (truncated if the buffer is too small).
If send returns , the kernel accepted those bytes, not necessarily all requested bytes; loop over the remainder when . This still does not mean the peer has received them; delivery is confirmed only by TCP's ACKs, which the application does not see.
Worked examples
Example 1: order of calls
Which sequence is a correct TCP server? socket, bind, listen, accept, recv, send, close. The client side needs only socket, connect, send, recv, close; bind is optional because connect assigns an ephemeral port. Calling accept without listen, or listen on a UDP socket, fails.
Example 2: counting sockets
A web server has 3 clients connected. On the server: 1 listening socket + 3 connected sockets = 4, all with local port 80. On each client: 1 socket with its own ephemeral port. The 3 server-side connected sockets are distinguished by the clients' (IP, port) pairs.
Example 3: what recv returns
The client sends "abc" and then "def" on a TCP connection and closes it. The server's first recv(100) may return "abcdef" (or "abc"), and once all data is read, recv returns 0 bytes: the end-of-stream signal caused by the client's FIN. Over UDP, the same two sendto calls give two recvfrom results, "abc" and "def".
Example 4: no server
A client calls connect to a port where nothing is listening. The server's TCP replies to the SYN with RST, and connect fails with "connection refused". For UDP, sendto to such a port succeeds locally; the error, if any, comes back later as an ICMP port-unreachable message.
Example 5: restarting a server during TIME-WAIT
A server that closes its connections first (an active close) leaves them in TIME-WAIT on its own port. If it is restarted at once, the new listening socket's bind to that port fails on Linux with "address already in use" (EADDRINUSE) until TIME-WAIT ends; a server that sets SO_REUSEADDR on its listening socket before every bind can restart at once; on Linux the option must have been set on the earlier socket too. The transport's TIME-WAIT state is visible to the program through the socket API.
Related material. Iterative and concurrent servers
An iterative server handles one connection at a time: accept, serve, close, accept again. A concurrent server can fork a process or dispatch a thread after each accept. Fork case: the child serves the connected socket and closes its inherited listening descriptor; the parent closes its copy of the connected descriptor and returns to accept. Each process closes only the copies it does not use. Thread case: threads share one descriptor table, so the worker must not close the shared listening socket and the acceptor must not close the connection assigned to the worker; the worker closes that connection when finished (Inter-Process Communication, System Calls and OS Structure).
Important properties and observations
- A socket is a file descriptor;
read,writeandclosework on it. - Only the server binds a well-known port; clients normally let
connector the firstsendtochoose one. listendoes not block;acceptdoes.closeis not immediate release: the kernel still sends queued data and the FIN, and the side that closed first sits in TIME-WAIT.shutdowngives a half-close: stop sending (the peer reads end of stream) but keep receiving.
Common misconceptions.
- "
acceptperforms the handshake." The kernel does;acceptreturns an established connection. - "
acceptreturns the listening socket." It returns a new socket; the listening one keeps listening. - "Each
sendmatches onerecvin TCP." Only in UDP. - "UDP
connectcontacts the server." It only fixes a default peer locally. - "The client must call
bind."connectbinds automatically.
GATE examination perspective
- Server order: socket, bind, listen, accept; client: socket, connect.
- Blocking calls: accept, connect, recv, recvfrom (and send when the buffer is full).
- Sockets on a server with clients: , sharing the local port.
- TCP versus UDP calls: no listen or accept for UDP; sendto and recvfrom carry addresses.
- recv returning 0 means the peer has closed its sending side.
Summary
Sources
- A. S. Tanenbaum, D. J. Wetherall, Computer Networks, 5th ed., Pearson, 2011: Sections 6.1.3 (Berkeley Sockets) and 6.1.4 (a socket programming example).
- J. F. Kurose, K. W. Ross, Computer Networking: A Top-Down Approach, 8th ed., Pearson, 2021: Sections 2.7.1 (Socket Programming with UDP) and 2.7.2 (Socket Programming with TCP).
- B. A. Forouzan, Data Communications and Networking, 5th ed., McGraw-Hill, 2013: Chapter 25 (Introduction to Application Layer).