L'accesso ai socket nel in Java avviene tramite l'uso delle classi e delle interfacce presenti nel package java.net. Un'applicazione può, quindi, accedere a flussi di dati su reti basate sui protocolli TCP/IP e UDP/IP.

Java
Linguaggio Java
Linguaggio Java
categoria · sviluppo · modifica

Come posso contribuire?
→ Vai su Wikiversity

Guida alla programmazioneJava/Introduzione - Specifica di linguaggioJava/Tipi di dato - Libreria standard

  1. Comunicazione serialeJava/Comunicazione seriale
  2. Stream
  3. Input e output su consoleJava/Input e output
  4. Java Collections Framework
  5. AWT e SwingJava/AWT e Swing
  6. Reflection
  7. SerializzazioneJava/Serializzazione
  8. SocketJava/Socket

Si utilizzano in particolare le seguenti classi:

  1. InetAddress, per la gestione degli indirizzi IP
  2. ServerSocket, per la creazione di socket lato server
  3. Socket, per la creazione di socket lato client
  4. DatagramSocket, implementa i socket per la trasmissione senza connessione
  5. MulticastSocket, fornisce supporto per i socket di tipo multicast

Indirizzo IP

modifica

In Java un indirizzo IP è rappresentato dalla classe InetAddress: questa fornisce tutti quei metodi necessari a manipolare un indirizzo internet, necessario all'instradamento dei dati sulla rete. In particolare:

  • getAllByName(String host) - è un metodo che ritorna tutti gli indirizzi internet di un host dato il suo nome in forma estesa (esempio: getAllByName("www.wikipedia.org"))
  • getByName(String host) - ritorna un oggetto INetAddress che rappresenta l'indirizzo IP dell'host. [1]
  • getHostName() ritorna una stringa rappresentante il nome dell'host.

Socket Lato Server

modifica

Per realizzare un canale di comunicazione Client/Server occorre la creazione di un programma Server che rimane in ascolto su una determinata porta, e di un programma Client che, da remoto, lo invochi.

Per la creazione del programma Server in Java è necessario seguire i seguenti passaggi:

  • Importare i package java.io e java.net per poter utilizzare le classi Socket e ServerSocket
  • Intercettare l'eccezione IOException che deve essere gestita (tramite try e catch) o propagata (tramite throws), dato che vengono richiamati metodi delle classi che appartengono ai package java.io e java.net.
  • Creare un oggetto ServerSocket, utilizzando il costruttore ServerSocket(int port) che si aspetta come parametro la porta del Server in ascolto per il servizio da attivare. Ad esempio:
 ServerSocket server = new ServerSocket(9999);

Da notare che il numero corrispondente alla porta di ascolto non è una stringa.

  • Utilizzare il metodo accept() dell'oggetto ServerSocket per poter accettare le chiamate Client su un oggetto Socket:
 Socket client = server.accept();

