Socket

Come già detto, i programmi applicativi utilizzano i protocolli mediante apposite interfacce (API) fornite dal sistema operativo e dal software di rete. In generale, queste API non sono standardizzate, ma, fortunatamente, per i protocolli TCP e UDP, esiste uno standard de facto (disponibile nella stragrande maggioranza dei linguaggi di programmazione, tra cui, ad esempio, C e Java): l’interfaccia socket.

I socket sono stati inizialmente definiti nel 1983, e da allora si sono affermati come lo standard de facto per l’implementazione di applicazioni distribuite.

Interoperabilità

I socket sono disponibili su moltissime piattaforme (sistemi operativi e linguaggi di programmazione). Grazie a essi, si ottiene quindi l’interoperabilità dei programmi di rete: due applicazioni che usano i socket (con lo stesso protocollo di trasporto, ad esempio TCP) possono interagire anche se vengono eseguite su macchine con sistemi operativi diversi, e/o sono scritte in linguaggi di programmazione diversi.

Viceversa, applicazioni che utilizzano protocolli (di trasporto, applicativi, ecc...) diversi non possono interagire. Ad esempio, un’applicazione che utilizza UDP non può interagire con un’applicazione che utilizza TCP. Infatti, un elemento fondamentale della nozione di protocollo è che le applicazioni comunicanti usino appunto lo stesso protocollo.

Identificazione di una macchina

Per poter comunicare con una macchina, bisogna avere un modo di identificarla univocamente, distinguendola da tutte le altre macchine nel mondo. Come già detto, questa funzione è svolta dagli indirizzi IP, che (nel caso di IPv4) sono numeri di 32 bit, indicati solitamente in una notazione “decimale punteggiata”, come ad esempio 193.206.183.131.

Siccome, in generale, un indirizzo numerico di questo tipo non è molto facile da ricordare, vi si può associare un nome simbolico, detto nome di dominio (un esempio è uninsubria.it). La corispondenza tra indirizzi simbolici e nomi di dominio è mantenuta dal DNS (Domain Name System).

Dal punto di vista programmativo, in Java, un indirizzo IP può essere ottenuto da un nome di dominio attraverso il metodo statico InetAddress.getByName, che restituisce un oggetto di tipo InetAddress. Ad esempio, il programma seguente visualizza l’indirizzo IP corrispondente al nome di dominio passato come argomento a riga di comando:

import java.net.InetAddress;
import java.net.UnknownHostException;

public class WhoAmI {
	public static void main(String[] args) throws UnknownHostException {
		if (args.length != 1) {
			System.err.println("Usage: WhoAmI DomainName");
			System.exit(1);
		}

		InetAddress addr = InetAddress.getByName(args[0]);
		System.out.println(addr);
	}
}

Alcuni esempi di esecuzione di questo programma sono:

$ java WhoAmI google.com
google.com/216.58.208.174
$ java WhoAmI www.dista.uninsubria.it
www.dista.uninsubria.it/193.206.183.131

Client e server in Java

Ad alto livello, per stabilire una connessione tra client e server in Java:

  1. Il server deve rimanere in ascolto di richieste di connessione, usando un oggetto speciale ServerSocket.
  2. Il client cerca di stabilire una connessione con un server, attraverso un oggetto di tipo Socket.
  3. Una volta effettuata la connessione, vi si costruiscono sopra degli stream di I/O, che permettono di trattarla come se si stesse leggendo da e scrivendo su un file. Le regole secondo le quali leggere/scrivere (ad esempio, cosa contengono le richieste del client e le risposte del server) sono dettate dai protocolli di livello più alto.

Indirizzo localhost