Socket API
How a program uses the network: the socket as a file-descriptor-like handle, the TCP server sequence socket - bind - listen - accept - recv/send - close and the client sequence socket - connect - send/recv - close, what bind, listen and accept really do (the handshake is completed by the kernel before accept, accept returns a new socket), why a TCP recv is not matched to a send (a byte stream has no message boundaries) while a UDP datagram is delivered as a unit, ports and the address-in-use error, and the TCP states seen from outside; the C programs on the page were compiled and run, and the TCP states come from a real conversation observed with netstat.
Why it exists
An application on one computer must talk to an application on another, but the transport protocols live inside the operating system. The socket API (from the Berkeley UNIX of the early 1980s) is the interface through which programs ask the kernel for network communication: you create a socket, bind it to an address and port, connect or accept, and then send and receive almost as if you were writing to and reading from a file. It sits between the application layer and the transport layer (layering) and is implemented with system calls (system calls).
Prerequisites
You need TCP's connection and byte stream (TCP basics) and the idea of a system call (system calls).
Precise definitions
A socket is an endpoint of communication. A program creates one with socket(domain, type, protocol) and gets back a small integer, a descriptor, like a file descriptor. The type selects the transport service: SOCK_STREAM is TCP (reliable byte stream), SOCK_DGRAM is UDP (unreliable datagrams). The domain AF_INET means IPv4.
An endpoint address is the pair (IP address, port number) (struct sockaddr_in: family, 16-bit port in network byte order, 32-bit address).
The TCP server sequence (typical):
| call | what it does |
|---|---|
socket() | creates the socket |
bind(s, addr) | assigns the local address and port (port 0: let the system pick a free one) |
listen(s, backlog) | turns the socket into a listening socket; the kernel will complete handshakes for it and queue up to about backlog established connections |
accept(s) | takes the next established connection from the queue (blocking if there is none) and returns a new socket for that connection together with the client's address |
recv(), send() (or read, write) | transfer data on the new socket |
close() | closes a socket; the other side's recv then returns (end of stream) |
The TCP client sequence: socket(), connect(s, server address) (the kernel sends the SYN and returns when the handshake is complete or fails), then send()/recv(), then close(). The client normally does not call bind: the system chooses a free ephemeral port as the local port at connect.
The UDP sequence: socket(AF_INET, SOCK_DGRAM, 0); the receiver binds to a port; sendto(s, data, destination) and recvfrom(s, buf, ...). There is no listen, no accept, no connect and no handshake.
Two kinds of socket in a TCP server. The listening socket stays bound to the server's well-known port and only accepts connections. Each accepted connection has its own connected socket (a new descriptor) with the same local port and the client's address as the remote end. Connections are told apart by the four-tuple (source IP, source port, destination IP, destination port), so many clients can use one server port.
How it works
A TCP conversation, seen from the kernel
A real TCP conversation on the loopback interface: start
Computed by a script, not typed| step | server calls | client calls | TCP states observed with netstat |
|---|---|---|---|
| step 1 | |||
| step 2 | |||
| step 3 | |||
| step 4 | |||
| step 5 | |||
| step 6 |
Step 0
A Python server and client talk over 127.0.0.1; after every step the TCP states are read with the system's netstat. The states are OBSERVED (macOS netstat), not assumed.
The table was produced by a real server and client on the loopback interface, reading the states of the TCP sockets with netstat after each step. Note that after connect() the connection is already ESTABLISHED although the server has not yet called accept(): the three-way handshake is handled by the kernel, and accept() merely hands the finished connection to the program. After the client closes, the client socket waits in FIN_WAIT_2 and the server's in CLOSE_WAIT until the server closes; the socket of the side that closed first ends in TIME_WAIT.
The C program
The program below does both the server and the client side in one process (possible because the connection is completed by the kernel before accept): it creates a listening socket on a port chosen by the system, connects to it, sends two pieces of data of and bytes, accepts, and receives them with a single call asking for bytes.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
int main(void) {
/* server side: socket, bind (port 0 = let the system choose), listen */
int ls = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in a;
memset(&a, 0, sizeof a);
a.sin_family = AF_INET;
a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.sin_port = 0;
if (bind(ls, (struct sockaddr *)&a, sizeof a) < 0) return 1;
if (listen(ls, 1) < 0) return 1;
socklen_t len = sizeof a;
getsockname(ls, (struct sockaddr *)&a, &len);
/* client side: socket, connect; the handshake finishes in the kernel before accept is called */
int c = socket(AF_INET, SOCK_STREAM, 0);
int one = 1;
setsockopt(c, IPPROTO_TCP, TCP_NODELAY, &one, sizeof one);
if (connect(c, (struct sockaddr *)&a, sizeof a) < 0) return 1;
send(c, "ab", 2, 0);
send(c, "cde", 3, 0);
int s = accept(ls, NULL, NULL);
char buf[100];
ssize_t n = recv(s, buf, 5, MSG_WAITALL); /* wait until 5 bytes have arrived */
printf("two send calls (2 + 3 bytes), one recv for 5 bytes returned %zd bytes: %.*s\n", n, (int)n, buf);
struct sockaddr_in cl, peer, srv;
len = sizeof cl;
getsockname(c, (struct sockaddr *)&cl, &len);
len = sizeof peer;
getpeername(s, (struct sockaddr *)&peer, &len);
len = sizeof srv;
getsockname(s, (struct sockaddr *)&srv, &len);
printf("accepted socket's peer port == client's own port: %s\n", peer.sin_port == cl.sin_port ? "yes" : "no");
printf("accepted socket's local port == listening port: %s\n", srv.sin_port == a.sin_port ? "yes" : "no");
printf("accepted socket is a new descriptor: %s\n", s != ls ? "yes" : "no");
/* a second bind to the port that is in use fails */
int t = socket(AF_INET, SOCK_STREAM, 0);
if (bind(t, (struct sockaddr *)&a, sizeof a) < 0) printf("second bind to the same port fails: %s\n", strerror(errno));
close(t);
/* closing one side: the other side's recv returns 0 (end of the stream) */
close(c);
n = recv(s, buf, sizeof buf, 0);
printf("after the client closed, recv returned %zd\n", n);
close(s);
close(ls);
return 0;
}two send calls (2 + 3 bytes), one recv for 5 bytes returned 5 bytes: abcde
accepted socket's peer port == client's own port: yes
accepted socket's local port == listening port: yes
accepted socket is a new descriptor: yes
second bind to the same port fails: Address already in use
after the client closed, recv returned 0The first line shows that TCP has no message boundaries: the two send calls were joined in the byte stream and one recv (with MSG_WAITALL, which waits for all bytes) returned both. Without MSG_WAITALL a recv returns whatever bytes are available at that moment: in an earlier trial one call returned only the first bytes and the next call the other . A TCP program must therefore loop until it has read a complete message and must mark message ends itself (a length field, a delimiter, or closing the connection). The next lines show that the accepted socket is a new descriptor with the same local port as the listening socket, and that its remote port is the client's ephemeral port. A second bind to a port in use fails with the error "Address already in use". After the client closes, recv returns .
UDP keeps the boundaries
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
int main(void) {
int r = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in a;
memset(&a, 0, sizeof a);
a.sin_family = AF_INET;
a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.sin_port = 0;
if (bind(r, (struct sockaddr *)&a, sizeof a) < 0) return 1;
socklen_t len = sizeof a;
getsockname(r, (struct sockaddr *)&a, &len);
int s = socket(AF_INET, SOCK_DGRAM, 0); /* no connect, no handshake */
sendto(s, "ab", 2, 0, (struct sockaddr *)&a, sizeof a);
sendto(s, "cde", 3, 0, (struct sockaddr *)&a, sizeof a);
char buf[100];
for (int i = 1; i <= 2; i++) {
ssize_t n = recvfrom(r, buf, sizeof buf, 0, NULL, NULL);
printf("recvfrom %d returned %zd bytes: %.*s\n", i, n, (int)n, buf);
}
close(s);
close(r);
return 0;
}recvfrom 1 returned 2 bytes: ab
recvfrom 2 returned 3 bytes: cdeEach recvfrom returns exactly one datagram: the application sees the messages as they were sent (the two calls to sendto give two calls to recvfrom with and bytes). If the buffer is smaller than the datagram the excess is discarded. UDP does not guarantee delivery or order (on the loopback interface they are in practice never lost).
TCP (SOCK_STREAM) | UDP (SOCK_DGRAM) | |
|---|---|---|
| set-up | listen / accept / connect (handshake) | none |
| data | byte stream, no message boundaries | datagrams with boundaries |
| reliability | reliable, ordered | none |
| sender and receiver | one connection per pair of sockets | one socket can talk to many peers |
| receiver call | recv | recvfrom |
Worked examples
EX 1Order of the calls
EasyTCP server: socket, bind, listen, accept, recv/send, close. TCP client: socket, connect, send/recv, close. A UDP server: socket, bind, recvfrom/sendto.
EX 2How many sockets does the server have?
MediumA server listening on port with clients connected has sockets: one listening socket and three connected sockets. All connected sockets have local port ; they differ in the remote address and port.
EX 3Reading until a message is complete
MediumA client sends a -byte message with two send calls. The server's first recv may return bytes, the next (or at once). The server must call recv repeatedly, adding up the counts, until bytes (known from a length header) have arrived. For UDP a message of bytes sent with one sendto is returned by one recvfrom.
EX 4Typical questions
GATE level(a) "Which call blocks until a client connects?" accept. (b) "Which call does a UDP server not need?" listen and accept. (c) "What does accept return?" A new socket descriptor for the connection. (d) "Does the client have to call bind?" No, the system assigns an ephemeral port. (e) "What does recv return when the peer has closed?" . (f) "Which socket type for a reliable stream?" SOCK_STREAM. (g) "How can many clients be served on one port?" Each connection has its own socket, identified by the four-tuple.
Cheat box
Cheat box
- TCP server: socket, bind, listen, accept (new socket), recv/send, close. Client: socket, connect, send/recv, close.
- UDP: socket, bind, recvfrom/sendto; no connection.
acceptreturns a NEW socket; the listening socket stays; same local port, different peer.- TCP is a byte stream (loop on recv); UDP keeps datagram boundaries.
- The kernel completes the handshake before accept;
recvreturning means the peer closed.
Traps and GATE patterns
Connections
Across subjects
Within this subject
The socket calls are system calls into the kernel's TCP and UDP code (system calls); what happens inside connect and close is the handshake and teardown of TCP basics. The application protocols that use sockets are HTTP over TCP and DNS over UDP.
PYQs
7 previous year questions from your book. I re-solved each one myself (1 by running or simulating, 6 by reasoning only); the book's key is shown next to my answer. All PYQs
A TCP server application is programmed to listen on port number P on host S. A TCP client is connected to the TCP server over the network. Consider that while the TCP connection was active, the server machine S crashed and rebooted. Assume that the client does not use the TCP keepalive timer. Which of the following behaviors is/are possible?
- (a)If the client sends a packet after the server reboot, it will receive a FIN segment.
- (b)If the client was waiting to receive a packet, it may wait indefinitely.
- (c)The TCP server application on S can listen on P after reboot.
- (d)If the client sends a packet after the server reboot, it will receive a RST segment.
Consider socket API on a Linux machine that supports connected UDP sockets. A connected UDP socket is a UDP socket on which connect function has already been called. Which of the following statements is/are CORRECT? I. A connected UDP socket can be used to communicate with multiple peers simultaneously. II. A process can successfully call connect function again for an already connected UDP socket.
- (a)I only
- (b)II only
- (c)Both I and II
- (d)Neither I nor II
Identify the correct order in which a server process must invoke the function calls accept, bind, listen, and recv according to UNIX socket API.
- (a)listen, accept, bind, recv
- (b)bind, listen, accept, recv
- (c)bind, accept, listen, recv
- (d)accept, listen, bind, recv COMPUTER NETWORK
Which one of the following socket API functions converts an unconnected active TCP socket into a passive socket?
- (a)connect
- (b)bind
- (c)listen
- (d)accept
A client process P needs to make a TCP connection to a server process S. Consider the following situation: the server process S executes a socket(), a bind() and a listen() system call in that order, following which it is preempted. Subsequently, the client process P executes a socket() system call followed by connect() system call to connect to the server process S. The server process has not executed any accept() system call. Which one of the following events could take place?
- (a)connect() system call returns successfully
- (b)connect() system call blocks
- (c)connect() system call returns an error
- (d)connect() system call results in a core dump
Which of the following system calls results in the sending of SYN packets?
- (a)socket
- (b)bind
- (c)listen
- (d)connect
Which one of the following is not a client-server application?
- (a)Internet chat
- (b)Web browsing
- (c)E-mail
- (d)Ping
Self-check
Question 1
A server has accepted connections and is still listening. How many sockets does it have?
Show answerHide
: the listening socket plus five connected sockets.
Question 2
Why must a TCP receiver loop when reading a message?
Show answerHide
TCP is a byte stream without message boundaries; one recv can return only part of the data sent by one send (or the data of several sends).
Question 3
Which two calls are used by the UDP receiver instead of listen and accept?
Show answerHide
None are needed; it calls bind and then recvfrom.
Revision card
One-screen revision
- TCP server: socket, bind, listen, accept, recv/send, close. Client: socket, connect, send/recv.
- accept returns a new socket; handshake done by the kernel.
- TCP: stream, loop on recv; UDP: datagrams, recvfrom, no connection.
References
- ·Kurose, Ross, Computer Networking: A Top-Down Approach (8th edition), Chapter 2, Section 2.7 (Socket programming: creating network applications)
- ·Stevens, Fenner, Rudoff, UNIX Network Programming, Volume 1 (3rd edition), Chapters 4 and 8 (Elementary TCP and UDP sockets)
- ·Tanenbaum, Wetherall, Computer Networks (5th edition), Chapter 6, Section 6.1.3 (Berkeley sockets)
Verification
Both C programs on the page (a TCP exchange and a UDP exchange over the loopback interface, using port 0 so that the system chooses a free port) are compiled with -Wall -Wextra -Werror and run by npm run verify:c; the TCP program printed identical output in 40 consecutive runs on macOS (clang). The table of TCP states is OBSERVED: a Python server and client on 127.0.0.1 were examined with netstat after every step (scripts/gen-cn5-traces.py); the state names are those of this operating system and the sequence is the one of the textbooks. OBSERVATION worth knowing: in a first version of the TCP program the receiver called recv once without MSG_WAITALL and in 1 run of 5 got only 2 of the 5 bytes (and the other 3 on the next call): a single recv may return less than the sender sent, because the byte stream has no boundaries. The page therefore uses MSG_WAITALL for a deterministic demonstration. PORTABILITY: the programs use POSIX sockets (macOS, Linux); Windows uses Winsock with small differences. QUOTED, not verified here: the textbook statements about the backlog (the number of queued connections is system dependent), SO_REUSEADDR, blocking against non-blocking calls, select / poll, and the claim that the data of one UDP datagram cannot be split across recvfrom calls (the program shows datagram boundaries kept on loopback; a datagram larger than the buffer is truncated). Textbook chapter references are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (CRC remainders, binomial tail, checksums); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose). No defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)