La Programmazione Client/Server in Java
Mar 16 Ott 2007
   





Introduzione

Per realizzare un programma in grado di gestire una connessione client-server in java si utilizzano le classi disponibili nel package java.net. Le classi del package java.net che si utilizzeranno sono le seguenti I metodi della classe ServerSocket che si utilizzeranno sono i seguenti
 
Classe ServerSocket
Metodo Descrizione
Socket accept() Si pone in attesa di una richiesta. Arrivata la richiesta la accetta e costituisce una connessione
void close Chiude la Socket.
InetAddress getInetAddress() Restitisce l'indirizzo locale di questa Server Socket.
int getLocalPort() Restituisce la porta a cui è in attesa la Socket.
 

Il costruttore di questa classe è ServerSocket(int port), dove con port si indica la porta in cui si pone in ascolto la ServerSocket.

I metodi della classe Socket che si utilizzeranno sono i seguenti

Classe Socket
Metodo Descrizione
void close() Chiude questa Socket.
int getLocalPort() Restituisce la porta a cui è connessa la Socket.
void setReceiveBufferSize(int size) Specifica la dimensione del Buffer di ricezione.
void setSendBufferSize(int size) Specifica la dimensione del Buffer per l'invio dei dati. 

I costruttori di questa classe sono i seguenti

Costruttori della Classe Socket
Costruttore Descrizione
Socket() Crea una Socket non connessa.
Socket(InetAddress address, int port) Crea una Socket e la connette alla specifica porta dello specifico indirizzo IP.
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) Crea una Socket sulla macchina locale di cui è specificata l'indirizzo IP e la porta, e la connette con la porta remota specificata e con l'indirizzo IP specificato.
Socket(String host, int port) Crea una Socket e la connette con l'host il cui indirizzo è specificato da host con la porta port.

Esempio Server

Un Server ha la struttura seguente

Server.java
  1. import java.io.*;
  2. import java.net.*;
  3. import java.util.*;
  4.  
  5. public class Server extends Thread
  6. {
  7. private ServerSocket Server;
  8. public static void main(String argv[]) throws Exception
  9. {
  10. new Server();
  11. }
  12. public Server() throws Exception
  13. {
  14. Server = new ServerSocket(4000);
  15. System.out.println("Il Server è in attesa sulla porta 4000.");
  16. this.start();
  17. }
  18. public void run()
  19. {
  20. while(true)
  21. {
  22. try {
  23. System.out.println("In attesa di Connessione.");
  24. Socket client = Server.accept();
  25. System.out.println("Connessione accettata da: "+
  26. client.getInetAddress());
  27. Connect c = new Connect(client);
  28. }
  29. catch(Exception e) {}
  30. }
  31. }
  32. }
  33.  
  34. class Connect extends Thread
  35. {
  36. private Socket client = null;
  37. BufferedReader in = null;
  38. PrintStream out = null;
  39. public Connect() {}
  40. public Connect(Socket clientSocket)
  41. {
  42. client = clientSocket;
  43. try
  44. {
  45. in = new BufferedReader(
  46. new InputStreamReader(client.getInputStream()));
  47. out = new PrintStream(client.getOutputStream(), true);
  48. }
  49. catch(Exception e1)
  50. {
  51. try { client.close(); }
  52. catch(Exception e) { System.out.println(e.getMessage());}
  53. return;
  54. }
  55. this.start();
  56. }
  57. public void run()
  58. {
  59. try
  60. {
  61. out.println("Generico messaggio per il Client");
  62. out.flush();
  63. // chiude gli stream e le connessioni
  64. out.close();
  65. in.close();
  66. client.close();
  67. }
  68. catch(Exception e) {}
  69. }
  70. }

Il server deve poter accettare richieste da più client contemporaneamente. La classe Server estendendo la classe Thread

  1. public class Server extends Thread

