Interprocess Communication & Sockets
Processes on different computers share no memory, so the only way they can cooperate is by sending each other messages. This Part covers the building blocks every later topic stands on: the TCP/IP stack, TCP and UDP, ports and sockets in Java, how data is flattened into bytes (marshalling), multicast, and overlay networks.
- You willcompare TCP and UDP along named dimensions, the classic 3-mark exam question.
- You willread and write Java TCP and UDP client and server code, call by call.
- You willexplain the failure models of UDP, TCP and IP multicast, and why TCP is not fully reliable.
- You willmarshal one record in CDR, Java serialization, XML and JSON, and argue XML vs JSON.
Protocols and the TCP/IP Stack
The problem. Two programs running on two different computers cannot read each other's variables: each has its own private memory. If they are to cooperate, one must put data into a message, hand it to the network, and the other must pick it up. Interprocess communication (IPC) is the name for how processes on different computers exchange data in this way.
IPC is the foundation for everything higher up in the subject. Remote method invocation (Part VI), group communication and publish-subscribe (Part VII), the NFS file system (Part VIII) and DNS (Part IX) are all built from the primitives in this Part: UDP and TCP sockets, marshalling, multicast and overlays. The picture shows where those primitives sit.
Clients, servers and protocols exam
Client-server computing has three elements. A client is a process that requests a service. A server is a process that offers a service to one or more clients. The two are joined by a network. (A process is simply a running program.)
For the two to understand each other, sending bytes is not enough: they must agree in advance on what the bytes mean and when to send them. That agreement is a protocol.
A protocol is a well-known set of rules and formats to be used for communication between processes in order to perform a given task (CDK §3.3.4). It defines two things:
- the sequence of messages to be exchanged: who sends what and when, so that sending and receiving are coordinated;
- the format of the data in those messages: how it is encoded.
Humans follow protocols too, but theirs are unspoken (eye contact, gestures, taking turns to talk). Computers need the rules spelled out exactly. Because an agreed protocol says nothing about the programming language or the machine, a Java program can talk to a C program as long as both follow it.
The course example is Buyya's Math server. It reads one line of text of the form operator:first:second, such as +:12:21, and replies with one line holding the answer.
12+21), the other side misreads it or waits forever.The TCP/IP stack exam
Sockets, the programming interface this Part teaches, live inside the TCP/IP protocol stack, so it helps to know what each layer does. A layer is a piece of networking software that offers a service to the layer above and uses the service of the layer below. The four layers, from the top down, are:
| Layer | What it does | Examples | Unit |
|---|---|---|---|
| Application | Message exchange between standard or user applications | HTTP, FTP, Telnet, SMTP, DNS, Zoom | Messages |
| Transport | Delivers data to a specific process on a remote host using ports. Its programming interface is sockets | TCP, UDP | Segments / datagrams |
| Internet / Network | Delivers packets to a remote host by IP address, routing across networks. Best-effort only | IP (v4, v6) | IP datagrams |
| Physical / Link | Transmits the signals or frames carrying the data between directly connected nodes | Ethernet, WiFi, device drivers | Frames |
Encapsulation: how data moves through the stack
Data moves through the stack by encapsulation. On the way down, each layer wraps what it was given in its own header (a block of control information such as addresses) and passes the bigger package down. On the way up at the receiver, each layer reads and strips off its own header and passes the rest up. Step through it:
- The sending process hands its data, the string "hello", to the transport layer through a socket. At this point it is just application data.
- The transport layer (TCP or UDP) adds its header, which holds the source and destination ports, plus a checksum.
- The Internet layer (IP) adds its header, which holds the source and destination IP addresses so that routers can forward the packet towards the right host.
- The link layer adds a frame header and sends the bits across the physical network. Each row in the middle is the same data with one more wrapper.
- At the receiver the package goes up, and each layer strips its own header. The link layer removes the frame header; IP checks the destination address in the IP header: yes, this is the right host.
- The transport layer reads the destination port in the TCP/UDP header. That port is what tells the host which process gets the data.
- The headers are gone, and the receiving process reads exactly the bytes that were sent: "hello".
Best effort: what IP does not promise
The Internet layer is unreliable, or "best-effort": it tries to deliver each packet but promises nothing. IP packets may be lost, duplicated, delayed or reordered. IP also checksums only its own header, not the data, so TCP and UDP add their own checksums. (A checksum is a small number computed from the bytes; the receiver recomputes it, and a mismatch reveals corruption.) Putting the check at the ends rather than in the network is the end-to-end argument from Part II at work.
Addresses
Hosts are identified by IP addresses. An IPv4 address is 4 bytes, written in "dotted-quad" form, one decimal number per byte, such as 128.250.25.158. An IPv6 address is 16 bytes. People use host names instead, and DNS (Part IX) maps a name to its address.
Check yourselfA packet has reached the right computer. Which header field decides which program on that computer receives it, and which layer reads it?
The destination port in the TCP or UDP header, read by the transport layer. The IP address in the IP header only got the packet to the host; the port picks out the process.
Check yourselfWhich of these can IP do to your packets? - Lose them
- Deliver them twice
- Deliver them in a different order
- All of the above
D. IP is best-effort: packets may be lost, duplicated, delayed or reordered. It also checks only its own header, which is why TCP and UDP carry their own checksums.
- IPC is how processes on different computers exchange messages; every later topic is built on it.
- A protocol fixes the sequence of messages and the format of the data.
- TCP/IP has four layers. IP gets data to the right host (best effort only); TCP/UDP get it to the right process using ports; each layer adds a header on the way down and strips it on the way up.
TCP vs UDP exam
The transport layer offers applications two protocols that make very different promises. Choosing between them, and being able to list their differences, is one of the most reliably examined skills in the subject: the 2016 exam asked for three differences outright.
TCP (Transmission Control Protocol) is "a connection-oriented protocol that provides a reliable flow of data between two computers". Its programming interface is a two-way byte stream.
UDP (User Datagram Protocol) is a connectionless protocol that sends independent packets called datagrams, "with no guarantees about arrival or order of arrival". Its interface is message passing.
Connection-oriented means the two ends must first set up a connection, an agreed two-way channel, before any data flows. Connectionless means each datagram is sent on its own, carrying its full destination address, with no set-up.
How TCP builds reliability on top of unreliable IP
Since IP is only best-effort, TCP has to manufacture its guarantees out of extra machinery at the two ends (CDK §3.4.6). There are six mechanisms:
- Connection set-up. The two ends agree a bidirectional channel before data flows. The routers in between know nothing about it: the connection exists only as state at the two ends.
- Sequence numbers. Each segment carries the byte offset of its data within the stream, so the receiver can put segments back in order and drop duplicates.
- Acknowledgements and retransmission. The receiver acknowledges ("acks") the data it has received; if no ack arrives before a timeout, the sender retransmits.
- Flow control. Acks carry a window size: how much the sender may send before the next ack. A slow reader can therefore throttle a fast writer.
- Receive buffering. Incoming data is held in a buffer at the receiving end until the application reads it.
- Checksums cover both the header and the data.
UDP, by contrast, adds very little to IP: only ports, a length field and an optional checksum. There is no set-up and there are no acks, and a single message can be up to 64 KB. The header layouts show the difference in how much each protocol carries.
The comparison table exam
Be able to reproduce most rows of this table. An exam answer that names the dimension ("Connection: …", "Reliability: …") and then states both sides is the easiest way to collect every mark.
| Aspect | TCP | UDP |
|---|---|---|
| Connection | Connection-oriented: connect/accept handshake, then close | Connectionless: every datagram is independent and fully addressed |
| Abstraction | Byte stream, no message boundaries | Discrete messages, boundaries preserved |
| Reliability | Acks, timeouts, retransmission. Data arrives unless the connection breaks | None. Datagrams may be lost (omission failures) |
| Ordering and duplicates | Delivered in sender order; duplicates discarded | No ordering; no duplicate detection |
| Flow control | Yes (sliding window). A fast writer is blocked | None. A fast sender overruns the receiver and datagrams are dropped |
| Overhead | Higher: state at both ends, extra messages, latency | Low: small header, no set-up, no acks |
| Message size | Unlimited | ≤ 64 KB (often about 8 KB in practice) |
| Multicast | No (point-to-point) | Yes (IP multicast) |
| Java classes | Socket, ServerSocket | DatagramSocket, DatagramPacket, MulticastSocket |
| Slide analogy | A phone call | Sending several letters or emails, each holding part of the message |
| Uses | HTTP, FTP, Telnet, SMTP, SSH; Skype call signalling | DNS, VoIP, live streaming, online games, clock server, ping-style probes; Skype media |
Why not use TCP for everything?
Because guaranteed delivery costs three overheads (CDK §4.2.3):
- State has to be stored at both the source and the destination (sequence numbers, buffers, timers).
- Extra messages have to be transmitted (the set-up handshake and the acks).
- The sender suffers extra latency (it must wait for the set-up before sending, and for acks).
Choosing between them
every byte must arrive, in order: file transfer, web pages, and requests that change state (an enrolment, a bank update). The set-up cost is spread over a whole session of requests.
timeliness matters more than completeness, as with voice and video, where a late packet is useless anyway; when a short request can simply be retried, as with DNS; or when you need multicast.
The slides list "Ping" as a UDP application, but the real ping utility uses ICMP, a different protocol. Read the slide as meaning "a simple echo or probe service".
Past exam 2016 Q4(a) · 3 marksState three differences between UDP and TCP communication.
Any three of these, each naming the dimension and both sides (this is the guide's model answer):
- Connection. TCP is connection-oriented, so the two ends must connect and accept before any data flows. UDP is connectionless: each datagram is independent and carries its own destination.
- Reliability. TCP uses acks, retransmission, checksums and sequence numbers, so lost packets are resent and duplicates are removed. UDP has no acks or retries, so datagrams can be lost (omission failures).
- Ordering. TCP delivers data in sender order, whereas UDP may deliver it out of order.
- Abstraction. TCP offers a byte stream without message boundaries, while UDP sends discrete messages of at most about 64 KB.
- Flow control and overhead. TCP has flow control and a higher overhead in state, extra messages and latency. UDP has neither, so it gives low latency, which suits DNS and VoIP, and it also supports multicast.
Check yourselfWhich statement about UDP is false? - It preserves message boundaries.
- It supports multicast.
- Its flow control stops a fast sender overrunning the receiver.
- A datagram may be lost without either side being told.
C is false. UDP has no flow control: a fast sender overruns the receiver and datagrams are dropped. Flow control (the sliding window) is a TCP feature. A, B and D are all true of UDP.
Check yourselfFor each, pick TCP or UDP and give the reason: (a) a live video call, (b) uploading an assignment file, (c) a DNS lookup.
- (a) UDP. Timeliness beats completeness: a late video frame is useless, so retransmission would only add delay.
- (b) TCP. Every byte must arrive, in order, with no duplicates.
- (c) UDP. A short request that can simply be retried on timeout; a TCP connection set-up would cost more than the lookup itself.
Check yourself"TCP is better than UDP, so it should be used for everything." Give the textbook's three reasons why not.
Guaranteed delivery costs (1) state stored at both source and destination, (2) extra messages transmitted (set-up and acks), and (3) extra latency for the sender. Applications that can tolerate loss, or that do their own retries, avoid these costs by using UDP.
- TCP: connection-oriented, reliable, ordered byte stream with flow control and no message boundaries; costs state, messages and latency. A phone call.
- UDP: connectionless datagrams, boundaries kept, no reliability, ordering or flow control, ≤ 64 KB, supports multicast; cheap and fast. Letters in the post.
- TCP builds its guarantees on best-effort IP with set-up, sequence numbers, acks and retransmission, windows, receive buffers and checksums.
Ports and Sockets
The problem. An IP address only gets data as far as the right computer. But one computer runs many processes at once (a web server, a mail server, your browser), so something else has to pick out the right process. That is the job of ports. Sockets are then what those processes actually program against.
Ports exam
A port is a logical access point on a host, identified by a positive 16-bit integer (0–65535). TCP and UDP use ports to map incoming data to a particular process. The IP address finds the computer; the port finds the process on it.
Together the two make a transport address: the pair (IP address, port).
Buyya compares it to posting a letter. The IP address is the house address, which gets the letter to the right building; the port is the name of the person living there, which gets it to the right reader.
Well-known ports and port ranges
Some services have well-known ports, registered with IANA (the body that assigns Internet numbers), so that every client knows where to find them:
| ftp | telnet | smtp | http | https | login |
|---|---|---|---|---|---|
| 21/tcp | 23/tcp | 25/tcp | 80/tcp | 443/tcp | 513/tcp |
Who owns a port
The rules on ownership come from CDK §4.2.2. A port has exactly one receiving process but can have many senders; IP multicast (3.9) is the one exception. A process may use several ports, but processes cannot share a port, so a second attempt to bind one fails with BindException. A client's port is chosen by the system, whereas servers publicise their port numbers so that clients can reach them.
BindException. The only case where several processes share a port is IP multicast.Location transparency: don't hard-code the address
Hard-coding (IP, port) into a client pins the service to one machine forever. To get location transparency (clients not needing to know where a service physically is), clients instead ask a name server or binder (Part VI, Part IX) for the service by name at runtime. This allows relocation of a service (moving it between runs and re-registering), but not migration while it is running.
Sockets exam
Buyya: a socket is "an endpoint of a two-way communication link between two programs running on the network". It is bound to a port, so that the transport layer can tell which application the data is for.
CDK §4.2.2 puts it in terms of IPC: interprocess communication means sending a message from a socket in one process to a socket in another. To receive, a socket must be bound to a local port and one of the host's IP addresses, and each socket uses either UDP or TCP.
Sockets come from BSD UNIX, and they are the programming interface at the transport layer. They are language-independent, so a socket program in Java can talk to one written in C. The picture is the one to carry in your head: a socket is a process's "doorway" onto the network, and its address is an IP address plus a port.
"A socket handle is treated like a file handle"
Socket I/O follows the same pattern as file I/O: you open the socket, read and write through streams, then close it. getInputStream() and getOutputStream() return ordinary Java streams, so the same wrapper classes you use for files work on sockets unchanged.
| Step | File | TCP socket |
|---|---|---|
| Open | new FileInputStream("data.txt") | new Socket(host, port) |
| Get a stream | the stream itself | s.getInputStream(), s.getOutputStream() |
| Read / write | read, write, wrapped in DataInputStream etc. | exactly the same wrappers |
| Close | close() | close() |
The listen/accept model (TCP) exam
How can one TCP server talk to many clients at once, all on the same well-known port? The answer is that the server uses one socket only for listening, and gets a brand-new socket for every client it accepts:
- The server binds a listening socket to its well-known port and listens.
- The client creates a socket, to which the system assigns a port, and sends a connection request to (server host, port).
- The server accepts the request. This creates a new socket dedicated to that client, and all further communication with the client goes through it.
- The original listening socket stays open, so the server can accept other clients while it serves this one.
- Requests that have not yet been accepted wait in the listening socket's queue. Java's default queue length is 50, and a request that arrives when the queue is full is refused.
- The server creates a listening socket bound to its well-known port 1234 and waits. Its queue of pending connection requests is empty.
- Client A creates a socket (the system gives it port 52311) and sends a connection request to (S, 1234). Until the server accepts it, the request waits in the listening socket's queue.
- The server accepts. A new socket dedicated to A is created, and from now on A and the server talk only through it. It is identified by the 4-tuple (client IP, client port, server IP, server port) = (A, 52311, S, 1234).
- The listening socket is still open, so client B (port 40000) can connect too; its request waits in the queue while the server is busy.
- Another accept gives B its own new socket. Notice both new sockets use server port 1234: TCP tells them apart by the 4-tuples, which differ in the client half.
- If requests arrive faster than the server accepts them, the queue fills (Java's default length is 50) and a further request, here from client C, is refused.
Buyya's chapter says the new socket is bound to "a different port". The slides' "recent model" says it uses the same server port, and that is the correct version: TCP tells connections apart by the 4-tuple (client IP, client port, server IP, server port), so many connections can share the server's port.
Check yourselfWhat is a port and why is it needed?
A port is a 16-bit number that TCP and UDP use to map incoming data to a process. It is needed because an IP address identifies only the host. Each port has one receiver but can have many senders. Well-known ports are 0–1023 (for example FTP on 21 and HTTP on 80), and user services use ports from 1024 up.
Check yourselfDefine a socket. How does a TCP server serve many clients?
A socket is an endpoint of a two-way link between two programs. It is bound to a port, and it is programmed like a file handle.
To serve many clients, the ServerSocket listens on a well-known port, and each accept() returns a new socket for that client. The new socket has the same local port and is distinguished by the 4-tuple, while the listening socket keeps accepting. Each client socket is handed to a thread, so a blocked client does not stall the others.
Check yourselfYou start your server on port 7896, then start a second copy of it on the same machine. What happens, and why?
The second copy fails with BindException (port already in use). A port has exactly one receiving process, so two processes cannot bind the same port. (Multicast is the only exception.)
Check yourselfA server has accepted three clients. How many sockets does it hold, and which port are they on? - 3 sockets on 3 different ports
- 3 sockets, all on the server port
- 4 sockets, all on the server port
- 1 socket shared by all clients
C. One listening socket plus one new socket per accepted client, and all of them use the server's port. The connections are distinguished by their 4-tuples (client IP, client port, server IP, server port). Option A is Buyya's older description, which the slides correct.
- A port (16-bit) picks the process on a host; (IP, port) is a transport address. One receiver per port, many senders; servers publish their ports, clients get system-chosen ones; user services use 1024 and up.
- A socket is an endpoint of a two-way link, bound to a local port and IP address, using either UDP or TCP, and programmed like a file handle.
- A TCP server keeps one listening socket open and gets a new socket per accepted client on the same port, told apart by the 4-tuple.
TCP Sockets in Java
Now the ideas become code. The java.net package gives you a handful of classes, and the TCP server and client each follow a short fixed recipe. This is the code the exam expects you to be able to read, explain and write.
The java.net classes
The java.net package has one class for IP addresses, two for TCP and three for UDP:
ServerSocket exists only to hand out Sockets; each Socket is one end of a connection. On the UDP side a DatagramSocket sends and receives DatagramPackets, and MulticastSocket extends it with group membership (the hollow triangle means "is a subclass of").InetAddressrepresents an IP address and hides whether it is IPv4 or IPv6.getByName("host")does a DNS lookup and can throwUnknownHostException.ServerSocketis the TCP server socket.new ServerSocket(port)binds and listens; port 0 means any free port, which you can read back withgetLocalPort().accept()blocks (the calling thread waits) until a request arrives, and then returns aSocket.Socketis one end of a TCP connection.new Socket(host, port)creates the socket and connects it. Its main methods aregetInputStream(),getOutputStream(),setSoTimeout()andclose().DatagramSocketis a UDP socket. A server constructs it with a port, and a client with none, which gives it any free port. It offerssend(), a blockingreceive(),setSoTimeout()andconnect().DatagramPacketholds one datagram: the message bytes, their length, and a host and port. There is one constructor for sending and another for receiving, and the accessors aregetData(),getLength(),getAddress()andgetPort().MulticastSocketis a subclass ofDatagramSocketthat addsjoinGroup(),leaveGroup()andsetTimeToLive().
Stream wrappers (java.io)
A socket's streams only move raw bytes. To send typed values or lines of text, you wrap them in whichever java.io class suits your protocol, exactly as you would a file stream:
DataInputStream.readUTF(), BufferedReader.readLine(), ObjectInputStream.readObject(). Both ends must use matching wrappers.DataOutputStreamandDataInputStreamwrite and read primitive values (writeInt,writeUTF, …) in a machine-independent binary format, which makes them a simple external data representation (3.8).- For line-based text protocols, use
BufferedReader.readLine()together withPrintWriterorBufferedWriter. The slides'DataInputStream.readLine()is deprecated. ObjectOutputStreamandObjectInputStreamsend whole objects (3.8).
Be careful with writeUTF: it sends a 2-byte length followed by the UTF-8 bytes of the string, so every writeUTF must be matched by a readUTF.
readUTF (green) uses them to know where the string ends; a mismatched readLine (red) has no idea, and either hangs or returns garbage.Exceptions: each failure has a name
All of these are subclasses of IOException, so a single catch (IOException e) catches them all, but knowing which is which tells you what went wrong.
| Exception | What happened |
|---|---|
UnknownHostException | A bad host name: the DNS lookup failed |
ConnectException: Connection refused | No server is listening at that (host, port), for example because the client was started before the server |
BindException | The port is already in use |
SocketTimeoutException | A timeout set with setSoTimeout expired |
EOFException | The peer closed the stream (end of file / end of stream) |
The TCP recipes exam
Server: five steps
- Open the server socket:
new ServerSocket(PORT). - Wait for a client:
Socket client = server.accept(). - Create I/O streams from
client.getInputStream()andclient.getOutputStream(). - Communicate: read requests, write replies.
- Close the client socket:
client.close(). The server socket stays open.
Client: four steps
- Create a socket with
new Socket(server, port), which also connects it. - Create the I/O streams.
- Perform I/O with the server.
- Close the socket.
The client
The client below follows the four steps, and its comments mark each one. It sends the string "hello" and prints what comes back.
// TCPClient.java - send a string, print the reply
import java.net.*;
import java.io.*;
public class TCPClient {
public static void main(String[] args) {
try (Socket s = new Socket("localhost", 7896)) { // 1. connect
DataInputStream in = new DataInputStream(s.getInputStream()); // 2. streams
DataOutputStream out = new DataOutputStream(s.getOutputStream());
out.writeUTF("hello"); // 3. send
System.out.println("Received: " + in.readUTF()); // receive
} catch (UnknownHostException e) {
System.out.println("Unknown host: " + e.getMessage());
} catch (IOException e) { // e.g. ConnectException if server not up
System.out.println("IO: " + e.getMessage());
} // 4. try-with-resources closes the socket
}
}
| Line | What it does and why |
|---|---|
import java.net.*; java.io.*; | Sockets live in java.net; streams and IOException in java.io. |
try (Socket s = new Socket("localhost", 7896)) | Step 1. Creates the socket and connects to port 7896 on this machine; the constructor returns only once the connection is set up. Declaring it in try (…) ("try-with-resources") means Java closes it automatically at the end, even on an exception. |
new DataInputStream(s.getInputStream())new DataOutputStream(s.getOutputStream()) | Step 2. Wraps the raw byte streams so we can read and write typed values. |
out.writeUTF("hello") | Step 3. Sends the 2-byte length and the UTF-8 bytes of "hello". |
in.readUTF() | Blocks until the server's reply arrives, then returns it as a string, which is printed. |
catch (UnknownHostException e) | The host name could not be resolved. This catch must come before the IOException one, because it is a subclass. |
catch (IOException e) | Any other network failure, for example ConnectException if the server is not running. |
closing brace of try | Step 4. The socket is closed here automatically. |
The server, and why it uses threads
A server must serve many clients. The slides' recipe for that is a while(true) loop that accepts a client and then creates a thread, passing it the client socket as a parameter. (A thread is an independent line of execution inside a process; Part IV covers them fully.) The thread creates the streams, serves the client, and ends when it is done. This design is called thread-per-connection (CDK Fig. 4.6).
The contrast is an iterative server, such as the slides' SimpleServerLoop, which does the same work in the main thread. While it is blocked serving one slow client, it cannot accept() anyone else.
readUTF(), B and C wait in the queue, however quick their requests are.// TCPServer.java - echo server, one thread per connection
import java.net.*;
import java.io.*;
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket listenSocket = new ServerSocket(7896); // 1. open server socket
while (true) {
Socket clientSocket = listenSocket.accept(); // 2. wait for a client
new Thread(new Connection(clientSocket)).start(); // hand it to a worker thread
}
}
}
class Connection implements Runnable {
private final Socket socket;
Connection(Socket socket) { this.socket = socket; }
public void run() {
try (Socket s = socket; // 5. closed automatically
DataInputStream in = new DataInputStream(s.getInputStream()); // 3. streams
DataOutputStream out = new DataOutputStream(s.getOutputStream())) {
while (true) {
String data = in.readUTF(); // 4. blocks THIS thread only
out.writeUTF(data); // echo
}
} catch (EOFException e) { // client closed its end
System.out.println("Client left");
} catch (IOException e) {
System.out.println("IO: " + e.getMessage());
}
}
}
| Line | What it does and why |
|---|---|
new ServerSocket(7896) | Step 1. Binds the listening socket to port 7896 and starts listening. Throws BindException if the port is taken. |
while (true) | A server runs forever, accepting one client after another. |
listenSocket.accept() | Step 2. Blocks the main thread until a client connects, then returns a new Socket for that client. The listening socket stays open. |
new Thread(new Connection(clientSocket)).start() | Hands the client socket to a fresh thread and starts it; the main thread immediately loops back to accept(). |
class Connection implements Runnable | The work one thread does. Runnable means it has a run() method the thread executes. The socket arrives through the constructor. |
try (Socket s = socket; DataInputStream in …; DataOutputStream out …) | Steps 3 and 5. Creates the streams, and guarantees that the socket and streams are closed when the thread finishes, however it finishes. |
in.readUTF() | Step 4. Blocks this thread only until the client sends a string. The other clients' threads carry on. |
out.writeUTF(data) | Echoes the string back. |
catch (EOFException e) | The client closed its end, so readUTF hit end of stream: the normal way this loop ends. |
catch (IOException e) | Any other failure, such as a broken connection. |
Run the two programs against each other and this is the order in which the calls happen:
- The server starts first: new ServerSocket(7896) binds and listens, and accept() blocks the main thread until a client turns up. (If the client were started first, its connect would fail with
ConnectException.) - The client's new Socket("localhost", 7896) sends a connection request to the listening socket.
- The request is accepted, the connection is set up, and the client's
Socketconstructor returns: the client is connected and creates its streams. - On the server, accept() returns a new Socket, which the main thread hands to a new Connection thread before looping straight back to
accept()for the next client. The thread creates its streams and blocks inreadUTF(). - The client calls out.writeUTF("hello"). The bytes arrive on the per-client socket, and the thread's
readUTF()returns "hello". - The thread echoes the string with
writeUTF; the client'sreadUTF(), which was blocked, returns and it printsReceived: hello. - The client's try-with-resources closes its socket. On the server side the thread's next
readUTF()finds end of stream and throws EOFException; it prints "Client left" and its own try-with-resources closes its socket. The listening socket is untouched.
Message framing over TCP exam
The problem. TCP delivers a byte stream with no message boundaries. TCP itself decides how to cut the data into segments, and a read returns whatever bytes have arrived so far. So one write may arrive split over several reads, which is called a partial read, and, conversely, several writes may arrive together in one read.
The fix. The application protocol must frame each message so that the receiver knows where it ends. There are three ways:
What goes wrong without framing. A message that is cut in two fails to parse, or a reader blocks forever waiting for data that the writer thinks it has already sent. This is the "matching of data items" issue of 3.7. The snippet shows the delimiter approach, with one JSON object per line.
// Line-framed JSON (also import java.nio.charset.StandardCharsets)
BufferedReader in = new BufferedReader(
new InputStreamReader(s.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(
new OutputStreamWriter(s.getOutputStream(), StandardCharsets.UTF_8), true); // autoflush
out.println("{\"op\": \"QUERY\", \"subjectCode\": \"COMP90015\"}"); // one object + '\n'
String reply = in.readLine(); // collects partial reads until '\n'; null means peer closed
InputStreamReader(…, UTF_8)turns bytes into characters using an explicit encoding, andBufferedReaderaddsreadLine().PrintWriter(…, true): thetrueturns on autoflush, so everyprintlnis sent immediately.out.println(…)writes one JSON object followed by the'\n'delimiter.in.readLine()keeps collecting partial reads until it sees'\n', then returns the whole line; it returnsnullwhen the peer has closed the connection.
A buffered writer holds data until it is flushed. Forgetting to call flush(), or to enable autoflush as the snippet does, leaves the peer blocked in readLine() forever, even though your code "sent" the message.
Check yourselfWhich line creates a TCP server? new Socket(host, 7896)new ServerSocket(7896)new DatagramSocket()server.accept()
new Socket(host, 7896)new ServerSocket(7896)new DatagramSocket()server.accept()B. new ServerSocket(port) binds and listens. new Socket(host, port) always creates a client socket and connects it (a common pitfall). accept() is called on an existing server socket, and DatagramSocket is UDP.
Check yourselfA client calls out.writeUTF("hi") and the server reads with BufferedReader.readLine(). What happens?
The server receives a 2-byte length followed by "hi", with no newline, so readLine() blocks waiting for a '\n' that never comes (or, later, returns a line with garbage length bytes in it). Every writeUTF must be matched by a readUTF: the two ends must agree on the framing.
Check yourselfWhy does the server in TCPServer hand each client socket to a new thread rather than serving it in main?
Because readUTF() blocks. In an iterative server, while main is blocked waiting for one slow client it cannot call accept(), so every other client waits. With thread-per-connection, a read blocks only that client's thread; main goes straight back to accept().
Check yourselfA client sends two JSON requests with two separate write calls. The server does one read and gets both, run together. Is TCP broken? What should the protocol do?
No: TCP is a byte stream with no message boundaries, so several writes may arrive in one read (and one write may be split over several). The application must frame its messages: a delimiter such as one JSON object per line read with readLine, a length prefix, or fixed-size messages.
- Server:
new ServerSocket(port)→accept()(blocks, returns a newSocket) → streams → read/write → close the client socket. Client:new Socket(host, port)(connects) → streams → I/O → close. - Wrap the raw streams (
DataInput/OutputStream,BufferedReader/PrintWriter, object streams) and match every write with the corresponding read. - Serve many clients with a thread per connection. TCP has no message boundaries, so frame messages with a delimiter, a length prefix or a fixed size, and flush.
UDP Sockets in Java
With UDP there is no connection to set up, so the code deals only with sockets and packets. That makes it shorter than the TCP code, but it also means the program itself has to cope with whatever the network does: nothing tells you a datagram went missing.
A process creates a DatagramSocket. A server binds it to its agreed port, while a client lets the system pick any free port. To send, you build a DatagramPacket(bytes, length, address, port) and call send(): no connection is needed, because every packet carries its own destination. To receive, you pass in an empty DatagramPacket(buffer, buffer.length), and receive() fills in the data, its length and the sender's address and port. That last part is how the receiver knows where to send a reply.
receive() fills in the length and the sender's address and port (gold). Reply by sending to exactly that address and port. Only the first getLength() bytes of the buffer are the new message; the rest is whatever was there before.The client sends one datagram and waits for an answer; the server echoes each datagram back to whoever sent it.
// UDPClient.java - send one datagram and wait up to 2 s for the reply
import java.net.*;
import java.io.*;
public class UDPClient {
public static void main(String[] args) { // args: message, server host
try (DatagramSocket socket = new DatagramSocket()) { // any free local port
byte[] m = args[0].getBytes();
InetAddress host = InetAddress.getByName(args[1]);
socket.send(new DatagramPacket(m, m.length, host, 6789));
DatagramPacket reply = new DatagramPacket(new byte[1000], 1000);
socket.setSoTimeout(2000); // don't block forever
socket.receive(reply);
System.out.println(new String(reply.getData(), 0, reply.getLength()));
} catch (SocketTimeoutException e) {
System.out.println("Timeout: request/reply lost or server down");
} catch (IOException e) {
System.out.println("IO: " + e.getMessage());
}
}
}
| Line | What it does and why |
|---|---|
new DatagramSocket() | A UDP socket on any free local port: a client doesn't need a fixed port, because the server will learn it from each datagram. In try-with-resources, so it is closed at the end. |
args[0].getBytes() | Turns the message string into bytes; a datagram carries bytes, not strings. |
InetAddress.getByName(args[1]) | Looks up the server's IP address from its name (DNS); may throw UnknownHostException. |
socket.send(new DatagramPacket(m, m.length, host, 6789)) | Builds a sending packet addressed to port 6789 on the server and sends it. send returns at once: there is no connection and no acknowledgement. |
new DatagramPacket(new byte[1000], 1000) | An empty receiving packet with room for 1000 bytes. |
socket.setSoTimeout(2000) | Limits the next receive to 2 seconds. Without it, a lost request or reply would leave the client blocked forever. |
socket.receive(reply) | Blocks until a datagram arrives (or the timeout fires), then fills in reply. |
new String(reply.getData(), 0, reply.getLength()) | Converts only the bytes that actually arrived (getLength()), not the whole 1000-byte buffer. |
catch (SocketTimeoutException e) | No reply within 2 s: the request or the reply was lost, or the server is down. UDP cannot tell you which. |
// UDPServer.java - echo each datagram back to its sender
import java.net.*;
import java.io.*;
public class UDPServer {
public static void main(String[] args) {
try (DatagramSocket socket = new DatagramSocket(6789)) { // agreed server port
byte[] buffer = new byte[1000];
while (true) {
DatagramPacket request = new DatagramPacket(buffer, buffer.length);
socket.receive(request); // from ANY sender
socket.send(new DatagramPacket(request.getData(), request.getLength(),
request.getAddress(), request.getPort())); // reply to sender
}
} catch (IOException e) {
System.out.println("IO: " + e.getMessage());
}
}
}
| Line | What it does and why |
|---|---|
new DatagramSocket(6789) | Binds the server's socket to the agreed port 6789, so clients know where to send. |
byte[] buffer = new byte[1000] | One receive buffer, reused for every datagram. A longer datagram would be silently truncated. |
new DatagramPacket(buffer, buffer.length) | An empty receiving packet over that buffer. |
socket.receive(request) | Blocks until a datagram arrives from any sender, then fills in the data, length, and the sender's address and port. |
new DatagramPacket(request.getData(), request.getLength(), request.getAddress(), request.getPort()) | Builds the reply: the same bytes, addressed back to whoever sent the request. |
socket.send(…) | Sends the echo; then the loop goes back to receive for the next datagram, from whichever client. |
- The server binds a DatagramSocket to port 6789 and blocks in
receive. The client creates a DatagramSocket on any free port. No connection is made: neither side knows about the other yet. - The client builds a packet addressed to (host, 6789) and calls send(). Send is non-blocking: it returns as soon as UDP has the datagram.
- The server's receive() returns, and the request packet now holds the data, its length, and the client's IP address and port.
- The client sets a 2-second timeout and blocks in
receive(reply). - The server replies to request.getAddress(), request.getPort(), the address it just learned, and loops back to
receivefor the next datagram from anyone. - The client's
receivereturns, and it prints only the first getLength() bytes of the buffer. - If the request or the reply is lost, nobody is told. The client's timeout fires after 2 s with SocketTimeoutException; without the timeout it would block forever. The client cannot tell whether the request, the reply or the server was the problem.
Three pitfalls in the slide and textbook versions
- Use
m.length, which counts bytes, rather than the string'slength(), which counts characters. The two differ for non-ASCII text (an accented letter is two bytes in UTF-8). - Print only
getLength()bytes of the buffer, because otherwise stale buffer contents are printed too. - A message larger than the buffer is silently truncated:
Check yourselfHow does the UDP server know where to send its reply, when it never connected to the client?
receive() fills in the sender's IP address and port in the request packet. The server reads them with request.getAddress() and request.getPort() and builds its reply packet addressed there.
Check yourselfWhy does the UDP client call setSoTimeout but the UDP server doesn't?
A server waiting for requests is happy to wait forever: that is its job. A client waiting for a reply must not, because the request or reply may have been lost or the server may have crashed, and UDP never reports either. The timeout turns "wait forever" into a SocketTimeoutException the client can handle (for example by retrying).
- UDP code is just
DatagramSocket+DatagramPacket: no connection. A server binds its agreed port; a client takes any free port. - Send packets carry their destination; receive packets are filled in with the data, its length and the sender's address and port, which is how the reply is addressed.
- Use byte lengths, print only
getLength()bytes, size the buffer for the largest message, and give the client a timeout.
Characteristics of IPC exam
This section steps back from code to the general vocabulary CDK (§4.2.1) uses to describe any message-passing IPC. The same words (synchronous, blocking, reliable, ordered) come back in RPC, group communication and every later topic, so they are worth getting exactly right.
Message passing uses two operations, send and receive, defined in terms of a message (a sequence of bytes) and a destination. Each destination has a queue: senders add messages to it, and the receiver removes them.
Synchronous vs asynchronous; blocking vs non-blocking
An operation is blocking if the calling process (or thread) is suspended until something happens; non-blocking if it returns at once. The main distinction is between synchronous and asynchronous communication, and the table shows what each means for the two operations. Notice that only the asynchronous send is non-blocking, while receive is usually blocking either way.
| send | receive | |
|---|---|---|
| Synchronous | Blocking: waits until the matching receive is issued | Blocking: waits until a message arrives |
| Asynchronous | Non-blocking: returns once the message is copied to a local buffer; transmission continues in parallel | Blocking (usual), or non-blocking: a buffer is filled in the background and the program is told by polling or interrupt |
With multiple threads, one thread can block in receive while the others keep working, and blocking code is much simpler to write and read. Non-blocking receive looks more efficient, but it forces the program to handle messages outside its normal flow of control (in a callback or interrupt), so today's systems generally don't offer it.
In practice: Java's UDP has a non-blocking send and a blocking receive. With TCP, a read blocks until data arrives, and a write can block under flow control (when the receiver's buffer is full).
Destinations, reliability and ordering
Message destinations are (IP address, local port) pairs, with one receiver per port (3.3). The other two characteristics describe what the service promises about delivery.
Messages are guaranteed to be delivered despite a "reasonable" number of packets being dropped or lost. A service is unreliable if delivery can fail after even one lost packet.
Messages arrive uncorrupted and without duplication: exactly the bytes that were sent, exactly once.
Some applications need sender-order delivery (the order the messages were sent), and they treat out-of-order delivery as a failure.
Reliability, in CDK's sense, is validity plus integrity (the failure model of Part II). Keep these three words: the failure models in 3.7 are stated in exactly these terms.
Check yourselfSynchronous vs asynchronous communication: what's the difference, and why is blocking receive acceptable?
In synchronous communication, send and receive both block, so the processes synchronise on every message. In asynchronous communication, send returns once it has copied the message to a local buffer, and receive may block or not; a non-blocking receive is notified by polling or interrupt.
Blocking receive is acceptable because, with threads, one thread can block while the others run. Blocking is also simpler, which is why non-blocking receive is rarely provided.
Check yourselfWhich operation is non-blocking? - Synchronous send
- Synchronous receive
- Asynchronous send
- Java's
DatagramSocket.receive()
DatagramSocket.receive()C. Asynchronous send returns once the message is copied to a local buffer. Synchronous send and receive both block, and Java's UDP receive() blocks (until a datagram arrives or a timeout fires).
Check yourselfA message arrives intact but twice. Which reliability property is violated: validity or integrity?
Integrity, which requires messages to arrive uncorrupted and without duplication. Validity is about messages being delivered at all despite lost packets.
- Send puts a message on the destination's queue; receive takes it off. Destinations are (IP, port).
- Synchronous: blocking send and blocking receive. Asynchronous: non-blocking send; receive usually blocking (or non-blocking with polling/interrupt). Java UDP = non-blocking send, blocking receive.
- Reliability = validity (delivered despite reasonable loss) + integrity (uncorrupted, no duplicates); some applications also need sender ordering.
UDP and TCP: Issues and Failure Models exam
CDK now looks at each protocol in turn through the lens of 3.6: what issues a programmer must handle, and what can go wrong (its failure model, the precise list of failures it can suffer). These lists are frequently examined, and the TCP one contains the subject's favourite trick question: "is TCP reliable?"
UDP datagram communication (CDK §4.2.3)
A datagram is sent without acknowledgements or retries. There are four issues to know when using UDP:
Message size
The receiver supplies a fixed-size array, and longer messages are truncated. IP allows up to 216 bytes including headers, but most systems limit messages to about 8 KB, so larger data must be fragmented by the application.Blocking
Send is non-blocking (it returns once UDP/IP has the message); receive is blocking. An arriving message is queued at the socket bound to its destination port, and if no socket is bound there, it is discarded. To do other work while waiting, use separate threads.Timeouts
Waiting forever suits a server waiting for requests. A client waiting for a reply needssetSoTimeout(), because the sender may have crashed or the message may be lost. The interval should be large compared with the transmission time.Receive from any
receive accepts a message from any origin and reports who sent it. connect() restricts the socket to a single peer.UDP's failure model
- Integrity: UDP's checksum makes corruption negligible, but there is no duplicate detection.
- Omission failures: messages are dropped because of checksum errors or a lack of buffer space at the source or destination.
- Ordering failures: messages can be delivered out of sender order.
Applications therefore add their own checks: acknowledgements, retransmission and duplicate filtering turn UDP into a reliable request-reply protocol, which is exactly what Part VI builds. Uses: UDP suits DNS and VoIP, where occasional loss is acceptable, and more generally any application that wants to avoid the three overheads of guaranteed delivery.
Check yourselfDescribe UDP's failure model and how to make it reliable.
UDP suffers omission failures, caused by checksum errors or a lack of buffer space, and ordering failures. Its checksum gives integrity, but only against corruption. To make UDP reliable, you add acks, timeouts with retransmission, and sequence or request IDs that let the receiver filter duplicates and reorder messages. That is what a request-reply protocol does (Part VI).
TCP stream communication (CDK §4.2.4)
TCP takes the opposite approach: it presents the application with a stream and hides the awkward properties of the network underneath. There are five network characteristics the stream abstraction hides:
Roles, streams and closing
The client creates a socket and connects; the server's listening socket holds a queue of requests and accepts them. After set-up, the two can act as peers: each socket has an input stream and an output stream. Closing a socket means "I will write no more": data already buffered is still delivered, and once the reader has consumed it, further reads report end of stream (EOFException, -1, or null, depending on the read method). Using a broken stream gives an IOException.
close() still arrives; only after it has all been read does the reader see end of stream. That is the signal the echo server in 3.4 catches as EOFException.Three issues when using TCP streams
- Matching of data items. The two sides must agree on the content and order of what is sent. If one writes an
intthen adouble, the other must read anintthen adouble; otherwise the data is misinterpreted or the reader blocks. Framing (3.4) is part of this agreement. - Blocking. Reads block when no data is queued, and writes block under flow control.
- Threads. A server typically creates one thread per accepted connection, so that blocking on one client doesn't delay the others. Without threads, UNIX
selectcan test which streams have input ready.
TCP's failure model: is TCP reliable?
TCP protects integrity with checksums and sequence numbers, and validity with timeouts and retransmission. Even so, TCP is still not fully reliable. With excessive loss, a severed network or heavy congestion, TCP eventually declares the connection broken. When that happens, the processes cannot distinguish a network failure from a crash of the remote process, and they cannot tell whether their recent messages were received.
The two gold boxes are identical, and that is the point: from the client's side the two cases look exactly the same, and in neither can it know whether its last messages got through. Uses: TCP carries HTTP, FTP, Telnet and SMTP, each on a reserved port.
"Is TCP reliable?" wants a two-sided answer: yes, it masks packet loss (checksums and sequence numbers for integrity, retransmission for validity); but no, not in all circumstances, because under severe loss or a partition it declares the connection broken, and then the processes can't tell a network failure from a peer crash, nor whether recent messages arrived. "TCP is fully reliable" is a stated pitfall.
Check yourselfWhich characteristics of the network does the TCP stream abstraction hide?
Five:
- Message sizes: the application can write and read any amount.
- Lost messages: dealt with by acks and retransmission.
- Flow control: a fast writer is blocked.
- Duplication and ordering: handled with sequence numbers (packet identifiers).
- Message destinations: only needed at connect time.
Check yourselfIs TCP reliable?
TCP masks packet loss: checksums and sequence numbers give integrity, and retransmission gives validity. But with severe loss or a partition it declares the connection broken, and the processes then cannot tell a network failure from a crash of the peer, nor whether their recent messages arrived. So TCP does not guarantee delivery in all circumstances.
Check yourselfA UDP datagram arrives at a host, but no socket is bound to its destination port. What happens? - It waits in a queue until a socket binds the port
- It is discarded
- The sender gets an exception
- It is delivered to any socket on the host
B. An arriving datagram is queued at the socket bound to its destination port; if there is none, the message is discarded. UDP sends no notification back, so the sender never finds out.
- UDP issues: message size (truncation, about 8 KB), non-blocking send / blocking receive (unbound port → discarded), timeouts, receive from any. Failure model: omission and ordering failures; checksum for corruption but no duplicate detection.
- TCP hides message sizes, loss, flow control, duplication/ordering and destinations. Issues: matching data items, blocking, threads.
- TCP is not fully reliable: a broken connection can't be told apart from a peer crash, and recent messages may or may not have arrived.
Marshalling and External Data Representation exam
The problem. Inside a program, data lives in structures: records, arrays, objects pointing at other objects. A network carries only a flat sequence of bytes. So before sending, the data must be flattened into bytes, and on arrival it must be rebuilt. The complication is that machines differ in how they lay data out in memory:
- byte order: big-endian machines store the most significant byte of a number first, little-endian machines the least significant byte first;
- floating-point formats;
- character codes: ASCII (one byte per character) versus Unicode.
There are two ways to cope with these differences:
- An external data representation is "an agreed standard for the representation of data structures and primitive values".
- Marshalling is assembling a collection of data items into a form suitable for transmission in a message: translating it into the external representation.
- Unmarshalling is the reverse: disassembling it on arrival into an equivalent collection of data items.
Marshalling is usually done by middleware, using generated or generic code, because doing it by hand is error-prone.
The table compares the four representations covered below. The rows on type information and marshalling code are the ones that separate CDR from Java serialization most sharply.
| CORBA CDR | Java serialization | XML | JSON | |
|---|---|---|---|---|
| Form | Binary | Binary | Text | Text |
| Type info carried | None (types known from IDL) | Full: class name, version, field names and types | Tags; meaning given by namespaces and schemas | Field names plus basic types |
| Languages | Many, via IDL | Java only | Any | Any |
| Marshalling code | Generated from IDL | Generic, via reflection | Parser libraries | Parser libraries |
| Size | Compact | Fairly compact | Verbose | Lighter than XML |
| Used in | CORBA | Java RMI, object streams | SOAP web services, config files | Web/REST APIs, Assignment 1 |
To make the comparison concrete, the rest of this section encodes the same record in each format: a person called Smith, from London, born in 1984.
CORBA Common Data Representation (CDR)
CORBA is a multi-language middleware; IDL (Interface Definition Language) is the language-neutral notation in which both sides declare their data types. CDR has 15 primitive types, including short (16-bit), long (32-bit), their unsigned variants, float, double, char, boolean, octet (a raw byte) and any. Two rules govern how values are laid out:
- Byte order: values are sent in the sender's byte order, which is flagged in the message, and the receiver converts only if its own order differs (approach 2 above).
- Alignment: a primitive of n bytes starts at an index that is a multiple of n.
Constructed types are built from the primitives:
| Type | Sent as |
|---|---|
| string, sequence | a length, followed by the elements |
| array | the elements only (its length is fixed and known) |
| struct | its fields in declaration order |
| enum | an unsigned long |
| union | a type tag, followed by the chosen member |
No type information is sent. Both sides know the types from the IDL definition, and the IDL compiler generates the marshalling code. (Sun XDR, used by NFS, works similarly but always uses big-endian.) Step through the marshalling of struct Person {string name; string place; unsigned long year;} holding {"Smith", "London", 1984}, laid out 4 bytes per row:
- Both sides already hold the IDL definition of
Person, so the message needs to carry only values, in declaration order. Each row is 4 bytes. - The first field,
name, is a string, and a string is sent as its length first: the number 5, as a 4-byte unsigned long (bytes 0–3). - Then the characters themselves, one byte each: "Smit" fills bytes 4–7.
- The last character "h" goes in byte 8, and the row is padded with zeros to the next 4-byte boundary, so that the next 4-byte value starts at an index that is a multiple of 4 (the alignment rule).
- The second field,
place: its length 6 in bytes 12–15. - "Lond" in bytes 16–19…
- …and "on" plus two bytes of padding in bytes 20–23.
- The last field,
year, is an unsigned long: 1984 in bytes 24–27, in the sender's byte order (flagged elsewhere in the message). - Total: 28 bytes, and nothing in them says "name", "string" or "year". The receiver's IDL-generated code unmarshals by reading the same fields in the same order.
0-3 5 length of "Smith" 4-7 "Smit" 8-11 "h___" padded with zeros to a 4-byte boundary 12-15 6 length of "London" 16-19 "Lond" 20-23 "on__" 24-27 1984 unsigned long
Java object serialization
Java takes the opposite approach to CDR and sends type information along with the data. Serialization flattens an object, or a connected set of objects, for storage or transmission (for example as RMI arguments, Part VI), and deserialization restores them. A class allows this by declaring implements Serializable, a marker interface: one with no methods, which only labels the class as serializable.
The receiver is assumed to have no prior knowledge of the types, so the stream carries class information: the class name and a version number (serialVersionUID, or a hash of the class definition). The receiver uses it to check that it has the correct version of the class.
The whole object graph is serialized. If the object refers to other objects, those are serialized too. References are written as handles (small numbers standing for an object already in the stream), and each object and class is written only once; any later occurrence just uses the handle. This is what preserves shared references and cycles.
transient marks fields that must not be serialized, such as references to local resources like files and sockets, which mean nothing on another machine.
Reflection is what makes serialization generic. It is the ability of a program to inspect a class's name and its fields' names, types and values at runtime. So no per-class marshalling code is needed, unlike in CORBA. Deserialization uses it too: it loads the class by name, creates a constructor and fills in the field values.
public class Person implements java.io.Serializable {
private String name, place;
private int year;
private transient java.net.Socket conn; // not serialized: local resource
}
// sender: new ObjectOutputStream(sock.getOutputStream()).writeObject(person);
// receiver: Person p = (Person) new ObjectInputStream(sock.getInputStream()).readObject();
implements java.io.Serializable: permission to serialize; nothing to implement.name,place,year: ordinary fields, serialized with their names and types.transient … conn: skipped; after deserialization it isnull.writeObject(person)marshals the whole graph onto the socket's stream;readObject()rebuilds it and returns anObject, hence the cast(Person).
XML and JSON
The two text formats encode the same record as readable characters. (The guide's version also includes an id.)
<person id="123456789">
<name>Smith</name>
<place>London</place>
<year>1984</year>
</person>
{"id": 123456789, "name": "Smith", "place": "London", "year": 1984}
XML in more detail
XML (a W3C standard) is a textual, self-describing markup language. Its tags describe the data's structure, not its appearance, which is what distinguishes it from HTML. It is also extensible: users define their own tags, although applications that share documents must agree on them, as with SOAP's published tags.
- Building blocks: elements, which nest and so can represent hierarchies, and attributes, which are labels for simple values. Any binary data is encoded as base64 (binary turned into text characters).
- Well formed: tags match and are correctly nested, and there is a single root element.
- Namespaces (
xmlns:pers="URL") scope tag names to avoid clashes between vocabularies. - A schema defines the allowed elements and attributes, their nesting, order and types, so that a document can be validated. DTDs are an older, weaker way of doing this.
XML vs JSON trade-offs exam
Each format has advantages over the other, and you should be able to argue both directions.
- Richer self-description: namespaces avoid name clashes when several applications use one document.
- Schemas and DTDs allow formal validation of structure and types, which strict contracts such as SOAP web services rely on.
- Attributes, comments, and mixed text and markup; mature tooling (XPath, XSLT, signatures).
- More compact: no closing tags, so smaller messages and less bandwidth.
- Faster and simpler to parse and generate: maps directly onto program data structures (objects, arrays, maps); it is JavaScript's own object syntax.
- Native types (number, boolean, null, array) where XML has only text; easy for humans to read and write; dominant for REST APIs.
The two also have a lot in common: both are textual, human-readable and language-independent, and both are larger and slower than binary formats such as CDR or serialization. The tutors' point-by-point comparison is in 3.11.
Remote object references (CDK §4.3.4)
Marshalling also has to cope with references to objects in other processes. A remote object reference is "an identifier for a remote object that is valid throughout a distributed system". It is placed in invocation messages to say which object is to be invoked (Part VI).
A remote object reference must be unique in space and time. Space: unique across all processes on all computers. Time: never reused after its object is deleted, because clients may still hold old references, and invoking a deleted object must give an error and never reach a different object.
Check yourselfDefine marshalling. Compare CDR and Java serialization.
Marshalling translates data items into an external data representation for transmission, and unmarshalling rebuilds them on arrival. It is needed because of heterogeneity: machines differ in byte order, float formats and character sets.
CDR and Java serialization are both binary, but otherwise they take opposite approaches. CDR carries no type information, because both sides know the IDL; it is multi-language, sends values in the sender's byte order plus a flag, and uses marshalling code generated from the IDL. Java serialization carries the class name, version and field types, works in Java only, and is generic, using reflection.
Past exam 2016 Q4(b) · 2 marksGive a benefit of XML over JSON, and of JSON over XML.
- XML over JSON. XML is self-describing, with namespaces and schemas, so documents can be validated against a formal definition and several vocabularies can be combined without name clashes. This suits strict contracts such as SOAP web services.
- JSON over XML. JSON is more compact, because it has no end tags, and it is lighter and faster to parse. It maps directly onto programming-language data structures (objects and arrays; it is JavaScript object syntax), so it needs less bandwidth and processing.
Check yourselfWhat are Serializable, transient, handles and reflection for in Java serialization?
Serializableis a marker interface that allows a class's instances to be serialized.transientexcludes fields that refer to local resources, such as files and sockets.- Handles mean each object and class is written only once, with later references pointing to the handle. This preserves sharing and cycles.
- Reflection discovers the class and its field names, types and values at runtime, so no per-class marshalling code is needed. It is also used to reconstruct the objects.
Check yourselfWhat does a remote object reference contain, and why must it be unique in space and time?
It contains the IP address, port, creation time, object number and interface. The port and time identify the process, the counter identifies the object, and the interface says which methods the object offers.
It must be unique across all processes, and it must never be reused after its object is deleted, so that a stale reference gives an error instead of reaching a different object.
Check yourselfWhich representation sends no type information at all? - Java serialization
- JSON
- CORBA CDR
- XML
C. CDR sends only values; both sides know the types from the IDL. Java serialization sends full class information; JSON sends field names and basic types; XML sends tags.
Check yourselfIn CDR, why is "Smith" followed by three zero bytes?
Alignment: a primitive of n bytes must start at an index that is a multiple of n. "Smith" occupies bytes 4–8, so the next 4-byte value (the length of "London") must start at byte 12; bytes 9–11 are padded with zeros.
- Machines differ in byte order, float formats and character codes, so data is marshalled into an agreed external representation (or sent in the sender's format with a flag) and unmarshalled on arrival, usually by middleware.
- CDR: binary, no type info (IDL), sender's byte order + flag, alignment padding, generated code. Java serialization: binary, full class info, handles for sharing,
transient, generic via reflection, Java only. XML: text, tags, namespaces, schemas. JSON: text, compact, native types. - A remote object reference (address, port, time, object number, interface) must be unique in space and time.
Multicast Communication exam
Everything so far has been one process talking to one other. But some jobs need one process to talk to many: telling every replica of a database about an update, say. Sending a separate copy to each would be slow and would require knowing every member. Multicast solves this: it is sending one message from one process to every member of a group of processes, usually with group membership transparent to the sender.
Multicast has four uses:
Fault tolerance
via replicated services: requests go to all the servers in a group, so clients are still served if some of them fail.Service discovery
in spontaneous networks, for example Jini: "who out there offers a printer?"Performance
via replicated data: updates are multicast to all the replicas.IP multicast
In IP multicast, the sender transmits one IP packet to a multicast group, which is identified by a class D address: one whose first bits are 1110. The sender does not know who the members are or how big the group is. Membership is dynamic, so hosts can join and leave at any time, and you can send to a group without being a member.
For applications, IP multicast is available only through UDP. A socket joins the group, and every local socket that has joined and is bound to the destination port receives a copy, which makes this the one case where several processes share a port. Between networks, multicast routers forward the packets, and the TTL ("time to live") limits how many routers a packet may cross; Java's default of 1 keeps it on the local network.
IP multicast's failure model exam
IP multicast has the same failure model as UDP: omission failures, which makes it unreliable multicast. A message may reach some members but not others. That can be caused by loss between routers (which affects every member beyond that router), by a recipient's buffer being full, or by a multicast router failing. There is also no ordering: one sender's messages can arrive in different orders at different members, and messages from different senders arrive in different orders too.
- Two senders, S1 and S2, and a group of three members A, B and C (green). Time runs to the right; a slanted arrow is a message in flight.
- S1 multicasts m1. A and C get it, but B's copy is dropped because B's buffer is full. This is an omission failure, and nobody is told: neither S1 nor A nor C knows that B missed it.
- S1 multicasts m2. This time all three get it, but the copy to C is slow.
- S2 multicasts m3. It reaches C before m2 does, but reaches A after m2.
- The result: the members have seen different sets of messages (B lacks m1) in different orders (A has m2 before m3, C has m3 before m2). If A, B and C were replicas applying these as updates, they would now disagree.
How much this matters depends on the use. Replicated services need every replica to receive every request, and in the same order, or the replicas diverge, whereas discovery can tolerate loss. Hence the need for stronger protocols, built in Part VII:
- reliable multicast: a message is delivered to all members or none;
- totally ordered multicast: all members see all messages in the same order.
Multicast in Java
MulticastSocket s = new MulticastSocket(6789); // UDP socket on port 6789
InetAddress group = InetAddress.getByName("228.5.6.7"); // class D group address
s.joinGroup(group); // now receives group traffic
s.send(new DatagramPacket(m, m.length, group, 6789)); // one send reaches all members
s.receive(packet); // own and peers' messages
s.leaveGroup(group); s.close();
| Line | What it does and why |
|---|---|
new MulticastSocket(6789) | A UDP socket bound to port 6789. (Multicast exists only over UDP.) |
InetAddress.getByName("228.5.6.7") | The group's class D address. |
s.joinGroup(group) | Becomes a member, so this socket now receives datagrams sent to the group. (Needed to receive, not to send.) |
s.send(new DatagramPacket(m, m.length, group, 6789)) | An ordinary datagram addressed to the group: one send reaches every member bound to 6789. |
s.receive(packet) | Blocks until a group datagram arrives; a member receives its own messages as well as its peers'. |
s.leaveGroup(group); s.close(); | Leaves the group and releases the socket. |
Check yourselfWhat is IP multicast? Give its failure model and explain the need for stronger guarantees.
In IP multicast, one UDP datagram sent to a class D address (224–239.x.x.x) is delivered to all the members of a group. Membership is dynamic, senders need not be members, and the TTL limits how far the datagram spreads.
Its failure model is like UDP's. There are omission failures, where some members miss a message because of loss at a router, router failure or full buffers, and there is no ordering guarantee. Replicated services therefore need reliable multicast (all members or none) and totally ordered multicast, or the replicas become inconsistent.
Check yourselfWhich of these is a valid multicast group address? - 128.250.25.158
- 192.168.1.1
- 230.1.2.3
- 255.255.255.255
C. Class D addresses run from 224.0.0.0 to 239.255.255.255 (first bits 1110). 230.1.2.3 is in range; the others are not.
Check yourselfA multicast-based service-discovery protocol and a multicast-based replicated bank account both run over plain IP multicast. Which one is in trouble, and why?
The replicated bank account. Every replica must apply every update in the same order, but IP multicast may drop a message at some members and deliver messages in different orders, so the replicas' balances diverge. Discovery can tolerate a lost message (the client just asks again).
- Multicast sends one message to every member of a group; uses: fault tolerance, discovery, replicated data, event notification.
- IP multicast: one UDP packet to a class D address (224.0.0.0–239.255.255.255); dynamic membership; non-members can send; members on one host share the port; TTL limits the spread (Java default 1).
- Same failure model as UDP: omission failures and no ordering, hence reliable and totally ordered multicast (Part VII).
Overlay Networks and MPI
The last two topics are brief in the guide (CDK §4.5 and §4.6), but both appear in the self-test, and the overlay flooding problem is modelled on a real 2016 exam question. Overlays answer the question "how do I get a network service the Internet doesn't provide, without changing the Internet?"; MPI answers "what does message passing look like when performance is everything?"
Overlay networks
An overlay network is "a virtual network consisting of nodes and virtual links" built on top of an underlying network, such as IP. It provides a service tailored to a class of application, more efficient operation in a particular environment, or an extra feature such as multicast or security. It does this without changing the Internet itself, which fits the end-to-end argument (Part II).
- New services can be added without changing routers or standards.
- They allow experimentation and customisation.
- Multiple overlays can coexist.
- An extra level of indirection, which costs performance.
- Added complexity.
Overlays come in three types, grouped by what they are for:
| Type | Examples |
|---|---|
| For application needs | Distributed hash tables (key-based routing, usually on a ring), peer-to-peer file sharing, content distribution networks |
| For a network style | Wireless ad hoc networks, disruption-tolerant networks |
| For extra features | Multicast (the MBone), resilience, security (VPNs) |
Case study: Skype
Skype is built on an overlay. Its super nodes are ordinary hosts chosen for their bandwidth, reachability (a global IP address, not hidden behind NAT, the address translation home routers do) and availability. Users log in through a central login server, searches for users run across the super nodes, and a call needs no IP address or port. For the calls themselves, TCP is used for call signalling and UDP is preferred for media, with TCP, possibly through a relay node, used to get through firewalls.
Broadcast over an overlay of servers: flooding exam
In flooding, each server forwards a message to all its neighbours except the one it came from. This is safe only if the server graph is a tree (a connected graph with no loops). If a new server can connect to several existing servers, the graph has cycles, so copies circulate forever and members receive duplicates.
- Four servers. D joined by linking to C only, but C joined by linking to both A and B, so A, B and C form a cycle. Each server forwards a message to all its neighbours except the one it came from.
- A broadcasts m (id A:1) to its neighbours B and C.
- B forwards to C (its only other neighbour), and C forwards to B and D (everyone except A). On a tree this would be the end.
- But now B and C each receive a second copy and, following the rule, forward it again: B back to A, C back to A and on to D. A forwards again… The message circulates forever, servers deliver duplicates, and traffic grows until the network is flooded.
- Fix 1: unique IDs and a seen-set. Every broadcast carries a globally unique id (origin server's id plus a sequence number, or a UUID). Each server remembers the ids it has seen, and delivers and forwards a message only the first time; B and C drop their second copies. A hop count / TTL can be added as a safety net.
- Fix 2: a spanning tree. Compute a tree that reaches every server (here, leave out the B–C link) and forward only along its edges. The tree needs maintenance when servers join or leave.
2016 Q2(a)-styleServers broadcast messages to each other by forwarding to their neighbours. If a new server can connect to several existing servers instead of just one, what major problem arises and how would you fix it?
- Problem. With one connection per new server, the server graph is a tree, and "forward to all neighbours except the sender" reaches everyone exactly once. With several connections, the graph has cycles. A forwarded message comes back round a loop and is forwarded again, so it circulates forever. Servers deliver duplicates, and traffic grows until the network is flooded.
- Fix 1. Give every broadcast a globally unique ID, made of the originating server's ID plus a sequence number, or a UUID. Each server keeps a set of IDs already seen: it delivers and forwards a message only the first time, and drops later copies. Optionally, add a hop count / TTL as a safety net.
- Fix 2. Compute a spanning tree over the server graph and forward only along its edges. The tree needs maintenance when servers join or leave.
Check yourselfDefine an overlay network, with pros, cons and examples.
An overlay network is a virtual network of nodes and virtual links over an underlying network, offering a tailored service, efficiency or extra features. Its advantages are that the underlying network needs no change, that it allows experimentation, and that several overlays can coexist. Its disadvantages are the indirection, which costs performance, and the added complexity. Examples include DHTs, P2P file sharing, CDNs, the MBone and Skype.
MPI: the Message Passing Interface
MPI is a standard, from the MPI Forum in 1994, for message passing in high-performance computing (large parallel computations on clusters). It is portable, independent of the operating system and of language-specific socket APIs, and it has over 115 operations.
Its model is send and receive plus MPI library buffers at both ends. In MPI, blocking means "blocked until it is safe to return", that is, until the application's own buffer can be reused without corrupting the message.
There are four kinds of send, each in a blocking and a non-blocking form. The non-blocking forms return a handle at once, which you check later with MPI_Wait or MPI_Test.
| Send | Blocking | Non-blocking |
|---|---|---|
| Generic | MPI_Send: blocks until the message is in transit or delivered | MPI_Isend |
| Synchronous | MPI_Ssend: returns only once the message is delivered | MPI_Issend |
| Buffered | MPI_Bsend: returns once the data is copied into a user-attached buffer (MPI_Buffer_attach) | MPI_Ibsend |
| Ready | MPI_Rsend: the programmer asserts the receiver is ready, so the handshake is skipped. It fails if that is wrong | MPI_Irsend |
The receives are MPI_Recv (blocking) and MPI_Irecv (non-blocking), and they combine freely with any send. In this way MPI separates the synchronous/asynchronous distinction from the blocking/non-blocking one, which 3.6 treated as linked. MPI also has collective operations, which include scatter (split data among processes) and gather (collect it back).
Check yourselfWhich MPI send returns only once the message has been delivered? MPI_BsendMPI_SsendMPI_RsendMPI_Isend
MPI_BsendMPI_SsendMPI_RsendMPI_IsendB. MPI_Ssend, the synchronous send. MPI_Bsend returns once the data is in the user-attached buffer; MPI_Rsend skips the handshake on the programmer's word that the receiver is ready; MPI_Isend returns a handle immediately.
- An overlay is a virtual network of nodes and virtual links over IP, adding tailored services, efficiency or features (DHTs, P2P, CDNs, MBone, VPNs, Skype) at the cost of indirection and complexity.
- Flooding on a graph with cycles makes messages circulate forever; fix with unique ids plus seen-sets (and a TTL), or a spanning tree.
- MPI: HPC message passing with library buffers; "blocking" means safe to reuse the buffer; four sends (generic, synchronous, buffered, ready) × blocking/non-blocking.
Pitfalls and Tutorials 2 & 4
This last section gathers the mistakes that lose marks, a design question that pulls the whole Part together, and the official tutor answers from Tutorial 2 (Week 3: sockets, UDP and TCP) and Tutorial 4 (Week 5: data representation). Staff have confirmed the tutorial answers are examinable.
Common pitfalls
- Two common beliefs are both false: that "TCP is fully reliable" (3.7), and that "one write equals one read" (3.4).
- Mismatched reads and writes, such as
writeUTFpaired withreadLine, or a missingflush, leave the reader with garbage or blocked forever. new Socket(host, port)creates a client, never a server.client.close()closes one connection, not theServerSocket. Close sockets explicitly rather than relying on garbage collection.- With UDP, a
receivewith no timeout hangs forever if a datagram is lost.
Putting it together: a design question
ScenarioDesign the transport and message format for a multi-client enrolment (or dictionary) server.
- TCP. Requests change shared state, so they must not be lost, duplicated or reordered, and TCP provides that. The cost of setting up the connection is spread over the many requests in a session.
- Framing. TCP has no message boundaries, so the messages must be delimited (one JSON object per line, read with
readLine, with a flush on every write) or length-prefixed. Either way, partial reads are handled. - JSON. It is language-independent and debuggable, unlike Java serialization, which ties both ends to Java and to class versions.
- Concurrency. Use a multithreaded server (thread-per-connection or a worker pool, Part IV) with per-item locking, and handle
EOFExceptionandIOExceptionwhen a client disconnects.
Tutorial 2 (Week 3): sockets, UDP and TCP official answers
Tutorial 2 · Q1Briefly discuss three aspects of the socket interface.
- A socket is bound to a local port, and a socket address is IP address + port number.
- Sockets are used for communication, sending and receiving data, between two networked processes.
- Each socket is associated with one protocol, UDP or TCP.
- The socket interface is the programming interface between application code and the transport layer, and a socket handle is used much like a file handle.
Tutorial 2 · Q2Briefly explain three failures that can happen when using UDP.
- Data corruption can happen because UDP's checksum is optional.
- Omission failures happen because delivery is not guaranteed, so datagrams can be dropped.
- Ordering failures happen because datagrams can arrive out of sender order.
Tutorial 2 · Q3Briefly explain the aspects of TCP that address issues UDP does not.
The tutors pair each UDP problem with the part of TCP that answers it:
| UDP problem | TCP's answer |
|---|---|
| No connection | Connection-oriented: the client sends a connect request and the server accepts it before any data flows |
| Size limit of 64 KB | No limit on the amount of data an application can send |
| Can lose messages | Acknowledgements; unacknowledged data is retransmitted |
| A fast sender swamps the reader | Flow control matches the writer's speed to the reader's |
| Allows duplication and reordering | Message identifiers (sequence numbers) let the receiver reject duplicates and reorder segments |
Tutorial 2 · Q4List the steps at the client and at the server to establish a TCP stream socket connection.
The tutors' answer is the TCP three-way handshake, stepped through below, together with the Java calls on each side:
- Server. The server creates a socket bound to a known port (
new ServerSocket(port)), which listens.accept()blocks until a client connects and then returns a new socket for that client. The server reads and writes that socket's streams, then closes it. - Client. The client creates a socket naming the server's host and port (
new Socket(host, port)). This sends the connect request (SYN) and returns once the handshake is complete. The client then gets the input and output streams, reads and writes, and closes the socket. - The TCB (transmission control block) is the per-connection state each end keeps. It is the "state at both ends" listed among the costs of reliable delivery (3.2).
- The server's socket is bound to a known port and listening (
new ServerSocket(port);accept()is waiting). - The client calls connect() (in Java, inside
new Socket(host, port)), which sends SYN, a "let's synchronise" connection request. - The server creates its per-connection state, a TCB (transmission control block), in the SYN-RECEIVED state.
- The server replies SYN-ACK: it acknowledges the client's SYN and sends its own.
- On receiving SYN-ACK, the client's connect() returns success.
- The client sends the final ACK, acknowledging the server's SYN.
- The server's TCB moves to ESTABLISHED, and accept() returns the new per-client socket.
- Both ends now hold connection state, and data is exchanged on the stream in both directions.
The tutors' summary of UDP versus TCP matches 3.2. UDP is the simplest connectionless IPC: it offers a message-passing abstraction and sends single datagrams, and it is used for real-time video streaming, DNS and NTP. TCP offers a two-way stream with no message boundaries. It is the basis for producer/consumer communication, in which data is queued until the consumer is ready and the consumer waits when no data is available. It is used for HTTP and FTP.
Tutorial 4 (Week 5): data representation, XML and JSON official answers
The marshalling material repeats 3.8, including the two approaches: convert to an agreed external format, or send in the sender's format with an indication of which format it is. The tutors' XML vs JSON comparison, though, adds points to the trade-offs in 3.8. The table lines the two formats up point by point:
| Point | JSON | XML |
|---|---|---|
| Simplicity | Lightweight, so simple to read and write | Less simple than JSON |
| Data types | Has scalar data types, and expresses structured data through arrays and objects (native object support) | No notion of data types: XML Schema is needed to add type information |
| Arrays | Supports arrays | No array structure. Objects are expressed by convention, often a mix of attributes and elements |
| Readability | More human-readable | Less human-readable |
| Message size | Serialises to shorter strings, so less data is transmitted and performance improves | Text-heavy (closing tags), so messages are larger |
| Display | No display capabilities | Can be used to display data because it is a markup language |
CDK stresses that XML tags describe structure, not appearance (3.8). What the tutors mean is that XML can be transformed into a presentation, for example with XSLT stylesheets, so if you use this point, phrase it that way.
The 2016 paper asked exactly this (Q4(b): a benefit of XML over JSON and vice versa), so learn both columns. The same paper's Q4(a) asked for three TCP/UDP differences. For both, name the dimension first ("Message size: …", "Validation: …") and then give each side; that is what earns each mark.
Check yourselfA classmate writes "XML is better than JSON because XML has data types". What's wrong, according to the tutors' table?
It's backwards. XML itself has no notion of data types (everything is text); XML Schema is needed to add type information. JSON has scalar types (number, boolean, null) and arrays natively. A correct XML-over-JSON benefit is that schemas allow formal validation, and namespaces avoid name clashes.
- Never claim TCP is fully reliable or that one write equals one read; match reads to writes and flush;
new Socketis always a client; time out UDP receives. - Tutorial 2: socket = (IP, port), one protocol, file-like handle; UDP failures = corruption (optional checksum), omission, ordering; TCP answers each UDP gap; connection set-up is the SYN, SYN-ACK, ACK handshake, with a TCB at each end.
- Tutorial 4: JSON is simpler, typed, has arrays and is smaller; XML needs Schema for types, has no arrays, is larger, and can be transformed for display.