Paper 2 Computer Networks Answers

These answers correspond to Paper 2 Computer Networks Drills.

Verification note: every Python code block in this answer file has been executed locally.

Answer 1: Encode Decode

Model answer:

def round_trip(text):
    data = text.encode("utf-8")
    decoded = data.decode("utf-8")
    return decoded
 
 
print(round_trip("hello"))
print(round_trip("café"))

Expected output:

hello
café

Mark points:

  • uses .encode("utf-8") on the input string;
  • stores or passes the encoded byte sequence correctly;
  • uses .decode("utf-8") on the bytes;
  • returns the decoded string and produces the expected output for both ASCII and non-ASCII text.

Common weak answer:

  • returning the bytes object instead of decoding it back into a string.

Answer 2: Simple Client

Model answer with a local one-shot test server:

import socket
import threading
 
 
def start_ack_server():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(("127.0.0.1", 0))
    server.listen(1)
    port = server.getsockname()[1]
 
    def serve_once():
        conn, _ = server.accept()
        data = b""
        while b"\n" not in data:
            chunk = conn.recv(1024)
            if chunk == b"":
                break
            data += chunk
        message = data.decode("utf-8").strip()
        conn.sendall(("ACK:" + message + "\n").encode("utf-8"))
        conn.close()
        server.close()
 
    thread = threading.Thread(target=serve_once)
    thread.start()
    return port, thread
 
 
def send_message(host, port, message):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((host, port))
    client.sendall((message + "\n").encode("utf-8"))
    data = b""
    while b"\n" not in data:
        chunk = client.recv(1024)
        if chunk == b"":
            break
        data += chunk
    client.close()
    return data.decode("utf-8").strip()
 
 
port, thread = start_ack_server()
print(send_message("127.0.0.1", port, "HELLO"))
thread.join()

Expected output:

ACK:HELLO

Mark points:

  • creates a TCP socket using socket.AF_INET and socket.SOCK_STREAM, or an equivalent TCP socket call;
  • connects to the given (host, port) tuple;
  • encodes the outgoing message with a newline delimiter before sending;
  • uses sendall or otherwise ensures the full message is sent;
  • receives until the newline-delimited server reply is complete;
  • decodes the reply from bytes to string;
  • closes the client socket;
  • returns the decoded reply shown by the test output.

Common weak answer:

  • sending a Python string directly with sendall(message). Socket send operations require bytes.

Answer 3: Simple Server

Model answer with a local client test:

