Lesson 02 / 12
The Client–Server Model
The distinction between the role that initiates a connection and the one that waits, the concept of a port, and a comparison with the peer-to-peer model.
Contents
The previous lesson established how packets are carried across the network and left open the question of who initiates communication. The network carries both hosts the same way; the difference between them is not the network’s property but that of the programs running on them.
This lesson defines that difference: why does the program on the machine holding the
example.test page wait continuously, while the program on your machine starts talking
whenever it wants? By the end of the lesson, this course’s example server will be up and
running.
The Criterion That Separates the Roles
A server is a program that waits on a port and responds to incoming requests. A client is a program that initiates the connection and sends the request.
The only criterion for the distinction is who initiates the connection. A common misunderstanding is treating the server as the “powerful machine” and the client as the “weak machine.” This is not true: the role belongs to the program’s behavior, not the hardware. The same machine can be a server for one program and a client for another; servers are often clients of other servers.
The roles produce two asymmetries.
Discoverability. The client needs to be able to find the server; the server does not need to know the client. This is why the server’s address must be stable and known in advance. The client’s address can be temporary and can change from connection to connection.
Availability. The server must be listening at every moment a request might arrive. The client, in contrast, runs only at the moment it wants to speak. The server’s requirement to run continuously is the source of the hosting problem covered in later lessons.
The Port
An address specifies a machine. But more than one server program can run on a machine at the same time. Telling which program an incoming packet belongs to requires a second number: the port.
The address points to the building, the port to the apartment inside it. A connection is defined by four values: source address, source port, destination address, destination port. As long as that four-tuple is unique, countless connections can coexist between the same two machines.
Servers listen on well-known numbers; like the client’s address, the client’s port is
usually a temporary number assigned by the operating system at that moment. Unencrypted
web traffic is listened for on port 80, encrypted traffic on port 443. These numbers are
assigned by a registration authority and stay invisible because they are not written in
the address bar: typing http://example.test/ assumes 80, and https://example.test/
assumes 443.
If a different number is to be used, it is written explicitly in the address. This course’s examples use port 8080; choosing an unprivileged number allows the server to run without administrator rights.
Writing a Server
The most certain way to explain what a server does is to run one. The program below listens on a port, accepts a single connection, prints the bytes it receives, and sends back a fixed response.
import socket BODY = b"<!doctype html>\n<html lang=\"en\"><body><h1>Example Page</h1></body></html>\n" RESPONSE = ( b"HTTP/1.1 200 OK\r\n" b"Content-Type: text/html; charset=utf-8\r\n" b"Content-Length: " + str(len(BODY)).encode() + b"\r\n" b"Connection: close\r\n" b"\r\n" ) + BODY def run_server(port: int = 8080) -> None: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener: listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listener.bind(("127.0.0.1", port)) listener.listen(1) client, address = listener.accept() with client: request = client.recv(4096) print("--- incoming request ---") print(request.decode("utf-8", "replace"), end="") print("--- end of request ---") client.sendall(RESPONSE) run_server()
The program executes four steps in order, and these four steps are the same in every server:
bind— an address and port are claimed.listen— the program becomes ready to accept incoming connections.accept— the program waits until a client connects. Execution stops on this line.recv/sendall— the request is read, the response is written.
The third step is the essence of the role. The server waits without knowing when or who will connect.
The socket in the socket calls is the connection’s handle on the program’s side;
the operating system reads and writes data through this handle.
Running a Client
While the server runs, a client is run from another terminal. curl is used here as the
client. The --resolve option manually declares which address the name example.test
corresponds to; name resolution is the subject of the next topic, and this option stands
in for it for now. The -A option fixes the text the client identifies itself with.
curl -sv --resolve example.test:8080:127.0.0.1 -A "example-client" http://example.test:8080/
The exchange as seen on the client side (> shows outgoing, < incoming lines):
* Added example.test:8080:127.0.0.1 to DNS cache
* Hostname example.test was found in DNS cache
* Trying 127.0.0.1:8080...
* Connected to example.test (127.0.0.1) port 8080
> GET / HTTP/1.1
> Host: example.test:8080
> User-Agent: example-client
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 74
< Connection: close
<
{ [74 bytes data]
* Closing connection
<!doctype html>
<html lang="en"><body><h1>Example Page</h1></body></html>
Lines starting with * are curl’s own commentary, not part of the exchange.
On the server side, the bytes the client sent appeared:
--- incoming request --- GET / HTTP/1.1 Host: example.test:8080 User-Agent: example-client Accept: */* --- end of request ---
The two outputs are two ends of the same exchange. The point to notice is that what the server receives is not a structured object but plain text. Both the request and the response consist of bytes; the only thing that sets the rules is that both sides follow the same format. That format is HTTP, and it is the subject of the third topic.
The address 127.0.0.1 refers to the machine itself; packets sent to this address never
leave the machine. This makes it possible to examine the whole client–server exchange
without needing a real network.
The Peer-to-Peer Model
Client–server is not the only option. In the peer-to-peer model, every host acts as both client and server: it both receives resources and offers them to others. There is no privileged machine sitting at the center.
The two models are compared along the same criteria.
| Criterion | Client–server | Peer-to-peer |
|---|---|---|
| Location of the resource | On a known host | Spread across participating hosts |
| Discovery method | The server’s address is known | The list of peers is discovered |
| As demand grows | Server load grows | Capacity grows too |
| Single point of failure | The server | None |
| Control and consistency | Single location, easy | Distributed, hard |
| Availability | Depends on the server | Continues if enough peers remain |
The peer-to-peer model’s clearest advantage is scaling: every new host that wants the resource is, at the same time, new capacity able to offer that resource. As demand grows, so does supply.
It has two difficulties in return. The first is discoverability: without a stable address, determining who holds a resource is a separate problem. The second is control: guaranteeing a resource’s accuracy, currency, and authorization from a single point is hard in a distributed structure.
The web is built on the client–server model. The reason is that these two difficulties directly conflict with the web’s basic requirements: a domain name is expected to show specific content, and who has authority over that content must be clear. A stable name and stable responsibility require a stable server.
This distinction is not a sharp boundary. Many machines can share the load behind a single server; a direct connection can be established between two browsers. The roles are determined by a specific exchange, not by the machines.
Summary
- The criterion separating client from server is not hardware but who initiates the connection.
- The server listens continuously at a stable address; the client’s address can be temporary.
- The port distinguishes multiple server programs on the same machine; a connection is defined by the four-tuple of source and destination addresses and ports.
- A server program follows the
bind,listen,accept, and read/write steps; waiting at theacceptcall is the essence of the role. - The request and response are not structured objects but byte sequences that both sides interpret the same way.
- The peer-to-peer model gains capacity as demand grows, but the client–server model was chosen for the web, which needs a stable address and central control.
Next Step
It was established that the server has to listen at a stable address; but this server is
not on the same local network as your machine. The packets in between pass through many
independently operated networks that share no common owner. The next lesson covers how
this structure, belonging to no single organization, holds together, and who determines
the path to the example.test server.
To keep your progress and take notes, Log in
My notes
Log in to take notes.