Il metodo che viene invocato ritorna un oggetto Socket che potrà essere utilizzato per le comunicazioni con il client

  • Creare due oggetti InputStreamReader e DataOutputStream per attivare uno stream di comunicazione con il cliente (ovviamente l'InputStreamReader servirà per leggere le richieste del client, e il DataOutputStream per inviare risposte al client):
 BufferedReader is = new BufferedReader(new InputStreamReader(client.getInputStream()));
 DataOutputStream os = new DataOutputStream(client.getOutputStream());
  • Richiamare il metodo close() sugli oggetti dello stream e sull'oggetto ServerSocket per chiudere i canali di comunicazione:
 os.close();
 is.close();
 server.close();

Per avere informazioni sul server in ascolto, Java mette a disposizione la classe InetAddress ed i metodi getInetAddress e getLocalPort per identificare, rispettivamente, il nome del server e la porta impiegata. Il metodo getInetAddress della classe ServerSocket permette di ottenere un dato che deve essere analizzato dalla classe InetAddress per poterne ricavare un valore stringa comprensibile. Dunque per conoscere l'indirizzo del server bisogna effettuare

 InetAddress indirizzo = server.getInetAddress();
 String indirizzo_leggibile = indirizzo.getHostAddress();

Per quanto riguarda il numero della porta utilizzato per ascoltare le chiamate client, bisogna utilizzare il metodo getLocalPort della classe ServerSocket in questo modo

 int porta = server.getLocalPort();

A livello implementativo, un programma Server deve contenere, dopo una inizializzazione, un ciclo, possibilmente infinito o condizionato da uscita del programma, in cui viene atteso in maniera sincrona il collegamento del client: ad ogni iterazione verrà creato un oggetto Socket che permetterà di stabilire tale collegamento e gestirà il flusso di dati, ricavabili tramite la DataInputStream.

Un esempio di programmazione del server è:

 static public void start() throws IOException
 {
   //inizializzazione dell'oggetto ServerSocket
   ServerSocket serverFather = new ServerSocket(9999);
   //recupero info su server
   InetAddress info = serverFather.getInetAddress();
   String server = info.getHostAddress();
   int port = serverFather.getLocalPort();
   System.out.println("Nome:"+ server + " Porta:"+ port);
   //ciclo infinito
   while (true)
   {
     System.out.println("Il server va in sleep...");
     //accesso le richieste del client
     Socket socket = serverFather.accept();
     System.out.println("Il server si sveglia...");
     //trovo le info sul client
     InetAddress infoclient = socket.getInetAddress();	
     String client = infoclient.getHostAddress();	
     int portclient = socket.getLocalPort();
     System.out.println("Il client " + client + " " + portclient +  " ha svegliato il server...");
		
     //apro il canale di comunicazione con il client
     DataInputStream is = new DataInputStream(socket.getInputStream());  
     DataOutputStream os = new DataOutputStream(socket.getOutputStream());
		
     //leggo i dati scritti, l'uscita viene dettata dalla stringa QUIT
     while (true)
     {
       String clientInput = is.readLine();
       if ((clientInput == null) || (clientInput.equals("QUIT")))
         break;
       os.writeBytes(clientInput + "\n");
       System.out.println("Il client ha scritto " + clientInput);
     }
     is.close();
     os.close();
     socket.close();
     System.out.println("Il client è stato soddisfatto...il server torna a dormire...");
  }
}

Socket Lato Client

modifica

A livello client i passaggi sono i seguenti:

  • Importare i package java.io, java.net per utilizzare le classi Stream (per i canali di comunicazione) e Socket (per il nostro scopo)
  • Intercettare l'eccezione IOException che deve essere gestita (tramite try e catch) o propagata (tramite throws), dato che vengono richiamati metodi delle classi che appartengono ai package java.io e java.net.
  • Creare un oggetto Socket e specificare l'indirizzo IP ed il numero di porta in ascolto sul server, caratteristiche imposte dal TCP per la comunicazione server/client. Ad esempio, per connettersi al server descritto negli esempi sopra, si deve effettuare un'inizializzazione nel seguente modo:
 Socket client = new Socket("localhost",9999);

A livello client l'indirizzo IP viene ricavato mentre la porta viene assegnata in modo dinamico in base alla disponibilità.

  • Creare il canale di comunicazione con il server per inviare e ricevere messaggi tramite gli Stream di Byte in input ed output tramite le classi DataInputStream e DataOutputStream, associandole ai metodi getInputStream e getOutputStream della classe Socket.
 DataInputStream is = new DataInputStream(client.getInputStream());
 DataOutputStream os = new DataOutputStream(client.getOutputStream());
  • Chiudere gli stream di comunicazione e l'oggetto socket
 is.close();
 os.close();
 client.close();

Server Multithreading

modifica

Nella pratica succede spesso che un server deve poter gestire le richieste di più client (come ad esempio nella richiesta di una pagina web da parte di più browser). Per fare questo è necessario attivare un thread separato per ogni richiesta: si deve pertanto sfruttare la programmazione multithreading messa a disposizione dal Java, effettuando un'estensione della classe Thread (o, alternativamente, un'implementazione dell'interfaccia Runnable).

Quello che segue è un esempio di server multithread. Sostanzialmente la tecnica è quella di creare un oggetto che funga da server, e tante sessioni, in thread separati, quante chiamate vengono effettuate dai client.

  • Per prima cosa si importano le librerie java.io e java.net
 import java.io.*;
 import java.net.*;
  • la classe che gestisce il server sarà un'estensione della classe Thread
 class serverMT extends Thread {
  • vengono dichiarate alcune variabili all'interno della classe che verranno utilizzate in seguito
private Socket s; //socket di connessione con il client
private BufferedReader in; //stream di input
private PrintWriter out; //stream di output
private String myclient;
  • Il costruttore della classe ha come oggetto il socket del client che effettua la richiesta di connessione al server. Inoltre in questa fase ci si occupa di aprire il canale di comunicazione e di far partire il thread associato alla gestione del servizio
public serverMT(Socket s) throws IOException {
 this.s = s;
 in = new BufferedReader(new InputStreamReader(s.getInputStream()));
 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())),true);
 this.start(); 
}
  • Metodo che permette la gestione del servizio, verrà chiamata a seguito di this.start() dell'oggetto serverMT:
public void run() {
  try {
    myclient = in.readLine();
    while (true) {
      String str = in.readLine();
      if (str.equals("END")) break;
      System.out.println("Ricevuto da C" + myclient + " e rispedito " + str);
      out.println(str);
    }
    System.out.println("chiusura...");
    s.close();
  } catch(IOException e) {
    System.out.println("Eccezione");
  }
}
  1. Javadoc della classe INetAddress, su download.oracle.com. URL consultato il 5 giugno 2011.