import socket
import threading
import time
 
 
def run_once_server(host, port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((host, port))
    server.listen(1)
 
    conn, _ = server.accept()
    data = b""
    while b"\n" not in data:
        chunk = conn.recv(1024)
        if chunk == b"":
            break
        data += chunk
    message = data.decode("utf-8").strip()
    conn.sendall(("RECEIVED:" + message + "\n").encode("utf-8"))
    conn.close()
    server.close()
 
 
temporary = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
temporary.bind(("127.0.0.1", 0))
port = temporary.getsockname()[1]
temporary.close()
 
thread = threading.Thread(target=run_once_server, args=("127.0.0.1", port))
thread.start()
time.sleep(0.05)
 
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", port))
client.sendall("PING\n".encode("utf-8"))
data = b""
while b"\n" not in data:
    chunk = client.recv(1024)
    if chunk == b"":
        break
    data += chunk
print(data.decode("utf-8").strip())
client.close()
thread.join()

Expected output:

RECEIVED:PING

Mark points:

  • creates a TCP server socket;
  • binds the socket to a host and port;
  • listens for incoming connections;
  • accepts one client connection;
  • receives bytes until the newline delimiter is found;
  • decodes the complete request line;
  • forms the response "RECEIVED:" + message;
  • encodes and sends the response with a newline delimiter;
  • closes both the connection socket and the server socket;
  • test client sends "PING\n" and the output matches RECEIVED:PING.

Common weak answer:

  • calling recv on the server socket instead of the accepted connection socket.

Answer 4: Echo Server Logic

Model answer:

def echo_messages(messages):
    replies = []
    for message in messages:
        if message == "QUIT":
            break
        replies.append("ECHO:" + message)
    return replies
 
 
print(echo_messages(["red", "blue", "QUIT", "green"]))

Expected output:

['ECHO:red', 'ECHO:blue']

Mark points:

  • initializes a list of replies;
  • loops through the input messages in order;
  • checks for "QUIT";
  • stops immediately when "QUIT" is reached;
  • does not echo "QUIT";
  • does not process later messages after "QUIT";
  • prefixes ordinary messages with "ECHO:";
  • appends each echo reply to the list;
  • returns the reply list;
  • preserves the original message order before "QUIT";
  • produces exactly two replies for the given test;
  • matches the expected output.

Common weak answer:

  • filtering out "QUIT" but still processing "green". The protocol says "QUIT" ends the session.

Answer 5: Port Config

Model answer:

HOST = "127.0.0.1"
PORT = 5000
 
 
def endpoint():
    return (HOST, PORT)
 
 
print(endpoint())

Expected output:

('127.0.0.1', 5000)

Mark points:

  • defines HOST with the given loopback address;
  • defines PORT as integer 5000;
  • returns the two values as a tuple in the correct order;
  • test output matches the expected endpoint.

Common weak answer:

  • storing the port as "5000" when socket calls expect an integer port number.

Answer 6: Protocol Message

Model answer:

def parse_message(message):
    parts = message.split(":")
    if len(parts) != 2:
        return None
 
    command, id_text = parts
    if not id_text.isdigit():
        return None
 
    return (command, int(id_text))
 
 
print(parse_message("GET:42"))
print(parse_message("BAD"))

Expected output:

('GET', 42)
None

Mark points:

  • splits the message using ":";
  • rejects messages that do not have exactly two parts;
  • separates the command from the ID text;
  • validates that the ID is numeric;
  • converts the ID to an integer;
  • returns None for the malformed "BAD" input.

Common weak answer:

  • assuming message.split(":")[1] always exists. A malformed message should not crash the program.

Answer 7: DNS Lookup

Model answer:

dns = {
    "school.test": "192.0.2.10",
    "library.test": "192.0.2.20"
}
 
 
def lookup(domain):
    if domain in dns:
        return dns[domain]
    return "NOT FOUND"
 
 
print(lookup("school.test"))
print(lookup("missing.test"))

Expected output:

192.0.2.10
NOT FOUND

Mark points:

  • represents domain names as dictionary keys;
  • represents IP addresses as dictionary values;
  • checks or retrieves the requested domain;
  • returns the correct IP for "school.test";
  • returns "NOT FOUND" for a missing domain.

Common weak answer:

  • looping through values rather than looking up by domain name. DNS resolution is naturally modelled as name-to-address lookup.

Answer 8: Packet Reassembly

Model answer:

fragments = [(3, "ORL"), (1, "HEL"), (4, "D"), (2, "LOW")]
 
 
def reassemble(fragments):
    ordered = sorted(fragments)
    payloads = []
    for _, payload in ordered:
        payloads.append(payload)
    return "".join(payloads)
 
 
print(reassemble(fragments))

Expected output:

HELLOWORLD

Mark points:

  • treats each fragment as a sequence number plus payload;
  • sorts fragments by sequence number;
  • extracts payloads after sorting;
  • joins payload strings without extra spaces;
  • reconstructs HELLOWORLD;
  • does not rely on the arrival order;
  • handles all four fragments;
  • prints the expected output.

Common weak answer:

  • joining the fragments in their received order, which would produce ORLHELDLOW.

Answer 9: Client Test

Model answer:

def expected_response(message):
    if message == "PING":
        return "PONG"
 
    if message.startswith("GET:"):
        item_id = message[4:]
        return "ITEM " + item_id
 
    return "ERROR"
 
 
print(expected_response("PING"))
print(expected_response("GET:42"))
print(expected_response("HELLO"))

Expected output:

PONG
ITEM 42
ERROR

Mark points:

  • handles the normal PING request;
  • handles a GET:<id> request by extracting the ID;
  • returns ERROR for an invalid request;
  • test output includes all three expected responses.

Common weak answer:

  • testing only the normal case. A useful client test includes an invalid request as well.

Answer 10: Connection Error

Model answer:

import socket
 
 
def safe_connect(host, port):
    try:
        client = socket.create_connection((host, port), timeout=0.1)
        client.close()
        return "CONNECTED"
    except OSError:
        return "CONNECTION FAILED"
 
 
print(safe_connect("127.0.0.1", 1))

Expected output:

CONNECTION FAILED

Mark points:

  • attempts the connection using the supplied host and port;
  • uses a short timeout so the program does not hang;
  • closes the socket if the connection succeeds;
  • catches connection-related OSError exceptions;
  • returns a controlled failure message instead of crashing;
  • produces the expected output for the closed local port test.

Common weak answer:

  • catching every exception and hiding unrelated programming errors. It is better to catch the connection-related exception type expected here.