25-Comp-B10 Distributed Systems · December 2019
Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)
U: x = write(i,55); write(j,66);” — assigning the result of a write to a variable is not meaningful pseudocode. Since the assigned name x is never subsequently read by U, this does not change the analysis below; U is treated as the two-operation transaction write(i,55); write(j,66), and T as x = read(i); write(j,44), exactly as printed.
Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.
(a) Sockets and object serialization (Python). A socket is the endpoint abstraction each side of a connection uses to send/receive bytes; a server socket binds to a well-known address/port and listens for incoming connections, while a client socket initiates a connection to that address. Serialization (marshalling) converts an in-memory object into a byte stream that can be sent over the socket and reconstructed identically at the other end.
# -- server.py --
import socket, json
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("0.0.0.0", 5000)) # server socket: bind to a known port
srv.listen(5) # await incoming connections
conn, addr = srv.accept() # blocks until a client connects
data = conn.recv(4096)
request = json.loads(data.decode()) # deserialize the incoming object
response = {"status": "ok", "echo": request}
conn.sendall(json.dumps(response).encode()) # serialize the reply object
conn.close()
# -- client.py --
import socket, json
cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
cli.connect(("server.example.com", 5000)) # client socket: connect out
request = {"op": "add", "a": 2, "b": 3}
cli.sendall(json.dumps(request).encode()) # serialize the request object
reply = json.loads(cli.recv(4096).decode()) # deserialize the reply object
print(reply)
cli.close()
Here JSON is used as the wire (external data) representation, playing the same role Java's Serializable/ObjectOutputStream or a binary format (e.g. Java RMI's own serialization, or protocol buffers) would: it converts a language-level object graph into a self-describing byte sequence both ends agree on, independent of each side's internal memory layout — the same requirement that makes cross-platform/cross-language communication possible in the first place.
(b) UDP vs. TCP, and UDP's applications. TCP is connection-oriented: it establishes a connection via a three-way handshake, then guarantees reliable, in-order, duplicate-free delivery via acknowledgements and retransmission, with flow and congestion control — at the cost of handshake latency and per-connection state on both ends. UDP is connectionless: a datagram is sent with no setup, no delivery/order guarantee, no flow control and a much smaller header, so it has lower latency and overhead but the application must handle any loss, duplication or reordering itself if it needs to. Applications using UDP: DNS lookups (small, latency-sensitive, resolver-managed retransmission makes TCP's setup cost not worth paying); real-time media/VoIP and video conferencing (a late or retransmitted packet is worse than a dropped one, since the audio/video has already moved past that point in time); online multiplayer gaming (frequent small state updates where the newest update supersedes a late old one); DHCP (address assignment before the host even has a usable network configuration); and streaming protocols built to tolerate loss directly rather than pay TCP's retransmission delay.
(c) RPC architecture. Remote Procedure Call lets a client invoke a procedure that actually executes on a remote server, while looking to the calling code like an ordinary local call. The architecture separates this illusion from the real network transfer via generated stub code on each side.
The main components are therefore: the client and server stubs (auto-generated from an interface definition, responsible for marshalling/unmarshalling and presenting a local-call-shaped interface on each side), the communication module on each host (implements the request-reply protocol over the underlying transport, handling retransmission/at-least-once or at-most-once semantics), and on the server side a dispatcher that receives an incoming request and routes it to the correct server stub/procedure implementation.
(d) Time for a single-threaded client to generate and return from two RPC requests.
Given. Client argument-computation time 6 ms per request; server processing time 10 ms per request; OS send/receive processing 0.5 ms per operation; network transmission 3 ms per message; marshalling/unmarshalling 0.25 ms per message; client is single-threaded (requests are strictly sequential); context-switching time ignored.
| Quantity | Value |
|---|---|
| Client compute time (per request) | 6 ms |
| Server processing time (per request) | 10 ms |
| OS send/receive processing (per operation) | 0.5 ms |
| Network transmission (per message) | 3 ms |
| Marshalling/unmarshalling (per message) | 0.25 ms |
| Number of requests | 2 (sequential, single-threaded) |
Find. The total elapsed time for the client to generate and return from two RPC requests.
Approach. Build up the time for ONE complete request-reply round trip from its twelve constituent pieces — client compute, then (marshal, OS send, network, OS receive, unmarshal) for the request, server processing, then the same five-piece chain in reverse for the reply — then multiply by two, since a single-threaded client cannot start computing the next request's arguments until the previous round trip has fully returned.
| Quantity | Value |
|---|---|
| Time for one request-reply round trip | 25.00 ms |
| Time for two sequential requests (single-threaded client) | 50.00 ms |