NivaarExam PrepOfficial exam papers ↗

25-Comp-B10 Distributed Systems · December 2019

Question 6 of 6: Client-Server Systems and Inter-Process Communication

Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)

Notes on this paper

17-Comp-B10 Distributed Systems — National Examinations, December 2019. 3 hours, closed book, Casio/Sharp approved calculator only. Candidates were instructed to answer any five of the six questions, all carrying equal weight (20 marks each) and mostly requiring essay-format answers, with only the first five as they appear in the answer book marked; all six are answered below as a complete study resource.

Reference texts: Coulouris, Dollimore, Kindberg & Blair, Distributed Systems: Concepts and Design (5th ed.) — system models, client-server architecture and mobile/ubiquitous computing (ch. 1–2, 19), interprocess communication and remote invocation, RPC (ch. 4–5), operating system support and middleware (ch. 6–8), security (ch. 11), distributed file systems (ch. 12), transactions and concurrency control, distributed transactions and two-phase commit (ch. 13–14).

Check — Question 5(a) as printed. The paper prints transaction U as “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 6: Client-Server Systems and Inter-Process Communication

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.

RPC architecture: components and message flow Client program Client stub Comm. module Server procedure Server stub Dispatcher + comm. module local call marshalled request request message (network) local call reply message (network) Client host Server host
Fig. Q6(c) — RPC architecture. The client program calls what looks like a local procedure on its client stub, which marshals the arguments into a request message and hands it to the communication module for network transmission. On the server host the communication module and dispatcher receive the message, select the correct server stub, which unmarshals the arguments and calls the real server procedure; the return value is marshalled back through the same path as a reply message.

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.

Given data — Q6(d)
QuantityValue
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 requests2 (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.

  1. Client-side request leg. Compute arguments, marshal the request, then hand it to the OS to send: $$T_{req,client} = 6 + 0.25 + 0.5 = 6.75\ \text{ms}.$$
  2. Network and server-side request leg. The request message crosses the network, the server's OS receives it, and the server unmarshals it: $$T_{req,net+srv} = 3 + 0.5 + 0.25 = 3.75\ \text{ms}.$$
  3. Server processing. $$T_{proc} = 10\ \text{ms}.$$
  4. Server-side and network reply leg. The server marshals the reply, its OS sends it, and it crosses the network: $$T_{reply,srv+net} = 0.25 + 0.5 + 3 = 3.75\ \text{ms}.$$
  5. Client-side reply leg. The client's OS receives the reply and unmarshals it: $$T_{reply,client} = 0.5 + 0.25 = 0.75\ \text{ms}.$$
  6. One round trip, and two sequential requests. Summing steps 1–5: $$T_{RT} = 6.75+3.75+10+3.75+0.75 = 25.00\ \text{ms}.$$ The client is single-threaded, so it cannot begin computing the second request's arguments until the first round trip has fully returned; the two requests are therefore strictly sequential: $$\boxed{T_{2\ requests} = 2\times T_{RT} = 2\times25.00 = 50.00\ \text{ms}}$$
Final Results — Q6(d)
QuantityValue
Time for one request-reply round trip25.00 ms
Time for two sequential requests (single-threaded client)50.00 ms
Check — marshal/unmarshal accounting. "Marshalling or unmarshalling takes 0.25 ms per message" is read as applying once at each end of each of the two messages (request, reply) — i.e. marshal-request, unmarshal-request, marshal-reply, unmarshal-reply, four occurrences of 0.25 ms per round trip — consistent with how the same OS-processing and network-time figures are also charged once per send/receive and once per message respectively.
Back to the paper →