Client server, REST and thin clients
Peer to peer and client server, websockets, CRUD and REST with JSON, and thin versus thick clients.
Do this lesson in the simulatorAt GCSE (F10.1) client-server and peer-to-peer were two ways to organise a network. At A level they are the start of a bigger idea: how programs on different computers are designed to work together. This lesson looks at the request-response model behind the web, the websocket protocol that breaks out of it, how CRUD operations map onto HTTP and SQL in a RESTful API, how the data is formatted as JSON or XML, and how much work to leave on the client: thin or thick.
Peer to peer and client server
In a peer-to-peer network every computer (peer) has equal status. Each can act as both a client and a server, sharing its own files and resources directly with others. There is no central server to buy or manage, and no single point of failure, but there is no central control of security or backups, and a resource is only available while the peer holding it is switched on. File-sharing systems that spread a large download across many peers use this model.
In a client-server network, servers provide services (files, email, web pages, databases, printing) and clients request them. The server is managed centrally, so security, backups and updates are handled in one place, but the server costs money, needs expertise to run, and if it fails every client loses the service.
The client server model
On the web, the client-server model works by request and response:
- The client (a browser or app) sends a request to the server, such as an HTTP
GET. - The server processes it: finds a file, runs a query, checks a password.
- The server sends back a response: a status code and the data.
The server never speaks first. A client that wants to know whether anything has changed must keep asking, which wastes bandwidth and adds delay. HTTP is also stateless: each request stands alone, and the server keeps no memory of the last one unless something like a cookie is sent with each request.
Websockets
The websocket protocol fixes this for applications that need live updates: chat, multiplayer games, share prices, or a dashboard showing a robot's sensors. The client opens an ordinary HTTP connection and asks the server to upgrade it. From then on the connection stays open as a persistent, full-duplex channel over a single TCP connection: either side can send a message at any time, with very little header overhead, and the server can push new data to the client as soon as it has it.
CRUD and REST
Almost every application that stores data does four things with it, known as CRUD. On the web, each maps onto an HTTP method, and on the server, onto an SQL statement:
| CRUD | HTTP method | SQL | Example |
|---|---|---|---|
| Create | POST |
INSERT |
POST /moves adds a new move |
| Retrieve (read) | GET |
SELECT |
GET /moves/1 fetches move 1 |
| Update | PUT |
UPDATE |
PUT /led changes the LED colour |
| Delete | DELETE |
DELETE |
DELETE /moves/1 removes move 1 |
REST (representational state transfer) is a way of designing a web API around this. Everything the server offers is a resource identified by a URL (/moves/1), the standard HTTP methods say what to do to it, each request carries everything the server needs (it is stateless), and the server replies with a representation of the resource, usually as JSON or XML, plus an HTTP status code:
| Code | Meaning |
|---|---|
200 OK |
the request worked; the resource is in the response |
201 Created |
a new resource was made; the response says where |
204 No Content |
the request worked and there is nothing to send back, as after a delete |
404 Not Found |
there is no such resource |
405 Method Not Allowed |
the resource exists but does not support that method |
A typical CRUD application in a browser works like this: JavaScript running in the page sends an HTTP request to the server's REST API; the server turns it into an SQL query on its database; the result is sent back as JSON; and the JavaScript updates the page with it, without reloading.
JSON and XML
Both are text formats for sending structured data. Here is the same move in each:
JSON: {"id": 1, "direction": "forward", "cm": 20}
XML: <move>
<id>1</id>
<direction>forward</direction>
<cm>20</cm>
</move>
JSON (JavaScript object notation) is usually preferred for web APIs because it is easier for a human to read, more compact (no closing tags), easier to create, and easier and quicker for a computer to parse, since it maps directly onto the objects, arrays and values that JavaScript and Python already use. XML is more verbose, but it supports attributes, comments and schemas that can check a document's structure, which is why it is still used where strict validation matters.
import json
move = {"id": 1, "direction": "forward", "cm": 20}
text = json.dumps(move) # a Python dictionary to JSON text, ready to send
print(text, len(text), "characters")
back = json.loads(text) # JSON text back into a dictionary
print(back["direction"], back["cm"])
xml = f"<move><id>{move['id']}</id><direction>{move['direction']}</direction><cm>{move['cm']}</cm></move>"
print(xml, len(xml), "characters")
Thin and thick clients
A thin client does very little itself: the server does the processing and stores the data, and the client mostly sends input and displays the results. A web app that runs entirely on a company's servers, used through a cheap Chromebook, is a thin-client arrangement. A thick (fat) client does most of the processing and storage locally, and uses the server only for some shared data or services.
| Thin client | Thick client | |
|---|---|---|
| Client hardware | cheap, low power | must be powerful enough to do the work |
| Management | software and data in one place: easy to update, back up and secure | software must be installed and updated on every machine |
| Network | needs a fast, reliable connection; useless without one | can keep working offline or on a poor connection |
| Server | carries the load of every client, so it must be powerful; a failure stops everyone | lighter load on the server |
| Security | data stays on the server, so a lost device leaks nothing | data on each device can be lost or stolen |
The BugBot simulator leans towards the thick end: your Python runs in your own browser, not on a server.
Task: the robot as a REST server
Turn the robot into a tiny REST server. The Client on this mat sends six requests over the radio, one a second. Each is words separated by single spaces: a method, a path, and for some an extra value.
The robot has two kinds of resource: /led, its LED colour (starting as off), and /moves/<id>, the moves it has made, each stored as its distance in whole centimetres with ids counting from 1. Handle each request like this:
| Request | What to do | Send |
|---|---|---|
GET /led |
nothing | 200 <colour> |
PUT /led <colour> |
set the LED to that colour name | 200 <colour> |
POST /moves <cm> |
drive forward <cm> cm at speed 50, store it under the next id |
201 /moves/<id> |
GET /moves/<id> |
nothing | 200 <cm>, or 404 if there is no such move |
DELETE /moves/<id> |
remove the move | 204, or 404 if there is no such move |
| any other path | nothing | 404 |
After sending each response, print <request> -> <response>. Stop after the sixth request, so the output is 6 lines. Check messages() every 0.1 s, and build responses from your stored resources rather than typing them.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
colour = "off"
moves = {}
next_id = 1
wait(6)
for sender, text in messages():
print(text)
Challenges
- Send
405for methods a resource does not support, such asDELETE /led. - Return each move as JSON:
200 {"id": 1, "cm": 20}. - Which of these would you build as a thin client and which as a thick one: a school's exam results system, a video editor, a supermarket till? Give a reason for each.