Free lesson · Computer Networking
Clients Servers And The Request Response Model
The Most Common Architecture on the Internet
The vast majority of your interactions with the internet follow a simple pattern: your device asks a question, and a remote machine answers it. You open a web page - your browser (the client) sends a request to a web server, and the server sends back the page content (the response). You tap a button in a mobile app - the app sends a request to an API server, and the server sends back data as JSON. You type a search query - your browser sends the query to Google's servers, and Google's servers send back search results.
This pattern is the client-server model, and it is the foundational architecture for nearly everything on the web. Understanding it deeply - its strengths, its limitations, and the alternatives - is essential because every networked system you design, build, or debug either follows this model or deliberately departs from it.
What Is a Client?
A client is any device or software that initiates a request to a server. Your web browser is a client. Your mobile app is a client. A backend service that calls another backend service's API is also a client - the same software can be both a client (when it makes outgoing requests) and a server (when it handles incoming requests).
Clients have several defining characteristics:
Clients initiate communication. The client always makes the first move. It decides when to send a request, what to request, and how to process the response. The server is passive until a request arrives - it sits waiting for clients to contact it.
Clients are typically many. A single web server might handle requests from millions of clients simultaneously. Scalability in client-server systems usually means scaling the server side, not the client side, because clients are already distributed by nature - each user has their own device.
Clients are often untrusted. From the server's perspective, any client could be malicious, modified, or compromised. This is why servers validate all input, authenticate all requests, and never trust client-provided data without verification. The principle of "never trust the client" is the foundation of web security.
Clients know the server's address, but servers do not know the client's address in advance. The client must know where to send the request (via a hostname resolved through DNS). The server only learns the client's address when the request arrives (from the source IP of the incoming packet). This asymmetry shapes many design decisions - the server cannot proactively push data to a specific client unless the client has first established a connection.
What Is a Server?
A server is any device or software that listens for incoming requests and sends back responses. The name "server" literally means "one who serves" - it waits for requests and fulfills them. A server can be a physical machine, a virtual machine in the cloud, a container, or a serverless function - the architectural role is the same regardless of the deployment model.
Servers have their own defining characteristics:
Servers listen on known addresses and ports. A web server listens on port 80 (HTTP) or port 443 (HTTPS). A database server listens on port 5432 (PostgreSQL) or port 3306 (MySQL). A DNS server listens on port 53. These well-known port numbers are conventions that allow clients to find the right service without additional discovery.
Servers are typically few (relative to clients). A service might have 100 servers handling requests from 10 million clients. The server-to-client ratio creates an asymmetric scaling challenge - servers must handle the aggregate load of all their clients simultaneously.
Servers are long-lived. While clients come and go (users open and close browsers, apps are launched and terminated), servers run continuously. They are designed to handle millions of requests across days, weeks, or months without restarting. This longevity introduces challenges like memory leaks, connection exhaustion, and resource accumulation that short-lived client processes do not face.
Servers must handle concurrent clients. When a server receives requests from thousands of clients simultaneously, it must process them concurrently. Different server architectures handle this differently - thread-per-request (traditional Java/C++), event-driven single-threaded (Node.js), goroutine-per-request (Go), and async/await patterns (Python asyncio, Rust). Each approach has different trade-offs in throughput, latency, memory usage, and programming complexity. The server's concurrency model is one of the most important architecture decisions affecting how many clients it can serve simultaneously.
The Request-Response Cycle
The request-response cycle is the fundamental unit of client-server communication. The client sends a request, the server processes it, and the server sends back a response. The cycle has a beginning (request sent), a middle (processing), and an end (response received). After the response, the cycle is complete - neither side has any obligation to the other until the next request.
This simple cycle drives most of the protocols you will study in this course:
- HTTP is a request-response protocol: the client sends an HTTP request, the server sends an HTTP response
- DNS is a request-response protocol: the client sends a DNS query, the server sends a DNS answer
- Database queries follow request-response: the application sends a query, the database returns a result set
- API calls follow request-response: the client sends a REST or GraphQL request, the server returns JSON
sequenceDiagram
participant C as Client<br/>(Browser)
participant S as Server<br/>(Web Server)
C->>S: HTTP Request<br/>GET /api/users
Note over S: Process request<br/>Query database<br/>Build response
S-->>C: HTTP Response<br/>200 OK + JSON data
C->>S: HTTP Request<br/>POST /api/orders
Note over S: Validate input<br/>Create order<br/>Return confirmation
S-->>C: HTTP Response<br/>201 Created + order ID
C->>S: HTTP Request<br/>GET /api/orders/123
Note over S: Fetch order<br/>Check permissions
S-->>C: HTTP Response<br/>200 OK + order details
This sequence diagram shows three request-response cycles between a client browser and a web server. First, the client sends a GET request for /api/users, the server processes it by querying the database, and responds with 200 OK and JSON data. Second, the client sends a POST request to /api/orders, the server validates input and creates the order, responding with 201 Created and the order ID. Third, the client sends a GET request for a specific order, the server fetches it and checks permissions, responding with 200 OK and order details. Each cycle is independent.
Stateless vs Stateful Communication
Interactive animation: Does the server remember you - stateless requests carry everything and scale freely, stateful ones are efficient but harder to scale.
An important property of the request-response model is whether the server remembers anything about previous requests from the same client.
Stateless Communication
In stateless communication, each request is independent. The server does not remember anything from previous requests - every request must contain all the information the server needs to process it. HTTP is fundamentally a stateless protocol.
Statelessness has significant advantages for scalability. If the server does not store any per-client state, any server in a cluster can handle any request from any client. Load balancers can distribute requests freely without worrying about routing clients to specific servers. Servers can be added or removed from the cluster without affecting clients. Crashed servers can be replaced without losing client state.
The trade-off is that stateless communication requires the client to send more data with each request. Instead of saying "give me the next page of results," the client must say "give me page 3 of results for query X with filters Y and Z, and here is my authentication token proving I am user U." This is why HTTP requests include cookies, authorization headers, and query parameters that re-establish context with every request.
Stateful Communication
In stateful communication, the server maintains information about each client across multiple requests. A TCP connection is stateful - both sides track sequence numbers, window sizes, and connection state across the lifetime of the connection. A database connection is stateful - the server tracks the authenticated user, the current transaction, and any server-side cursors.
Stateful protocols are more efficient per-interaction (less data sent per request) but harder to scale (clients must be routed to the specific server that holds their state, or state must be shared across servers via external stores like Redis).
Most real-world systems use a combination: stateless HTTP for the request-response layer, with authentication state externalized in JWTs or session stores, and stateful TCP connections underneath for reliable data delivery.
In microservices architectures, the client-server model repeats at multiple levels. A user's browser is a client to the API gateway server. The API gateway is a client to the authentication service server. The authentication service is a client to the database server. Each hop is a separate request-response cycle with its own latency, failure modes, and retry logic. A single user request might trigger five or six internal client-server interactions, each adding latency and complexity. Understanding this cascading pattern helps you reason about end-to-end latency budgets, error propagation, and the importance of timeouts and circuit breakers at every hop in the chain.
This cascading behavior also explains why distributed tracing tools like Jaeger, Zipkin, and AWS X-Ray exist - they follow a single user request through all the internal client-server hops, measuring the latency at each stage and identifying bottlenecks that would be invisible when looking at any single service in isolation.
Tip: The stateless-vs-stateful distinction is one of the most important architectural concepts in System Design. When an interviewer asks "how would you scale this system?", the first question to ask yourself is "where is the state?" Stateless components scale horizontally by adding more instances behind a load balancer. Stateful components require either sticky sessions (routing the same client to the same server), shared external state (Redis, database), or state replication across servers. Each approach has different trade-offs in complexity, latency, and consistency.
Interactive animation: One user request cascading through client-server hops - browser to API gateway to authentication service to database.
Beyond Request-Response - When the Model Does Not Fit
The request-response model works well when the client knows when it needs data and can ask for it. But several important use cases break this assumption:
Real-Time Updates
In a chat application, a stock ticker, or a collaborative editor, the server needs to push data to the client whenever something changes - without waiting for the client to ask. Three approaches handle this:
Long polling: The client sends a request and the server holds it open until new data is available, then responds. The client immediately sends another request. This is request-response disguised as push - the server controls the timing of responses. It works everywhere but is inefficient because each response requires a new HTTP request.
Server-Sent Events (SSE): The server keeps an HTTP connection open and sends data over it as events occur. The client opens a single connection and the server pushes updates through it. This is true server-push over HTTP. It is simple and works well for one-way updates (notifications, live feeds) but does not support client-to-server messages on the same connection.
WebSockets: A persistent, full-duplex connection where both client and server can send messages at any time. WebSockets upgrade from an initial HTTP connection to a different protocol that supports bidirectional messaging. This is the standard for chat applications, multiplayer games, and collaborative tools that need both client-to-server and server-to-client communication on a persistent connection.
Peer-to-Peer (P2P)
Interactive animation: Client-server sends everything through a central server, while peer-to-peer devices talk directly and must solve discovery.
In the client-server model, the server is a centralized point through which all communication flows. In peer-to-peer architecture, devices communicate directly with each other without a central server. Each device acts as both client and server simultaneously.
P2P is used in specific scenarios where centralization is impractical or disadvantageous:
- File sharing (BitTorrent): Files are distributed across many peers, and each downloader also uploads chunks to other downloaders. This distributes the bandwidth cost across all participants rather than concentrating it on a single server.
- Video calling (WebRTC): Voice and video data flows directly between participants rather than through a central server, reducing latency and server bandwidth costs.
- Blockchain networks: Nodes communicate directly with each other to propagate transactions and blocks without a central authority.
P2P has significant challenges: discovery (how do peers find each other without a central directory?), NAT traversal (how do peers behind home routers establish direct connections?), and reliability (what if the peer you need is offline?). Most "P2P" systems actually use centralized servers for discovery and coordination while using direct peer connections for data transfer.
graph TD
subgraph ClientServer["Client-Server Model"]
direction TB
CS_C1["Client 1"] --> CS_S["Server"]
CS_C2["Client 2"] --> CS_S
CS_C3["Client 3"] --> CS_S
CS_S --> CS_C1
CS_S --> CS_C2
CS_S --> CS_C3
end
subgraph P2P["Peer-to-Peer Model"]
direction TB
P1["Peer 1"] --- P2["Peer 2"]
P2 --- P3["Peer 3"]
P1 --- P3
end
style CS_S fill:#4f46e5,stroke:#4338ca,color:#fff
style CS_C1 fill:#a78bfa,stroke:#8b5cf6,color:#fff
style CS_C2 fill:#a78bfa,stroke:#8b5cf6,color:#fff
style CS_C3 fill:#a78bfa,stroke:#8b5cf6,color:#fff
style P1 fill:#7c3aed,stroke:#6d28d9,color:#fff
style P2 fill:#7c3aed,stroke:#6d28d9,color:#fff
style P3 fill:#7c3aed,stroke:#6d28d9,color:#fff
This diagram compares two architectural models side by side. In the Client-Server model, three clients all communicate through a central server - arrows go from each client to the server and from the server back to each client. In the Peer-to-Peer model, three peers connect directly to each other in a mesh - Peer 1 connects to Peer 2 and Peer 3, and Peer 2 connects to Peer 3. There is no central server in the P2P model.
Interview: The client-server model is so pervasive that interviewers often test whether you understand its constraints and alternatives:
"Design a chat application." - The base design is client-server, but real-time messaging requires WebSockets or SSE rather than pure request-response polling. The interviewer wants to see that you know when request-response is insufficient.
"How would you handle millions of concurrent connections?" - Understanding the asymmetry of client-server (many clients, few servers) leads to answers about connection pooling, event-driven servers (Node.js, Go), and the C10K/C10M problem.
"Design a video conferencing system." - Peer-to-peer (WebRTC) for small groups, centralized media servers (SFU) for large meetings. The architecture shifts based on scale because P2P mesh connections grow quadratically with participants.
What Comes Next
The next topic examines packets - the fundamental unit of data on the internet. You have seen that clients send requests and servers send responses, but how does that data actually travel across the network? The answer is packets - small, independently routed pieces of data that are assembled into complete messages at the destination.
Exercise: Starter: Open your browser's Network tab (developer tools) and load any website. Count the number of HTTP requests shown. Each one is a separate request-response cycle. How many cycles does a single page load require? Are there requests for resources you did not expect (tracking scripts, analytics, third-party fonts)?
Intermediate: Use
curlto make an HTTP request from the command line:curl -v https://httpbin.org/get. The-vflag shows the full request and response headers. Identify the request method, the request headers, the response status code, and the response body. Now trycurl -X POST -d '{"name":"test"}' -H "Content-Type: application/json" https://httpbin.org/post- how does the request differ from a GET?Advanced: Research the differences between REST, GraphQL, and gRPC as request-response paradigms. All three follow the client-server model, but they differ in how requests are structured, how data is serialized, and how efficiently they use the network. When would you choose each one? What are the trade-offs in terms of performance, flexibility, and developer experience?