La programmazione distribuita in Java ha come obiettivo permettere a più programmi, in esecuzione su macchine diverse, di cooperare come se fossero parte di un unico sistema. Il problema principale non è solo la comunicazione, ma l’astrazione della rete: idealmente il programmatore dovrebbe concentrarsi sulla logica applicativa e non sui dettagli di trasmissione dei dati.
Modello client-server
Il modello più classico è quello client–server, in cui un server resta in attesa di richieste e uno o più client si collegano per ottenere un servizio. In Java, il primo strumento per realizzare questo modello sono i socket.
Socket
Note
Con i socket TCP, il server apre una porta e rimane in ascolto, mentre il client si collega specificando indirizzo IP e porta. Una volta stabilita la connessione, la comunicazione avviene tramite stream di input e output associati al socket.
Example
import java.net.*;import java.io.*;import java.util.*;public class EchoServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(4321); System.out.println("Server in attesa..."); Socket socket = serverSocket.accept(); System.out.println("Client connesso"); Scanner in = new Scanner(socket.getInputStream()); PrintWriter out = new PrintWriter(socket.getOutputStream()); while (true) { String line = in.nextLine(); if (line.equals("quit")) break; out.println("Ricevuto: " + line); out.flush(); // fondamentale per inviare i dati } socket.close(); serverSocket.close(); } } ``` ```java import java.net.*; import java.io.*; import java.util.*; public class LineClient { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1", 4321); System.out.println("Connessione stabilita"); Scanner socketIn = new Scanner(socket.getInputStream()); PrintWriter socketOut = new PrintWriter(socket.getOutputStream()); Scanner stdin = new Scanner(System.in); while (true) { String input = stdin.nextLine(); socketOut.println(input); socketOut.flush(); System.out.println(socketIn.nextLine()); } } } ```
Questo approccio funziona, ma presenta limiti evidenti: il server gestisce un solo client alla volta e il programmatore deve occuparsi manualmente del protocollo di comunicazione. Per supportare più client, il server deve essere multi-threaded, creando un thread per ogni connessione.
Example
import java.net.*;import java.io.*;import java.util.*;import java.util.concurrent.*;public class MultiEchoServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(1337); ExecutorService pool = Executors.newCachedThreadPool(); while (true) { Socket socket = serverSocket.accept(); pool.submit(() -> { try { Scanner in = new Scanner(socket.getInputStream()); PrintWriter out = new PrintWriter(socket.getOutputStream()); while (true) { String line = in.nextLine(); if (line.equals("quit")) break; out.println("Ricevuto: " + line); out.flush(); } socket.close(); } catch (IOException e) { e.printStackTrace(); } }); } } } ```
Java RMI
Note
Java Remote Method Invocation è un protocollo tale per cui un client può invocare metodi di un oggetto che risiede in un’altra JVM come se fosse locale. Questo è possibile grazie a oggetti remoti, interfacce remote e proxy (stub e skeleton).
Il primo passo è la definizione di un interfaccia remota condivisa tra client e server che estende Remote e ogni metodo lancia RemoteException.
Il server poi implementa questa interfaccia e rende l’oggetto accessibile da remoto estendendo UnicastRemoteObject. Questo oggetto è poi pubblicato nell’RMI Registry.
Il client poi recupera lo stub dal registry e utilizza l’oggetto remoto come se fosse locale.
A runtime, RMI si occupa di serializzare i parametri, trasmetterli sulla rete, invocare il metodo sul server e restituire il risultato. Oggetti non remoti vengono passati per copia, mentre oggetti remoti vengono passati tramite riferimento allo stub.
Infine, è importante ricordare che RMI gestisce le chiamate in modo concorrente: più client possono invocare metodi simultaneamente, quindi i metodi remoti devono essere thread-safe.