Client server, REST and thin clients

Peer to peer and client server, websockets, CRUD and REST with JSON, and thin versus thick clients.

A12.8Networks and the webA level30 min

Do this lesson in the simulator

At 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:

  1. The client (a browser or app) sends a request to the server, such as an HTTP GET.
  2. The server processes it: finds a file, runs a query, checks a password.
  3. 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")

Run this in the simulator

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

  1. Send 405 for methods a resource does not support, such as DELETE /led.
  2. Return each move as JSON: 200 {"id": 1, "cm": 20}.
  3. 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.