realizza un multithreading, in questo modo il server è in grado di accettare richieste da più client contemporaneamente.
La costruzione del server prevede l'esecuzione dei seguenti passi
  • Si crea un oggetto ServerSocket e lo si pone in ascolto su una determinata porta.
    La classe Server implementerà il server, nel main della classe Server si crea un nuovo oggetto Server. Nel costruttore della classe Server si crea un oggetto ServerSocket che si metterà in ascolto sulla porta 4000, quindi si fa partire l'esecuzione in multithreading chiamando il metodo this.start(), che lancia il metodo run.

    1. public Server() throws Exception
    2. {
    3. Server = new ServerSocket(4000);
    4. System.out.println("Il Server è in attesa sulla porta 4000.");
    5. this.start();
    6. }

  • Nel metodo run vi è un ciclo infinito in cui il server è in ascolto sulla porta 4000, ed ogni volta che riceve una richiesta crea i canali di comunicazione per poter comunicare con il client.
    Quando il server riceve una richiesta da un client crea una nuova istanza di una Socket per quel client.

    1. while(true)
    2. {
    3. try
    4. {
    5. System.out.println("In attesa di Connessione.");
    6. Socket client = Server.accept();
    7. System.out.println("Connessione accettata da: "+
    8. client.getInetAddress());
    9. Connect c = new Connect(client);
    10. }
    11. catch(Exception e) {}
    12. }

  • Una volta accettata la connessione viene creata una nuova istanza dell'oggetto Connect. Questo oggetto viene utilizzato per creare i canali di comunicazione tra il client ed il server. Nel costruttore di questa classe si crea il canale di comunicazione in output

    1. out = new PrintStream(client.getOutputStream(), true);

    ed il canale di comunicazione in input

    1. in = new BufferedReader(
    2. new InputStreamReader(client.getInputStream()));

    in caso di errori si chiude la connessione, altrimenti viene lanciato il metodo che si occuperà della comunicazione con il server this.start().
    Nell'esempio proposto ci si limita ad inviare un generico messaggio al server e subito dopo chiude i canali di comunicazione e la connessione.
  • Una volta conclusa la comunicazione saranno chiusi i canali di comunicazione e la connessione.

Esempio Client

Un Client ha la struttura seguente

Client.java
  1. import java.io.*;
  2. import java.net.*;
  3. import java.util.*;
  4.  
  5. public class Client
  6. {
  7. public static void main(String argv[])
  8. {
  9. BufferedReader in = null;
  10. PrintStream out = null;
  11. Socket socket = null;
  12. String message;
  13. try
  14. {
  15. // open a socket connection
  16. socket = new Socket("localhost", 4000);
  17. // Apre i canali I/O
  18. in = new BufferedReader(
  19. new InputStreamReader(socket.getInputStream()));
  20. out = new PrintStream(socket.getOutputStream(), true);
  21. // Legge dal server
  22. message = in.readLine();
  23. System.out.print("Messaggio Ricevuto : " + message);
  24. out.close();
  25. in.close();
  26. }
  27. catch(Exception e) { System.out.println(e.getMessage());}
  28. }
  29. }

Il client si connette al server seguendo i seguenti passi
  • Il client effettua una connessione con il server creando un nuovo oggetto Socket

    1. socket = new Socket("localhost", 4000);

    in questo esempio si suppone che il server si trovi sulla stessa macchina del client.
  • Quindi si creano i canali di comunicazione con il server

    1. in = new BufferedReader(
    2. new InputStreamReader(socket.getInputStream()));
    3. out = new PrintStream(socket.getOutputStream(), true);

  • a questo punto si può incominciare la comunicazione con il server. In questo esempio la comunicazione si limita alla lettura di un messaggio da parte del client.

    1. message = in.readLine();
    2. System.out.print("Messaggio Ricevuto : " + message);

  • Una volta completata la comunicazione il client disalloca le risorse impiegate: chiude i canali di comunicazione e la connessione con il server.

    1. out.close();
    2. in.close();