Αντρω δι Νεξυνω

[Java] Scambio di file tra host remoti : Swing e SwingWorker

« Older   Newer »
  Share  
view post Posted on 5/12/2015, 12:54
Avatar

Nubbio x Sempre

Group:
Moderazione globale
Posts:
7,226

Status:


PARTE 1 di 3 : Introduzione



Buon giorno agli eventuali lettori :)

Riprendo qui, l'argomento dello scambio di file tra host remoti già trattato, in modalità di test esemplificativo, in questo precedente post.
Fermo restando quanto li indicato, che non sarà ripreso, la struttura logica li utilizzata sarà qui adattata ad un taglio più "pratico", anche se pur sempre di test, mirato ad affrontarne le finalità in un contesto grafico.

In particolare, dal lato server, si implementerà un metodo per consentire, a scelta dell'utente della applicazione, di selezionare l'interfaccia e l'indirizzo di rete su cui mettersi in ascolto per la trasmissione del file, utilizzando le tecniche già indicate in questo altro post, ovvero di mettersi in ascolto su di una porta di qualsivoglia indirizzo (opzione comunemente usata)
Tanto per il server quanto per il client, si affronterà l'argomento "grossi volumi", ossia la limitazione a "thread singolo" dello EDT di swing, limitazione che fa sembrare "congelata" l'interfaccia grafica in presenza di pesanti elaborazioni. Affrontare tale aspetto è lo scopo del test in questo post (vi rammento che qui appunto metodi studiati a scopo di apprendimento e, bene o male, funzionanti a mio beneficio futuro ed anche Vostro qualora Vi occorra). Questo "argomento" sarà affrontato utilizzando la classe "SwingWorker", da me mai utilizzata prima dato che non esisteva quando iniziai con Java ... siate buoni quindi ;)


Precisazione su quanto segue



Oltre al codice relativo ai particolari di implementazione, sarà fornito il codice completo delle applicazioni client e server sviluppate con Eclipse ed utilizzante la libreria MigLayout nella definizione delle interfacce grafiche, tale libreria non è standard in Java, tenetelo presente qualora vogliate copiare direttamente il codice.
In ogni caso, le interfacce sono semplicissime e poco rilevanti, createle da Voi come preferite, più importante è la logica applicata.


Cosa si vuole realizzare? :
le finestre grafiche relative



Le finalità che mi pongo è la definizione di metodi di scambio files che consentano una certa interattività con l'utente, cioè l'utenza deve poter stabilire, dal lato server, i parametri di ascolto in rete ed il file da trasmettere, dal lato client dove salvare il file ricevuto; da entrambi i lati, inoltre, l'utente dovrà essere puntualmente informato dello stato dei processi in corso. Il tutto, comunque, mirato ad un utilizzo in una lan locale.

Per fare ciò, dal lato client sarà sufficiente una finestra minimale, con il giusto necessario di controlli; il lato server sarà un attimino più articolato, avendo stabilito si debba poter decidere i parametri di rete da utilizzarsi, motivazioni di snellezza e riutilizzo mi fanno propendere alla definizione di una finestra specializzata allo scopo.

Pertanto, il "server" sarà dotato di due finestre, una principale definente i metodi relativi alle iterazioni per selezione e trasmissione del file, oltre che alle comunicazioni dei processi

png

ed una finestra sussidiaria per la selezione dell'indirizzo e porta di ascolto

png

come detto, dal lato "client" utilizzeremo una unica finestra, più o meno simile e speculare, nelle funzioni, a quella del server

png

Dal punto di vista delle azioni e relazioni corrispondenti tra le Gui esposte, ho cercato di schematizzarle nel diagramma che segue, spero sia sufficientemente chiaro, comunque cercherò di dare più puntuale idea quando ne affronterò l'implementazione

png



Per ora chiudo qui, nel prossimo post cercherò di affrontare l'architettura del server.

Edited by nuzzopippo - 6/1/2016, 12:44
 
Web  Top
view post Posted on 7/12/2015, 15:28
Avatar

Nubbio x Sempre

Group:
Moderazione globale
Posts:
7,226

Status:


PARTE 2 di 3 : Il server - Rete di ascolto



Introduzione generale



In questa serie di post vedremo le funzionalità lato "Server" che imposteremo nel nostro processo di test per lo scambio di un file in modalità grafica; si richiama la finestra principale già mostrata prima

png

Di per se la finestra è semplicissima, 4 bottoni per i comandi, una etichetta e, in fine, una progress bar ed un'area di testo, non editabile, che utilizzeremo per comunicare all'utente la fase in corso del processo e l'avanzamento della trasmissione del file.

In primo luogo, è necessario effettuare delle considerazioni sulla "disponibilità" dei comandi da concedersi all'utente; in effetti non possono rendersi disponibili i processi per la trasmissione del file se non è stato prima stabilito quale sia il file da trasmettere e la porta di comunicazione da utilizzarsi ... banalissima considerazione, lo so, "chi si sognerebbe mai di trasmettere un file senza prima averlo scelto?", domanda giusta e lecita ma l'esperienza insegna che i limiti dell'utonto trascendono l'infinito, lasciare possibile un processo illogico, per quanto impensabile, comporta che prima o poi qualcuno lo userà, magari solo perché è curioso come una scimmia, ed allora sarà il Vostro programma a non funzionare adeguatamente (cosa per altro vera) e non lo stupido comportamento dell'utente la motivazione del problema.
Quindi, condizioneremo il comportamento della nostra finestra in base allo stato di definizione di alcune variabili contenenti i dati necessari al nostro scopo, cioè indirizzo e porta di ascolto e nome del file da trasmettere.

Definite le seguenti variabili :
CODICE
private String indirizzo = "";
   private int porta = 0;
   private String nomeFile = "";


controlleremo, quindi, lo stato di accessibilità dei bottoni di comando condizionandolo alle tre variabili, giusto per marcare la doverosa espressione di volontà all'avvio dei processi condizioneremo la possibilità di selezionare un file alla definizione della porta di ascolto. Per far ciò definiremo un metodo richiamabile ad ogni variazione di stato delle variabili, siano "btApri" il nome del bottone per la selezione del file da inviare, "btInvia" preposto alla trasmissione e "resetComandi()" il metodo, privato, della GUI preposto, scriveremo :
CODICE
private void resetComandi() {
       if (porta <= 0) {
           btApri.setEnabled(false);
       } else {
           btApri.setEnabled(true);
       }
       if (porta == 0 || nomeFile.equals("")) {
           btInvia.setEnabled(false);
       } else {
           btInvia.setEnabled(true);
       }
   }


Noterete come ho ignorato completamente la variabile "indirizzo", che pure ho definito quale "necessaria", lo è ma l'utente deve avere il potere di decidere se l'applicazione deve ascoltare su tutte le sottoreti disponibili ovvero su di una specifica. Sarà utilizzata in fase di implementazione del ServerSocket.


Definizione della rete



Effettueremo la definizione delle condizioni di ascolto in rete della nostra applicazione server tramite una finestra specializzata che utilizzerà gli oggetti "NetworkInterface" e "InetAddress" della libreria standard di java "java.net", in sostanza applicheremo in modalità grafica i concetti base già espressi in questo precedente post.

png

Preciso che nella implementazione che segue tale finestra è un oggetto JFrame ma avrebbe potuto benissimo essere un JDialog, forse sarebbe stato anche più opportuno, vedremo in una eventuale successiva evoluzione dll'argomento, la scelta discende solo da mie radicate abitudini.

Tale finestra, nella sua apparente semplicità, è più complessa della principale, noterete, in particolare la tebella contenente i dati essenziali delle interfacce di rete disponibili presuppone un modello di tabella specifico e, per comodità, un array di oggetti contenitori dei selezionati dati descrittivi dei singoli indirizzi di rete.

Inoltre, per questioni di mera comunicazione tra finestre, nel costruttore dello JFrame ho previsto, quale parametro, il passaggio della finestra chiamante la quale ha definito un metodo pubblico finalizzato alla impostazione dei dati di rete.
Il codice di chiamata della finestra di definizione della rete :
CODICE
JButton btRete = new JButton("Rete ...");
       btRete.addActionListener(new ActionListener() {
               public void actionPerformed(ActionEvent arg0) {
               SelRete frame = new SelRete(Gui.this);
               frame.setVisible(true);
               }
       });


Come potete vedere dal codice, non si tratta altro che della dichiarazione di un oggetto "SelRete", un JFrame, parametrizzato con un oggetto GUI, un altro JFrame, che altro non è che la finestra chiamante.

Rilevazione dei dati di rete



Operazione che deve essere svolta una singola volta nell'arco di vita della finestra,
pertanto è opportuno procedervi nel costruttore stesso del JFrame.
Tale operazione andrà svolta in due fasi :
  1. nella prima fase bisogna ottenere le schede di rete presenti nel sistema, per averle utilizzeremo il metodo "getNetworkInterfaces()" dell'oggetto "NetworkInterface" che restituirà una collezione di oggetti "NetworkInterface" rappresentanti ogni singola scheda di rete dimensionata nel sistema
  2. nella srconda fase interrogheremo ogni singola scheda definita nel sistema in maniera da ottenere gli indirizzi di rete associati, per farlo useremo il metodo "getInetAddresses()" di NetworkInterface che restituirà una collezione di oggetti "InetAddress" che potremo interrogare per ottenere le specifiche caratteristiche di rete.
Le collezioni restituite sono della tipogia "Enumeration".

Dal punto di vista "pratico", non occorre fare altro che definire i metodi per porre in essere le due fasi prima descritte, azione che potrebbe facilmente essere svolta in un paio di cicli "for" ma, per questione di mera chiarezza e manutenzione del codice, ritengo opportuno scindere in due metodi specializzati, il primo dedicato ad ottenere le schede di rete presenti nel sistema :
CODICE
private void vediRete() {
       indirizzi.clear();
       try {
           Enumeration<NetworkInterface> schede = NetworkInterface.getNetworkInterfaces();
           if (schede != null) {
               for (NetworkInterface scheda : Collections.list(schede)) {
                   aggiungiScheda(scheda);
               }
           }
       } catch (SocketException ex) {
           String errore = ex.getMessage();
           javax.swing.JOptionPane.showMessageDialog(this, errore, "Errore nel rilevamento rete", javax.swing.JOptionPane.ERROR_MESSAGE);
       }
       setTBL();
   }


Il secondo, invece, si fa carico della interrogazione delle singole interfacce di rete per ottenere gli indirizzi associati e procedere alla memorizzazione dei dati di rete :
CODICE
private void aggiungiScheda(NetworkInterface scheda) throws SocketException {
       // scarta le schede non attive
       if (!scheda.isUp()) { return; }
       Enumeration<InetAddress> inAddrs = scheda.getInetAddresses();
       for (InetAddress addr : Collections.list(inAddrs)) {
           DescrInd ind = new DescrInd();
           ind.setScheda(scheda.getDisplayName());
           if (addr instanceof Inet4Address) {
               ind.setTipo("IPV4");
           } else if (addr instanceof Inet6Address) {
               ind.setTipo("IPV6");
           } else {
               ind.setTipo("???");
           }
           ind.setIndirizzo(addr.getHostAddress());
           indirizzi.add(ind);
       }
   }


Il codice è abbastanza banale, nel metodo privato "vediRete()" non si fa altro che ottenere l'elenco delle schede di rete presenti nel sistema per poi passarle una ad una al metodo "aggiungiScheda(NetworkInterface scheda)" per la rilevazione dati.
"aggiungiScheda()" esce subito se la scheda di rete non attive (.isUp() == false) altrimenti provvede ad interrogare la scheda per ottenerne gli indirizzi di rete associati.
Inoltre, si valuta il tipo di indirizzo di rete, ho previsto le sole sole reti TCP-IP versioni 4 e 6 (non essedomene interessato, al momento non saprei se possono rilevarsi altre categorie), la verifica viene fatta controllando se lo InetAddress corrente nel ciclo è una istanza delle classi "Inet4Address" o "Inet6Address" lasciando indeterminate eventuali altre tipologie.

I dati rilevati sono dati in pasto ad un oggetto "DescrInd", creato apposta per la memorizzazione e restituzione di tali dati, che viene aggiunto ad un ArrayList di tali oggetti, "indirizzi", utilizzato da un modello di tabella, appositamente definito, che popola la tabella della finestra di selezione della rete di ascolto, aggiornata dal metodo "setTBL()" richiamato dal metodo vediRete alla fine della analisi delle interfacce di rete.
A questo punto sarà sufficiente selezionare la riga di tabella contenente l'indirizzo voluto per poterne trasmettere i dati tramite il gestore degli eventi del bottone "Trasmetti" che richiama il metodo privato "trasmettiIndirizzo()", il codice relativo :

CODICE
private void trasmettiIndirizzo() {
           boolean noRete = chkRete.isSelected();
       if (!noRete) {
           if (tbRete.getSelectedRow() == -1) { return; }
           if (indirizzi.get(tbRete.getSelectedRow()).getIndirizzo() == null) { return; }
       }
       int porta;
       try {
           porta = Integer.parseInt(txtPorta.getText());
       } catch(NumberFormatException e) {
           javax.swing.JOptionPane.showMessageDialog(this, "La porta indicata non è un numero valido", "Porta di ascolto errata",
                   javax.swing.JOptionPane.ERROR_MESSAGE);
           txtPorta.requestFocus();
           return;
       }
       if (porta < 1025 || porta > 65535) {
           javax.swing.JOptionPane.showMessageDialog(this, "La porta indicata è fuori del range ammesso", "Porta di ascolto errata",
                   javax.swing.JOptionPane.ERROR_MESSAGE);
           txtPorta.requestFocus();
           return;
       }
       if (noRete) {
           mater.setAddress("", porta);
       } else {
           mater.setAddress(indirizzi.get(tbRete.getSelectedRow()).getIndirizzo(), porta);
       }
       this.dispose();
   }



Anche se di semplice logica, tale metodo necessita di una qualche spiegazione.

In primo luogo viene valutata l'intenzione dell'utente di definire o meno una rete di ascolto, tale intenzione viene espressa dalla selezione del checkbox "Ignora indirizzo rete" che, si badi bene, NON indica che il servizio sarà fornito solo localmente all'host ma esattamente l'opposto perché sarà fornito da qualsiasi parte arrivi, indipendentemente dall'indirizzo di rete, per rendere il servizio locale all'host in uso bisognerà selezionare l'interfaccia di rete relativa, quella corrente in figura nel mio caso, che di norma corrisponde all'indirizzo di loopback "127.0.0.0" o, più frequentemente, "127.0.0.1"

Un elemento fondamentale da stabilire è la porta di comunicazione su cui deve porsi in ascolto il server, in una interfaccia di rete TCP-IP ne sono possibili 65.535, in parte riservate, il metodo verifica che nella casella di testo relativa sia valorizzato un numero intero ricadente nel range di porte disponibili, in caso contrario non procede, per poi invocare il metodo pubblico, della gui chiamante, "setAddress(String indirizzo, int porta)", in modalità diversificata a seconda della scelta utente circa l'ascolto in rete.

L'atto finale del processo è la chiusura della finestra, ormai non più occorrente, restituendo il controllo alla gui principale.

Di seguito il codice completo dei vari oggetti interagenti per la definizione della rete da utilizzarsi :

DescrInd : contenitore dati utili di rete



CODICE
public class DescrInd {
   private String scheda = "";
   private String tipo = "";
   private String indirizzo = "";
   private int porta = 0;
   
   public void setScheda(String valore) {
       scheda = valore;
   }
   public String getScheda() {
       return scheda;
   }
   
   public void setTipo(String valore) {
       tipo = valore;
   }
   public String getTipo() {
       return tipo;
   }
   
   public void setIndirizzo(String valore) {
       indirizzo = valore;
   }
   public String getIndirizzo() {
       return indirizzo;
   }
   
   public void setPorta(int valore) {
       porta = valore;
   }
   public int getPorta() {
       return porta;
   }
}



SchedeTM : modello di tabella



CODICE
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;

@SuppressWarnings("serial")
public class SchedeTM extends AbstractTableModel  {
   /**Costruttore di inizializzazione, lista vuota
    *
    */
   public SchedeTM() { lista = new ArrayList<DescrInd>(); }
   /**Costruttore operativo, si attende la lista di indirizzi da mostrare
    *
    * @param lista
    */
   public SchedeTM(ArrayList<DescrInd> lista) {
       this.lista = lista;
   }
   /**Restituisce il nome di una colonna
    *
    * @param colonna
    * @return
    */
   @Override
   public String getColumnName(int colonna) {
       return nomiColonna[colonna];
   }

   @Override
   public int getColumnCount() {
       return nomiColonna.length;
   }

   @Override
   public int getRowCount() {
       return lista.size();
   }

   @Override
   public Object getValueAt(int riga, int colonna) {
       if (lista.get(riga) == null) {
           return null;
       } else {
           switch (colonna) {
               case 0:
                   return lista.get(riga).getScheda();
               case 1:
                   return lista.get(riga).getIndirizzo();
               case 2:
                   return lista.get(riga).getTipo();
               default:
                   return null;
           }
       }
   }

   private String[] nomiColonna = {
       "Scheda", "Indirizzo", "Tipo"};    
   private ArrayList<DescrInd> lista = null;    
}



SelRete : Jframe, finestra di selezione della rete



CODICE
import java.awt.EventQueue;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import net.miginfocom.swing.MigLayout;

import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JCheckBox;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;


@SuppressWarnings("serial")
public class SelRete extends JFrame {

       private JPanel contentPane;
       private JTextField txtPorta;
       private JTable tbRete;
       JCheckBox chkRete;
       
   ArrayList<DescrInd> indirizzi = new ArrayList<DescrInd>();
   Gui mater = null;        

       /**
        * Launch the application.
        */
       public static void main(String[] args) {
               EventQueue.invokeLater(new Runnable() {
                       public void run() {
                               try {
                                       SelRete frame = new SelRete(null);
                                       frame.setVisible(true);
                               } catch (Exception e) {
                                       e.printStackTrace();
                               }
                       }
               });
       }

       /**
        * Create the frame.
        */
       public SelRete(Gui mamma) {
               mater = mamma;
               setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
               setBounds(100, 100, 450, 300);
               contentPane = new JPanel();
               contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
               setContentPane(contentPane);
               contentPane.setLayout(new MigLayout("", "[grow][][grow][]", "[][grow][][][][]"));
               
               JScrollPane scrollPane = new JScrollPane();
               contentPane.add(scrollPane, "cell 0 0 4 0,grow");
               
               tbRete = new JTable();
               scrollPane.setViewportView(tbRete);
               
               JLabel lblNewLabel = new JLabel("Porta :");
               contentPane.add(lblNewLabel, "cell 0 1,alignx trailing");
               
               txtPorta = new JTextField();
               txtPorta.setText("9999");
               contentPane.add(txtPorta, "cell 0 1,growx");
               txtPorta.setColumns(10);
               
               chkRete = new JCheckBox("Ignora indirizzo rete");
               contentPane.add(chkRete, "cell 2 1 3 1");
               
               JButton btnTrasmetti = new JButton("Trasmetti");
               btnTrasmetti.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent arg0) {
                               trasmettiIndirizzo();
                       }
               });
               contentPane.add(btnTrasmetti, "cell 0 3");
               
               JButton btnChiudi = new JButton("Chiudi Finestra");
               btnChiudi.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                               SelRete.this.dispose();
                       }
               });
               contentPane.add(btnChiudi, "cell 3 3");
               
               avvia();
       }
       
       private void avvia() {
               //if (mater == null) { return; }
               vediRete();
       }
       
   // Rileva le schede di rete nel sistema
   private void vediRete() {
       indirizzi.clear();
       try {
           Enumeration<NetworkInterface> schede = NetworkInterface.getNetworkInterfaces();
           if (schede != null) {
               for (NetworkInterface scheda : Collections.list(schede)) {
                   aggiungiScheda(scheda);
               }
           }
       } catch (SocketException ex) {
           String errore = ex.getMessage();
           javax.swing.JOptionPane.showMessageDialog(this, errore, "Errore nel rilevamento rete", javax.swing.JOptionPane.ERROR_MESSAGE);
       }
       setTBL();
   }
   
   // Lista gli indirizzi di rete associati ad una scheda
   private void aggiungiScheda(NetworkInterface scheda) throws SocketException {
       // scarta le schede non attive
       if (!scheda.isUp()) { return; }
       Enumeration<InetAddress> inAddrs = scheda.getInetAddresses();
       for (InetAddress addr : Collections.list(inAddrs)) {
           DescrInd ind = new DescrInd();
           ind.setScheda(scheda.getDisplayName());
           if (addr instanceof Inet4Address) {
               ind.setTipo("IPV4");
           } else if (addr instanceof Inet6Address) {
               ind.setTipo("IPV6");
           } else {
               ind.setTipo("???");
           }
           ind.setIndirizzo(addr.getHostAddress());
           indirizzi.add(ind);
       }
   }
   
   private void trasmettiIndirizzo() {
           boolean noRete = chkRete.isSelected();
       if (!noRete) {
           if (tbRete.getSelectedRow() == -1) { return; }
           if (indirizzi.get(tbRete.getSelectedRow()).getIndirizzo() == null) { return; }
       }
       int porta;
       try {
           porta = Integer.parseInt(txtPorta.getText());
       } catch(NumberFormatException e) {
           javax.swing.JOptionPane.showMessageDialog(this, "La porta indicata non è un numero valido", "Porta di ascolto errata",
                   javax.swing.JOptionPane.ERROR_MESSAGE);
           txtPorta.requestFocus();
           return;
       }
       if (porta < 1025 || porta > 65535) {
           javax.swing.JOptionPane.showMessageDialog(this, "La porta indicata è fuori del range ammesso", "Porta di ascolto errata",
                   javax.swing.JOptionPane.ERROR_MESSAGE);
           txtPorta.requestFocus();
           return;
       }
       if (noRete) {
           mater.setAddress("", porta);
       } else {
           mater.setAddress(indirizzi.get(tbRete.getSelectedRow()).getIndirizzo(), porta);
       }
       this.dispose();
   }
   
   private void setTBL() {
       tbRete.setModel(new SchedeTM(indirizzi));
   }

}



Le operazioni "di rete" sono concluse, il prossimo post riguarderà l'implementazione del ServerSocket tramite uno SwingWorker, sarà il cuore del processo server. Alla prox :D

Edited by nuzzopippo - 8/1/2016, 09:47
 
Web  Top
view post Posted on 14/1/2016, 16:55
Avatar

Nubbio x Sempre

Group:
Moderazione globale
Posts:
7,226

Status:


Rileggendo il precedente post, mi sono accorto di acer omesso di descrivere cosa avviene sulla GUI chiamante quando viene trasmesso l'indirizzo e porta di ascolto da utilizzarsi tramite il metodo pubblico "setAddress(String ind, int porta)", vengono semplicemente valorizzate due variabili globali alla classe GUI, emesso il messaggio relativo al posizionamento di ascolto e ricalibrati i controlli della form con il metodo "resetComandi()" già anticipato.
il codice è semplicissimo ed auto-esplicativo, una sua lettura ritengo sia più che sufficiente

CODICE
/**Reimposta l'indirizzo IP su cui ascoltare le richieste di connessione
    * per l'invio del file da scambiarsi.
    *
    * @param ind l'indirizzo IP su cui ascoltare, se nullo l'ascolto è generalizzato
    * @param porta la porta di ascolto.
    */
   public void setAddress(String ind, int porta) {
       this.indirizzo = ind;
       this.porta = porta;
       if(!ind.equals("")) {
               tMsg.append("Indirizzo di ascolto : " + ind + " sulla porta " + porta +"\n");
       } else {
           tMsg.append("Ascolto sulla porta : " + porta +"\n");
       }
       resetComandi();
   }



PARTE 2 di 3 : Il server - selezione file, il "Worker"



Una volta valorizzata la porta di ascolto, il metodo privato, del JFrame GUI, "resetComandi()" abiliterà il pulsante btApri, individuabile dall'etichetta "Apri" in figura nel precedente post, il cui lister di eventi rende disponibile una finestra di dialogo per la selezione, nel filesystem del server, del file da inviare, anche qui credo che la lettura dello stralcio di codice interessato sia esaustiva

CODICE
btApri.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent arg0) {
                       /* Richiama una finestra per la selezione del file da inviare, il
                        * path del file verrà memorizzato nella variabile locale "nomeFile" e
                        * ridisegnati i controlli
                        */
                       javax.swing.JFileChooser dialogo = new javax.swing.JFileChooser();
                       // si imposta alla home la directory di inizio ricerca
                       dialogo.setCurrentDirectory(new java.io.File(System.getProperties().getProperty("user.home")));
                       // impostazione titolo finestra di dialogo
                       dialogo.setDialogTitle("Selezione file da trasmettere");
                       // attiva la finestra di dialogo e ne ottiene la condizione di uscita
                       int res = dialogo.showOpenDialog(Gui.this);
                       if (res == javax.swing.JFileChooser.APPROVE_OPTION) {
                           try {
                               nomeFile = dialogo.getSelectedFile().getCanonicalPath();
                               lblFile.setText(dialogo.getSelectedFile().getName());
                               tMsg.append("File da trasmettere : " + nomeFile + "\n");
                           } catch (IOException e) {
                               tMsg.append("Errore nella selezione del file : " + e.getMessage() + "\n");
                               nomeFile = "";
                           }
                           resetComandi();
                       }
                       }
               });



Come si evince facilmente dal codice, non viene fatto altro che istanziare un oggetto "JFileChooser()", usare i metodi propri dell'oggetto per definirne il titolo e la direttrice da cui partire, puntata alla home dell'utente letta dalle proprietà di sistema (istruzione "System.getProperties().getProperty("user.home")") e quindi aprirlo col metodo "showOpenDialog(Jframe Frame)".
Ovviamente, si procede a verificare che l'utente abbia effettivamente selezionato un file prima di incamerarne il nome "completo" nel sistema server (".getCanonicalPath()") assegnandolo alla variabile dedicata, informare della selezione effettuata e procedere a resettare i comadi.
Da notare, che la modalità di apertura adottata (più o meno quella di default) NON permette la selezione di una directory, cosa che vedremo alla stesura del client, ma esclusivamente di files veri e propri ... al momento la cosa va benissimo, perché la trasmissione di un singolo file è il target di questo post, magari in una futura variante si studieranno le strategie per la trasmissione di interi pacchetti di files e direttrici.

Comunque, fatto salvo il verificarsi di errori, il nome file selezionato valorizzerà la variabile specifica ("nomeFile"), la qual cosa permetterà a resetComandi() (ultima istruzione nel listener) di abilitare le funzionalità del pulsante dedicato alla trasmissione del file, "btInvia", riconoscibile dalla scritta "Invia" nella finestra del precedente post ... qui le cose sono un attimino più complicate, rispetto a quanto visto sin ora.

Come già anticipato nella parte iniziale del primo post, qui si vuole realizzare una interfaccia utente che sia in grado di reagire anche in presenza di processi pesanti (pensate un po' alla trasmissione della ISO di un DVD), ciò malgrado l'univocità del Thread EDT (Event Dispatcher Thread), quello in cui gira swing per capirci, tale univocità, in presenza di elaborazioni che richiedono un certo tempo, causerebbe il congelamento della finestra fino al termine della elaborazione avviata.

Trasmissione del file : il worker




Detta scomodissima caratteristica, non presente in AWT, ha costretto i poveri programmatori a salti mortali sino al JDK 6, ove è stata introdotta la classe "SwingWorker", tale classe permette di avviare dei thread paralleli allo EDT di swing, superando così il congelamento della interfaccia utente, con un certo grado di comunicazione con quest'ultimo, cosa che rende possibile l'aggiornamento della finestra in relazione allo stato della elaborazione "pesante" in corso ... è una specie di coordinatore tra thread, in sostanza.

Vi invito caldamente a consultare la documentazione (e i tutorial) di Oracle in merito, SwingWorker è una classe complicatuccia che, oltre tutto, ho affrontato per la prima volta nello esempio in post, qui vi dirò come mi è riuscito di implementare, sulla base di quanto ho capito, un oggetto SwingWorker funzionante che gestisce il ServerSocket per la trasmissione del file aggiornando la finestra principale con l'avanzamento delle operazioni.

In primo luogo Vi invito a guardare la definizione della classe nella docs : Class SwingWorker<t,V>
la classe possiede due parametri indeterminati come tipo dei quali il primo, T, indica il tipo di valore che sarà restituito alla conclusione del processo parallelo allo EDT avviato, il secondo V indica un eventuale valore parziale che sarà emesso nel corso della elaborazione.
Volendo trasmettere il file scelto dall'utente ed informarlo sulla trasmissione in corso, senza bloccare la finestra durante la trasmissione, ne consegue in primo luogo che voglio sapere l'esito della trasmissione e, secondariamente, lo stato di avanzamento della trasmissione stessa, cosa che può essere fatta comodamente con una progress bar ... in entrambi i casi, dei valori interi vanno benissimo per lo scopo, chiamando SendWorker la nostra classe la sua dichiarazione sarà :
CODICE
public class SendWorker extends SwingWorker<Integer, Integer> {


Ovviamente il nostro worker dovrà conoscere l'indirizzo di rete e la porta di ascolto scelti dall'utente, oltre che il file da inviare, poi, dato che ci piace essere precisi, potremmo anche voler informare dello stato delle operazioni nelle varie fasi, oltre che dello avanzamento, potremo facilmente farlo in una text area. Dovremo, quindi prevedere nel nostro worker delle variabili non modificabili da valorizzarsi con i parametri passati al costruttore, come nello stralcio di codice seguente :
CODICE
private final String file;
   private final JTextArea inf;
   private final int porta;
   private final String indirizzo;
   
   private ServerSocket ss = null;
   private Socket s = null;
   
   /**Costruisce il server per invio di un file
    *
    * @param ind - indirizzo di rete
    * @param porta - porta di ascolto
    * @param file - canonical name del file da inviare
    * @param text - area di testo per messaggi
    * @throws Exception - in caso di errori o interruzioni
    */
   public SendWorker(String ind, int porta, String file, JTextArea text) throws Exception {
       this.indirizzo = ind;
       this.file = file;
       this.inf = text;
       this.porta = porta;
   }


Da notarsi come nel costruttore del nostro SendWorker sia stata prevista la clausola "throws", ciò è motivato dal fatto che un thread SwingWorker può venire interrotto, pur se qui non è implementata interruzione a livello software è comunque bene prevedere tale circostanza.

Dal punto di vista funzionale del nostro worker adotteremo la metodologia già visitata in questo post, che Vi invito a leggere, dato che non ritratterò qui i concetti già espressi, ovviamente adattandola al contesto ed alle scelte effettuate dall'utente.

In primo luogo dovremo agire diversamente nei casi in cui l'utente abbia scelto di agire su di una specifica rete ovvero indipendentemente da specifiche interfacce, utilizzando un diversificato avvio del ServerSocket che, nel primo caso avrà necessariamente bisogno dello specifico InetAddress associato all'indirizzo di rete scelto dall'utente, è mia abitudine atomizzare le unità funzionali di attività secondo compiti specifici, per questioni di manutenzione e riciclo del codice, pertanto ho previsto un metodo privato, getInetA(), che adotta tecniche analoghe a quelle viste per l'esposizione dei dati di rete e restituisce lo InetAddres trovato:

CODICE
private InetAddress getInetA() {
       try {
           Enumeration<NetworkInterface> schede = NetworkInterface.getNetworkInterfaces();
           if (schede != null) {
               for (NetworkInterface scheda : Collections.list(schede)) {
                   Enumeration<InetAddress> inAddrs = scheda.getInetAddresses();
                   for (InetAddress addr : Collections.list(inAddrs)) {
                       if(indirizzo.equals(addr.getHostAddress())) {
                           return addr;
                       }
                   }
               }
           }
       } catch (SocketException ex) {
           String errore = ex.getMessage();
           javax.swing.JOptionPane.showMessageDialog(null, errore, "Errore nel rilevamento rete", javax.swing.JOptionPane.ERROR_MESSAGE);
       }
       return null;
   }


Metodo che con qualche leggera modifica potrebbe anche essere inserito in una propria libreria di utilità.

Analogamente, ho preferito isolare anche le operazioni ascolto e connessione del ServerSocket, in un apposito metodo, che restituisce il Socket per la avvenuta connessione, al fine di poter gestire adeguatamente eventuali condizioni di errore :

CODICE
private Socket connetti() {
       Socket sock;
       try {
           if (indirizzo.equals("")) {
               ss = new ServerSocket(porta);
           } else {
               ss = new ServerSocket(porta, 0, getInetA());
           }
           inf.append("* Attendo un client\n");
           sock = ss.accept();
       } catch (UnknownHostException e) {
           inf.append("Errore : " + e.getMessage() + "\n");
           return null;
       } catch (IOException e) {
           inf.append("Errore : " + e.getMessage() + "\n");
           return null;
       }
       return sock;
   }


Si noti come entrambi i metodi utilizzino la text area ricevuta in parametro (inf) per aggiungerci i loro messaggi, anche in caso di errore, tali messaggi saranno aggiunti dallo ETD anche in pendenza di processi del worker, aggiornando l'interfaccia utente.

Si noti, altresì come viene definito diversamente il ServerSocket a seconda se sia stato definito o meno un indirizzo IP di ascolto da parte dell'utente, in particolare, l'istruzione
CODICE
ss = new ServerSocket(porta, 0, getInetA());

richiama il metodo getInetA() prima definito direttamente nella costruzione del ServerSocket e potrebbe causare un errore che verrebbe intercettato causando la restutuzione di un Socket nullo.

la classe SwingWorker ha un metodo particolare che deve essere obbligatoriamente sovrascritto nella implementazione di un oggetto SwingWorker, tale metodo è "doInBackground()" e rappresenta il metodo di avvio del thread parallelo allo EDT; esso deve restituire un valore analogo al parametro "T" impostato nella definizione della classe, tale valore, nel caso implementato, viene utilizzato per comunicare lo stato di uscita del processo, vediamo un po' il codice relativo :

CODICE
@Override
   protected Integer doInBackground() {
       s = connetti();
       if (s == null) {
           return -1;
       }
       inf.append("** Connesso ad host " + s.getInetAddress().getHostAddress() + "\n");
       File f = new File(file);
       String nome = f.getName();
       long dim = f.length();
       try {
           send_file(file, nome, dim);
       } catch (Exception e) {
           inf.append("Errore trasmissione : " + e.getMessage());
           return -2;
       }
       return 0;
   }



Come è evidente dal codice, il Thread del nostro worker utilizza immediatamente il metodo "connetti()" precedentemente visto per mettersi in ascolto, ottenendo il Socket per la comunicazione ad avvenuta richiesta di connessione, in caso di fallimento della connessione, connetti() restituisce un valore nullo ed in tale circostanza il thread viene immediatamente chiuso con restituzione di valore "-1", indicante la condizione di errore.
Ottenuto un canale di comunicazione valido (il Socket) il processo provvede ad aprire il file da trasmettere estraendone il nome e la dimensione in byte per poi dare il tutto in pasto al metodo privato "send_file(String path_file, String nome, long dim_file)" che in maniera del tutto analoga a quella già vista nel post prima indicato esegue l'effettiva trasmissione del file alla cui conclusione chiude restituendo "0" se tutto è andato bene o "-2" se si sono avuti errori nella trasmissione.
Il processo chiamante, analizzando il valore restituito da doInBackground() potrà, volendo, eseguire azioni conseguenziali all'esito avuto.

Naturalmente, sarà il metodo "send_file(...)" a provvedere ad informale sullo stato di avanzamento del trasferimento dati, lo fa utilizzando il metodo "setProgress(int progress)" della classe SwingWorker; il frammento di codice relativo :
CODICE
while((read = fis.read(buff)) != -1) {
               tot += read;
               oos.write(buff, 0, read);
               oos.flush();
               progr = (int) (tot * 100 / dim);
               setProgress(progr);
           }

Come si evince dal codice, nel ciclo di trasmissione dati viene calcolata la percentuale intera dei dati trasmessi fornendola a setProcess().
L'invocazione di setProcess() causa una notifica allo EDT di variazione delle proprietà intercettabile nella finestra chiamante il worker, vedremo tra poco come, del tutto analoga a quella che sarebbe data dal metodo "publish()" di SwingWorker, qui non utilizzato.

Per quanto riguarda il nostro oggetto SendWorker, di classe SwingWorker, l'essenziale per la sua costruzione è stato detto, il resto sono ammenicoli di contorno la cui essenza è già trattata nel post prima citato (se non l'avete già fatto leggetevelo!), vi sono ancora da fare delle codifiche a livello di processo chiamante che vedremo appresso.

Poco fa abbiamo detto che l'utilizzo del metodo setProcess() notifica un cambio di proprietà (ovviamente dell'oggetto SendWorker) allo EDT che può essere intercettato, l'intercettazzione può effettuarsi tramite un "PropertyChandeListener()", da applicarsi ad una istanza di SendWorker, vedremo in seguito come, c'è ancora da chiarire in qual modo si possa intercettare il valore di uscita (T) di doInBackground(), esso può essere fatto in vari modi, ciclado con i metodi get() di SwingWorker p.e., oppure intercettandolo nel metodo "done()" che viene immediatamente eseguito nello EDT alla uscita di doInBackground(), possiamo, quindi, sovrascrivere tale metodo "done()" intercettando detto valore di uscita.
Per questioni di comodità (disponibilità di variabili e controlli) tale implementazione può essere fatta già all'interno del lister del pulsante di comando relativo all'avvio ... ciò che ho fatto ;) :

CODICE
btInvia = new JButton("Invia");
               btInvia.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                               btInvia.setEnabled(false);
                       SendWorker worker = null;
                       PropertyChangeListener listener = new PropertyChangeListener() {
                           @Override
                           public void propertyChange(PropertyChangeEvent event) {
                               if("progress".equals(event.getPropertyName())) {
                                   jpbComFile.setValue((int) event.getNewValue());
                               }
                           }
                       };
                       try {
                           worker = new SendWorker(indirizzo, porta, nomeFile, tMsg) {
                               @Override
                               protected void done() {
                                   try {
                                       int uscita = get();
                                       if (uscita != 0) {
                                           tMsg.append("Trasmissione file fallita\n");
                                       } else {
                                           indirizzo = "";
                                           porta = 0;
                                           nomeFile = "";
                                           lblFile.setText("");
                                           resetComandi();
                                       }
                                   } catch(InterruptedException | ExecutionException e) {
                                       javax.swing.JOptionPane.showMessageDialog(null, e.getMessage(),
                                               "Errore", javax.swing.JOptionPane.ERROR_MESSAGE);
                                   }
                               }
                           };                
                           worker.addPropertyChangeListener(listener);
                       } catch (Exception ex) {
                                       javax.swing.JOptionPane.showMessageDialog(Gui.this, ex.getMessage(),
                                               "Errore", javax.swing.JOptionPane.ERROR_MESSAGE);
                       }
                       worker.execute();                                
                       }
               });



come potete leggere dal codice, il PropertyChangeListener() valuta la proprietà che è stata modificata e, se essa è "progress" provvede ad assegnare ad una JProgressBar (jpbComFile) il nuovo valore della proprietà; da notare il cast "(int)", necessario perche un oggetto "Integer" NON è un int (che è un tipo primitivo)
Per quanto riguarda il metodo "done()" prima detto, esso viene sovrascritto direttamente durante la fase di definizione del nuovo oggetto SendWorker, qui la valutazione del codice di uscita si limita a segnalare una eventuale uscita con errore, senza dettagliare, oppure a reinizializzare variabili e GUI in modo da predisporre ad un eventuale nuovo invio.

Si noti come si sia incapsulata la definizione del nuovo oggetto SendWorker in un blocco "try-catch", il motivo è dato dal fatto che un processo SwingWorker può essere interrotto, come qualsiasi altro thread, e tale interruzione andrebbe gestita ... certo, qui non l'ho fatto, puntando alla verifica della funzionalità di quanto ho fatto, magari si vedrà più in la, se e quando deciderò di affrontare il trasferimento di files multipli o direttrici.

Si noti altresì l'invocazione del metodo "execute()" del nostro oggetto SwingWorker al di fuori di ogni altro blocco di codice, a parte quello di evento del bottone, esso è il punto in cui il thread viene effettivamente avviato, in precedenza il pulsante stesso è stato disabilitato, ad evitare l'avvio contemporaneo di più oggetti in ascolto sulla stessa porta, quindi in conflitto ... sai mai qualche crampo al ditino :lol:

Ok, quello che c'era da dire sull'aspetto "server" credo di averlo detto, segue il codice completo di SendWorker e della finestra chiamante, assieme a quello nel post della finestra di selezione della rete avete il codice completo.

Nel prossimo post tratterò in manierà più succinta il client, basantesi sui concetti già trattati, ciao :D

Codice di SendWorker



CODICE
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Enumeration;

import javax.swing.JTextArea;
import javax.swing.SwingWorker;

public class SendWorker extends SwingWorker<Integer, Integer> {
   
   private final String file;
   private final JTextArea inf;
   private final int porta;
   private final String indirizzo;
   
   private ServerSocket ss = null;
   private Socket s = null;
   
   /**Costruisce il server per invio di un file
    *
    * @param ind - indirizzo di rete
    * @param porta - porta di ascolto
    * @param file - canonical name del file da inviare
    * @param text - area di testo per messaggi
    * @throws Exception - in caso di errori o interruzioni
    */
   public SendWorker(String ind, int porta, String file, JTextArea text) throws Exception {
       this.indirizzo = ind;
       this.file = file;
       this.inf = text;
       this.porta = porta;
   }
   
   @Override
   protected Integer doInBackground() {
       s = connetti();
       if (s == null) {
           return -1;
       }
       inf.append("** Connesso ad host " + s.getInetAddress().getHostAddress() + "\n");
       File f = new File(file);
       String nome = f.getName();
       long dim = f.length();
       try {
           send_file(file, nome, dim);
       } catch (Exception e) {
           inf.append("Errore trasmissione : " + e.getMessage());
           return -2;
       }
       return 0;
   }
   
   private InetAddress getInetA() {
       try {
           Enumeration<NetworkInterface> schede = NetworkInterface.getNetworkInterfaces();
           if (schede != null) {
               for (NetworkInterface scheda : Collections.list(schede)) {
                   Enumeration<InetAddress> inAddrs = scheda.getInetAddresses();
                   for (InetAddress addr : Collections.list(inAddrs)) {
                       if(indirizzo.equals(addr.getHostAddress())) {
                           return addr;
                       }
                   }
               }
           }
       } catch (SocketException ex) {
           String errore = ex.getMessage();
           javax.swing.JOptionPane.showMessageDialog(null, errore, "Errore nel rilevamento rete", javax.swing.JOptionPane.ERROR_MESSAGE);
       }
       return null;
   }
   
   private Socket connetti() {
       Socket sock;
       try {
           if (indirizzo.equals("")) {
               ss = new ServerSocket(porta);
           } else {
               ss = new ServerSocket(porta, 0, getInetA());
           }
           inf.append("* Attendo un client\n");
           sock = ss.accept();
       } catch (UnknownHostException e) {
           inf.append("Errore : " + e.getMessage() + "\n");
           return null;
       } catch (IOException e) {
           inf.append("Errore : " + e.getMessage() + "\n");
           return null;
       }
       return sock;
   }
   
   private void inviaParametri(String nome, long dimensione, ObjectOutputStream oos) {
       String param = nome + "," + dimensione;
       try {
           oos.flush();
           oos.writeObject(param);
           inf.append("Parametri file inviati con successo\n");
       } catch (IOException e) {
           inf.append("Errore : " + e.getMessage() + "\n");
       }        
   }
   
   private void send_file(String path_file, String nome, long dim_file) throws IOException {
       inf.append("* Inizio processo di invio file" + "\n");
       String path = path_file;
       long dim = dim_file;
       FileInputStream fis = null;
       ObjectOutputStream oos;
       try {
           fis = new FileInputStream(path);
           oos = new ObjectOutputStream(s.getOutputStream());
           byte[] buff = new byte[1024];
           int read;
           long tot = 0;
           int progr;
           // invio i dati del file al client
           inviaParametri(nome, dim, oos);
           // inizio fase di lettura
           inf.append("* Inizio invio file di " + dim + " byte\n");
           oos.flush();
           while((read = fis.read(buff)) != -1) {
               tot += read;
               oos.write(buff, 0, read);
               oos.flush();
               progr = (int) (tot * 100 / dim);
               setProgress(progr);
           }
           inf.append("\n");
           inf.append("** File inviato con successo\n");
           oos.close();
       } catch (FileNotFoundException e) {
           inf.append("Errore file" + e.getMessage() + "\n");
       } finally {
           if (fis!= null) {
               fis.close();
           }
           if (s!= null) {
               s.close();
           }
           if (ss != null) {
               ss.close();
           }
       }
   }
}



Codice della GUI principale



CODICE
import java.awt.EventQueue;

import javax.swing.JFrame;

import net.miginfocom.swing.MigLayout;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.border.BevelBorder;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JProgressBar;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;


@SuppressWarnings("serial")
public class Gui extends JFrame {

       /**
        * Launch the application.
        */
       public static void main(String[] args) {
               EventQueue.invokeLater(new Runnable() {
                       public void run() {
                               try {
                                       Gui window = new Gui();
                                       window.setVisible(true);
                               } catch (Exception e) {
                                       e.printStackTrace();
                               }
                       }
               });
       }

       /**
        * Create the application.
        */
       public Gui() {
               initialize();
               resetComandi();
       }
       
       // controlli che devono essere visibili dai processi
       private JTextArea tMsg;
       private JLabel lblFile;
       private JProgressBar jpbComFile;
       private JButton btApri;
       private JButton btInvia;

       /**
        * Initialize the contents of the frame.
        */
       private void initialize() {
               setTitle("Server scambio file");
               setBounds(100, 100, 376, 300);
               setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               getContentPane().setLayout(new MigLayout("", "[][grow][][][]", "[grow][][][]"));
               
               JScrollPane scrollPane = new JScrollPane();
               getContentPane().add(scrollPane, "cell 0 0 5 0,grow");
               
               tMsg = new JTextArea();
               tMsg.setLineWrap(true);
               scrollPane.setViewportView(tMsg);
               
               btApri = new JButton("Apri");
               btApri.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent arg0) {
                       /* Richiama una finestra per la selezione del file da inviare, il
                        * path del file verrà memorizzato nella variabile locale "nomeFile" e
                        * ridisegnati i controlli
                        */
                       javax.swing.JFileChooser dialogo = new javax.swing.JFileChooser();
                       // si imposta alla home la directory di inizio ricerca
                       dialogo.setCurrentDirectory(new java.io.File(System.getProperties().getProperty("user.home")));
                       // impostazione titolo finestra di dialogo
                       dialogo.setDialogTitle("Selezione file da trasmettere");
                       // attiva la finestra di dialogo e ne ottiene la condizione di uscita
                       int res = dialogo.showOpenDialog(Gui.this);
                       if (res == javax.swing.JFileChooser.APPROVE_OPTION) {
                           try {
                               nomeFile = dialogo.getSelectedFile().getCanonicalPath();
                               lblFile.setText(dialogo.getSelectedFile().getName());
                               tMsg.append("File da trasmettere : " + nomeFile + "\n");
                           } catch (IOException e) {
                               tMsg.append("Errore nella selezione del file : " + e.getMessage() + "\n");
                               nomeFile = "";
                           }
                           resetComandi();
                       }
                       }
               });
               getContentPane().add(btApri, "cell 0 1,alignx left");
               
               lblFile = new JLabel("Nessun file selezionato");
               lblFile.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
               getContentPane().add(lblFile, "cell 1 1 3 1,growx");
               
               btInvia = new JButton("Invia");
               btInvia.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                               btInvia.setEnabled(false);
                       SendWorker worker = null;
                       PropertyChangeListener listener = new PropertyChangeListener() {
                           @Override
                           public void propertyChange(PropertyChangeEvent event) {
                               if("progress".equals(event.getPropertyName())) {
                                   jpbComFile.setValue((int) event.getNewValue());
                               }
                           }
                       };
                       try {
                           worker = new SendWorker(indirizzo, porta, nomeFile, tMsg) {
                               @Override
                               protected void done() {
                                   try {
                                       int uscita = get();
                                       if (uscita != 0) {
                                           tMsg.append("Trasmissione file fallita\n");
                                       } else {
                                           indirizzo = "";
                                           porta = 0;
                                           nomeFile = "";
                                           lblFile.setText("");
                                           resetComandi();
                                       }
                                   } catch(InterruptedException | ExecutionException e) {
                                       javax.swing.JOptionPane.showMessageDialog(null, e.getMessage(),
                                               "Errore", javax.swing.JOptionPane.ERROR_MESSAGE);
                                   }
                               }
                           };                
                           worker.addPropertyChangeListener(listener);
                       } catch (Exception ex) {
                                       javax.swing.JOptionPane.showMessageDialog(Gui.this, ex.getMessage(),
                                               "Errore", javax.swing.JOptionPane.ERROR_MESSAGE);
                       }
                       worker.execute();                                
                       }
               });
               getContentPane().add(btInvia, "cell 4 1,alignx right");
               
               jpbComFile = new JProgressBar();
               getContentPane().add(jpbComFile, "cell 0 2 5 1,growx");
               
               JButton btRete = new JButton("Rete ...");
               btRete.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent arg0) {
                       SelRete frame = new SelRete(Gui.this);
                       frame.setVisible(true);
                       }
               });
               getContentPane().add(btRete, "cell 0 3,alignx left");
               
               JButton btChiudi = new JButton("Chiudi Finestra");
               btChiudi.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                       Gui.this.dispose();
                       System.exit(0);
                       }
               });
               getContentPane().add(btChiudi, "cell 3 3 4 3,alignx right");
       }

   private void resetComandi() {
       if (porta <= 0) {
           btApri.setEnabled(false);
       } else {
           btApri.setEnabled(true);
       }
       if (porta == 0 || nomeFile.equals("")) {
           btInvia.setEnabled(false);
       } else {
           btInvia.setEnabled(true);
       }
   }
   
   /**Reimposta l'indirizzo IP su cui ascoltare le richieste di connessione
    * per l'invio del file da scambiarsi.
    *
    * @param ind l'indirizzo IP su cui ascoltare, se nullo l'ascolto è generalizzato
    * @param porta la porta di ascolto.
    */
   public void setAddress(String ind, int porta) {
       this.indirizzo = ind;
       this.porta = porta;
       if(!ind.equals("")) {
               tMsg.append("Indirizzo di ascolto : " + ind + " sulla porta " + porta +"\n");
       } else {
           tMsg.append("Ascolto sulla porta : " + porta +"\n");
       }
       resetComandi();
   }    

       private String indirizzo = "";
   private int porta = 0;
   private String nomeFile = "";
   
}

 
Web  Top
view post Posted on 22/1/2016, 18:19
Avatar

Nubbio x Sempre

Group:
Moderazione globale
Posts:
7,226

Status:


PARTE 3 di 3 : il Client



Ovviamente, la parte "Client" del discorso qui affrontato è una applicazione, grafica, completamente separata dalla parte "Server".

Ancora una volta, si adatteranno le metodologie affrontate in questo post, che Vi invito a leggere, adattandole ad un contesto grafico non bloccante in caso di lunghe elaborazioni.

Di per se, la parte client è più semplice della parte server, presupponendo sia conosciuta "a priori" la posizione in rete del server e la relativa porta di ascolto, argomenti definibili a discrezione degli utenti e, quindi, non conosciuti a priori ... a dire il vero, escogitare un sistema di mutuo riconoscimento in LAN non sarebbe una idea malvagia, magari in futuro ;)

Comunque, la applicazione client qui definita è composta da due sole classi : una finestra grafica (GUI) per la gestione delle impostazioni e dei comandi ed un oggetto di classe SwingWorker che si occupa della effettiva ricezione del file.

La finestra grafica



png



L'interfaccia grafica ed il sistema di comando sono molto semplici, un paio di caselle di testo per l'impostazione di indirizzo e porta del server remoto, con una impostazione in locale di default, una etichetta per mostrare la direttrice di salvataggio scelta, argomento obbligatorio, una progressbar per visualizzare lo stato di avanzamento della ricezione dati e quattro bottoni di comando.

Per mia abitudine, evito di leggere direttamente dai controlli dati i valori di utilizzare nella applicazione, tendo a valutarli e, se sono congruenti, assegnarli a variabili preposte, pertanto di norma prevedo delle variabili specifiche a livello del form, nello specifico :
CODICE
// Variabili ad uso locale nella applicazione
   private String indirizzo = "127.0.0.1";
   private int porta = 9999;
   private String nomeDir = "";
   private boolean impRete = false;


Ritengo si spieghino da sole, unica degna di attenzione è la variabile "impRete", utilizzata quale commutatore di stato per definire le azioni da intraprendere alla pressione delo comando di impostazione dei dati di rete.

Pulsante "Rete ..."


Tale pulsante abilita all'inserimento di dati di rete diversi da quelli impostati di default, alla prima pressione abilita le caselle di testo preposte allo inserimento dei dati, commuta lo stato di impostazione della rete a VERO cambiando, in fine, la sua etichetta in "Salva".
Alla seconda pressione analizza i dati inseriti e se tutto è coerente li memorizza e si ridispone in stato di attesa, in caso di incoerenze vengono segnalate. Da tener presente che il controllo sull'indirizzo di rete riguarda la sola presenza di dati, non sapendo a priori se si sarà in presenza di un indirizzo IPV4, IPV6 ovvero in un ambiente servito da un DNS.
Il codice del relativo listener :

CODICE
btRete.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent arg0) {
                       if(!impRete) {
                           txtMsg.setText("");
                           indirizzo = "";
                           porta = 0;
                           txtSvr.setText("");
                           txtPorta.setText("");
                           impRete = true;
                           btRete.setText("Salva");
                           txtSvr.setEnabled(true);
                           txtSvr.setEditable(true);
                           txtPorta.setEnabled(true);
                           txtPorta.setEditable(true);
                           txtSvr.requestFocus();
                       } else {
                           if (txtSvr.getText() == null || txtSvr.getText().equals("")) {
                               javax.swing.JOptionPane.showMessageDialog(Gui.this, "L'indirizzo di ascolto del server è un dato necessario", "Dati mancanti",
                                       javax.swing.JOptionPane.ERROR_MESSAGE);
                               txtSvr.requestFocus();
                               return;
                           }
                           indirizzo = txtSvr.getText();
                           try {
                               porta = Integer.parseInt(txtPorta.getText());
                           } catch(NumberFormatException e) {
                               javax.swing.JOptionPane.showMessageDialog(Gui.this, "La porta indicata non è un numero valido", "Porta di ascolto errata",
                                       javax.swing.JOptionPane.ERROR_MESSAGE);
                               txtPorta.requestFocus();
                               return;
                           }
                           if (porta < 1025 || porta > 65535) {
                               javax.swing.JOptionPane.showMessageDialog(Gui.this, "La porta indicata è fuori del range ammesso", "Porta di ascolto errata",
                                       javax.swing.JOptionPane.ERROR_MESSAGE);
                               txtPorta.requestFocus();
                               return;
                           }
                           txtMsg.append("Indirizzo di ascolto : " + indirizzo + " sulla porta " + porta +"\n");
                           btRete.setText("Rete ...");
                           impRete = false;
                           resetComandi();
                       }
                   }
               });



Pulsante "Dir :"


Pulsante preposto alla definizione della direttrice di salvataggio del file che si deve ricevere, operazione obbligatoria, in assenza non sarà permesso di procedere ... perché? Per evitare di dover registrare il file in una direttrice temporanea e poi di doverlo copiare e cancellare, ovvio no? ;) il suo listener :
CODICE
btDir.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                       nomeDir = "";
                       JFileChooser jcosa = new JFileChooser();
                       jcosa.setCurrentDirectory(new File(System.getProperty("user.home")));
                       jcosa.setDialogTitle("Salva in ...");
                       jcosa.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                       jcosa.setAcceptAllFileFilterUsed(false);
                       if (jcosa.showOpenDialog(Gui.this) == JFileChooser.APPROVE_OPTION) {
                           nomeDir = jcosa.getSelectedFile().getAbsolutePath();
                       }
                       lblDir.setText(nomeDir);
                       resetComandi();
                       }
               });


Niente di che, come potete vedere, si limita ad invocare un JFileChooser, puntandolo sulla home dell'utente ed impostandone la modalità di selezione alle sole directory (istruzione .setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);), se l'utente seleziona una direttrice essa valorizzerà la variabile relativa.

Avrete notato la chiamata al metodo "resetComandi()" in calce ad entrambi i listener, tale metodo "decide" la possibilità di accesso ai comandi, in effetti se non vengono definiti gli indirizzi di rete e la direttrice di memorizzazione del file non è possibile procedere alla ricezione, sono un pigro! : PRIMA dimmi cosa vuoi fare POI lo faccio :D
CODICE
/* Imposta lo stato dei comandi secondo il contesto di definizione dei
    * parametri da utilizzare per il download
    */
       private void resetComandi() {
       if (indirizzo.equals("")) {
           btDir.setEnabled(false);
       } else {
           btDir.setEnabled(true);
       }
       if (!indirizzo.equals("") && porta != 0 && !nomeDir.equals("")) {
           btRicevi.setEnabled(true);
       } else {
           btRicevi.setEnabled(false);
       }
   }


Pulsante "Ricevi"


Pulsante complicato questo :D se non l'avete fatto leggetevi la seconda parte dedicata al server, quella sul SendWorker, pur se usati specularmente i concetti ed i metodi utilizzati per il server li ho applicati anche al client, non li ripeterò, quindi, il listener del bottone è:

CODICE
btRicevi.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                       GetWorker worker = null;
                       PropertyChangeListener listener = new PropertyChangeListener() {
                           @Override
                           public void propertyChange(PropertyChangeEvent event) {
                               if("progress".equals(event.getPropertyName())) {
                                   jpbPercFile.setValue((int) event.getNewValue());
                               }
                           }
                       };
                       try {
                           worker = new GetWorker(indirizzo, porta, nomeDir, txtMsg) {
                               @Override
                               protected void done() {
                                   try {
                                       int uscita = get();
                                       if (uscita != 0) {
                                           txtMsg.append("Trasmissione file fallita\n");
                                       } else {
                                           indirizzo = "";
                                           porta = 0;
                                           nomeDir = "";
                                           lblDir.setText("Nessuna direttrice corrente");
                                           resetComandi();
                                       }
                                   } catch (InterruptedException | ExecutionException e) {
                                       javax.swing.JOptionPane.showMessageDialog(null, e.getMessage(),
                                               "Errore", javax.swing.JOptionPane.ERROR_MESSAGE);
                                   }
                               }
                           };
                           worker.addPropertyChangeListener(listener);
                       } catch (Exception ex) {
                           javax.swing.JOptionPane.showMessageDialog(Gui.this, ex.getMessage(),
                                   "Errore", javax.swing.JOptionPane.ERROR_MESSAGE);
                       }
                       worker.execute();
                       }
               });


Come vedete, è quasi identico al listener del pulsante "Trasmetti" del server, solo che qui apre un oggetto chiamato GetWorker, sempre di tipo SwingWorker, che si incarica delle operazioni di ricezione, per il resto anche qui vengono definiti il listener per il cambio delle proprietà e sovrascritto il metodo "done()" di GetWorker ... i principi sono gli stessi già visti.

Ometto il pulsante "Chiudi finestra", troppo banale, esce dal programma.

GetWorker


Come detto, è un oggetto di tipo SwingWorker analogo e simmetrico al già visto "SendWorker e, come quest'ultimo, restituisce dei tipi interi per i parametri "<t, V>".
Alla sua costruzione deve ricevere i parametri di connessione, la direttrice di salvataggio e la textArea per la scrittura dei messaggi, il costruttore
CODICE
public GetWorker(String ind, int porta, String dir, JTextArea text) throws Exception {
       this.ip = ind;
       this.porta = porta;
       this.dirSave = dir;
       this.inf =text;
   }

Ovviamente, le variabili per la ricezione sono definite a livello globale per l'oggetto GetWorker, il metodo "doInBackground()" è sovrascritto in modo da replicare i metodi usati per il client da linea di comando : tenta la connessione e, se va bene, richiama un metodo "get_file()" che per prima cosa acquisisce i parametri del file da ricevere e quindi provvede alla ricezione e scrittura dei dati, il tutto condito dalla scrittura dei messaggi relativi nella TextArea e pubblicazione della percentuale ricevuta tramite il metodo "setProgress(___)" di SwingWorker.

Ritengo che il breve sunto sia sufficiente, i codici sorgenti e quanto detto nei post precedenti chiariscono i dettagli.

Codice sorgente GetWorker




CODICE
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JTextArea;
import javax.swing.SwingWorker;


public class GetWorker extends SwingWorker<Integer, Integer> {
   private final String ip;
   private final int porta;
   private final String dirSave;
   
   private final JTextArea inf;
   
   Socket s;
   
   public GetWorker(String ind, int porta, String dir, JTextArea text) throws Exception {
       this.ip = ind;
       this.porta = porta;
       this.dirSave = dir;
       this.inf =text;
   }
   
   @Override
   protected Integer doInBackground() {
       try {
           // mi connetto al server
           s = connetti();
           if (s == null) {
               inf.append("Non esiste alcun server all'indirizzo " + ip + " sulla porta " + porta);
               return -2;
           }
           inf.append("** Connesso ad host " + s.getInetAddress().getHostAddress() + "\n");
           get_File();
       } catch (IOException e) {
           inf.append("Errore trasmissione : " + e.getMessage() + "\n");
           return -3;
       } finally {
           if (s != null) {
               try {
                   s.close();
               } catch (Exception e) {}
           }
       }
       return 0;
   }
   
   // funzione per connettersi al server
   private Socket connetti() {
       Socket sok = null;
       try {
           sok = new Socket(ip, porta);
       } catch (UnknownHostException e) {
           inf.append("Errore nella connessione : " + e.getMessage() + "\n");
           sok = null;
       } catch (IOException e) {
           inf.append("Errore nella connessione : " + e.getMessage() + "\n");
           sok = null;
       }
       return sok;
   }
   
   private String[] ottengo_parametri(ObjectInputStream ois) {
       String parametri_file[] = null;
       try {
           String param = (String) ois.readObject();
           parametri_file = param.split(",");
       } catch (IOException | ClassNotFoundException e) {
           inf.append("*ERRORE nella ricezione parametri del file : " + e.getMessage());
       }
       return parametri_file;
   }
   
   private void get_File() throws IOException {
       inf.append(" - Inizio il processo di ricezione\n");
       int read;
       long tot = 0;
       ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
       String [] param_file = ottengo_parametri(ois);
       String nome = param_file[0];
       long dimensione = Long.parseLong(param_file[1]);
       inf.append(" - File " + nome + ", dimensione " + dimensione + " byte\n");
       int progr;
       // stream per il salvataggio del file su disco
       File file = new File(dirSave + System.getProperty("file.separator") + nome);
       // apro uno stream di output
       FileOutputStream fos = new FileOutputStream(file, true);
       byte[] buf = new byte[1024];
       // prende i dati a pacchetti di 1024 byte ciclando sin che ci sono dati
       while((read = ois.read(buf)) != -1) {
           tot += read;
           // scrive il pacchetto su disco
           fos.write(buf, 0, read);
           // comunica l'avanzamento percentuale
           progr = (int) (tot * 100 / dimensione);
           setProgress(progr);
       }
       if (tot == dimensione) {
           inf.append("\n - File ricevuto con successo");
       } else {
           inf.append("\nERRORE - dimensione non conforme, c'è stato qualche problema.\n");
       }
       fos.close();
   }
}



Codice sorgente GUI (JFrame)




CODICE
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import net.miginfocom.swing.MigLayout;

import javax.swing.JFileChooser;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JProgressBar;
import javax.swing.border.BevelBorder;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.concurrent.ExecutionException;


@SuppressWarnings("serial")
public class Gui extends JFrame {

       private JPanel contentPane;
       private JTextField txtSvr;
       private JTextField txtPorta;
       
       // cotrolli grafici aggiunti manualmente perché di default non accessibili a livello di applicazione
       private JButton btDir;
       private JButton btRicevi;
       private JButton btRete;
       private JTextArea txtMsg;
       private JLabel lblDir;
       private JProgressBar jpbPercFile;
       
       
       // Variabili ad uso locale nella applicazione
   private String indirizzo = "127.0.0.1";
   private int porta = 9999;
   private String nomeDir = "";
   private boolean impRete = false;
       

       /**
        * Launch the application.
        */
       public static void main(String[] args) {
               EventQueue.invokeLater(new Runnable() {
                       public void run() {
                               try {
                                       Gui frame = new Gui();
                                       frame.setVisible(true);
                               } catch (Exception e) {
                                       e.printStackTrace();
                               }
                       }
               });
       }

       /**
        * Create the frame.
        */
       public Gui() {
               setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               setBounds(100, 100, 450, 300);
               contentPane = new JPanel();
               contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
               setContentPane(contentPane);
               contentPane.setLayout(new MigLayout("", "[grow]", "[grow][][][][]"));
               
               JScrollPane scrollPane = new JScrollPane();
               contentPane.add(scrollPane, "cell 0 0,grow");
               
               txtMsg = new JTextArea();
               txtMsg.setEditable(false);
               txtMsg.setLineWrap(true);
               scrollPane.setViewportView(txtMsg);
               
               JPanel panel = new JPanel();
               contentPane.add(panel, "cell 0 1,growx");
               panel.setLayout(new MigLayout("", "[][grow][][]", "[19px]"));
               
               JLabel lblNewLabel = new JLabel("Ind. Serv. :");
               lblNewLabel.setToolTipText("");
               panel.add(lblNewLabel, "cell 0 0,alignx left,aligny center");
               
               txtSvr = new JTextField();
               txtSvr.setToolTipText("Indirizzo IP del server");
               txtSvr.setText("127.0.0.1");
               panel.add(txtSvr, "cell 1 0,alignx left,aligny top, growx");
               txtSvr.setColumns(10);
               
               JLabel lblNewLabel_1 = new JLabel("Porta:");
               panel.add(lblNewLabel_1, "cell 2 0,alignx left,aligny center");
               
               txtPorta = new JTextField();
               txtPorta.setToolTipText("Porta di ascolto del server ( >1025 <= 65535)");
               txtPorta.setText("9999");
               panel.add(txtPorta, "cell 3 0,alignx left,aligny top");
               txtPorta.setColumns(10);
               
               JPanel panel_1 = new JPanel();
               contentPane.add(panel_1, "cell 0 2,growx");

               panel_1.setLayout(new MigLayout("", "[][grow][]", "[25px]"));
               
               btDir = new JButton("Dir :");
               btDir.setToolTipText("Permette di impostare la direttrice in cui salvare il file da ricevere (obbligatorio)");
               btDir.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                       nomeDir = "";
                       JFileChooser jcosa = new JFileChooser();
                       jcosa.setCurrentDirectory(new File(System.getProperty("user.home")));
                       jcosa.setDialogTitle("Salva in ...");
                       jcosa.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                       jcosa.setAcceptAllFileFilterUsed(false);
                       if (jcosa.showOpenDialog(Gui.this) == JFileChooser.APPROVE_OPTION) {
                           nomeDir = jcosa.getSelectedFile().getAbsolutePath();
                       }
                       lblDir.setText(nomeDir);
                       resetComandi();
                       }
               });
               panel_1.add(btDir, "cell 0 0");
               
               lblDir = new JLabel("Nessuna direttrice");
               lblDir.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
               panel_1.add(lblDir, "cell 1 0,alignx left,aligny center, growx");
               
               btRicevi = new JButton("Ricevi");
               btRicevi.setToolTipText("Avvia la ricezione del file dall'host remoto");
               btRicevi.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                       GetWorker worker = null;
                       PropertyChangeListener listener = new PropertyChangeListener() {
                           @Override
                           public void propertyChange(PropertyChangeEvent event) {
                               if("progress".equals(event.getPropertyName())) {
                                   jpbPercFile.setValue((int) event.getNewValue());
                               }
                           }
                       };
                       try {
                           worker = new GetWorker(indirizzo, porta, nomeDir, txtMsg) {
                               @Override
                               protected void done() {
                                   try {
                                       int uscita = get();
                                       if (uscita != 0) {
                                           txtMsg.append("Trasmissione file fallita\n");
                                       } else {
                                           indirizzo = "";
                                           porta = 0;
                                           nomeDir = "";
                                           lblDir.setText("Nessuna direttrice corrente");
                                           resetComandi();
                                       }
                                   } catch (InterruptedException | ExecutionException e) {
                                       javax.swing.JOptionPane.showMessageDialog(null, e.getMessage(),
                                               "Errore", javax.swing.JOptionPane.ERROR_MESSAGE);
                                   }
                               }
                           };
                           worker.addPropertyChangeListener(listener);
                       } catch (Exception ex) {
                           javax.swing.JOptionPane.showMessageDialog(Gui.this, ex.getMessage(),
                                   "Errore", javax.swing.JOptionPane.ERROR_MESSAGE);
                       }
                       worker.execute();
                       }
               });
               panel_1.add(btRicevi, "cell 2 0,alignx left,aligny top");
               
               jpbPercFile = new JProgressBar();
               jpbPercFile.setStringPainted(true);
               contentPane.add(jpbPercFile, "cell 0 3, growx");
               
               JPanel panel_2 = new JPanel();
               contentPane.add(panel_2, "cell 0 4,growx");
               panel_2.setLayout(new MigLayout("", "[][grow][]", "[25px]"));
               
               btRete = new JButton("Rete...");
               btRete.setToolTipText("Permette di impostare indirizzo e porta di ascolto del server");
               btRete.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent arg0) {
                       if(!impRete) {
                           txtMsg.setText("");
                           indirizzo = "";
                           porta = 0;
                           txtSvr.setText("");
                           txtPorta.setText("");
                           impRete = true;
                           btRete.setText("Salva");
                           txtSvr.setEnabled(true);
                           txtSvr.setEditable(true);
                           txtPorta.setEnabled(true);
                           txtPorta.setEditable(true);
                           txtSvr.requestFocus();
                       } else {
                           if (txtSvr.getText() == null || txtSvr.getText().equals("")) {
                               javax.swing.JOptionPane.showMessageDialog(Gui.this, "L'indirizzo di ascolto del server è un dato necessario", "Dati mancanti",
                                       javax.swing.JOptionPane.ERROR_MESSAGE);
                               txtSvr.requestFocus();
                               return;
                           }
                           indirizzo = txtSvr.getText();
                           try {
                               porta = Integer.parseInt(txtPorta.getText());
                           } catch(NumberFormatException e) {
                               javax.swing.JOptionPane.showMessageDialog(Gui.this, "La porta indicata non è un numero valido", "Porta di ascolto errata",
                                       javax.swing.JOptionPane.ERROR_MESSAGE);
                               txtPorta.requestFocus();
                               return;
                           }
                           if (porta < 1025 || porta > 65535) {
                               javax.swing.JOptionPane.showMessageDialog(Gui.this, "La porta indicata è fuori del range ammesso", "Porta di ascolto errata",
                                       javax.swing.JOptionPane.ERROR_MESSAGE);
                               txtPorta.requestFocus();
                               return;
                           }
                           txtMsg.append("Indirizzo di ascolto : " + indirizzo + " sulla porta " + porta +"\n");
                           btRete.setText("Rete ...");
                           impRete = false;
                           resetComandi();
                       }
                   }
               });
               panel_2.add(btRete, "cell 0 0,alignx left,aligny top");
               
               JLabel lblNewLabel_2 = new JLabel("");
               panel_2.add(lblNewLabel_2, "cell 1 0,alignx left,aligny center,growx");
               
               JButton btnChiudi = new JButton("Chiudi finestra");
               btnChiudi.setToolTipText("Chiude la finestra corrente");
               btnChiudi.addActionListener(new ActionListener() {
                       public void actionPerformed(ActionEvent e) {
                       Gui.this.dispose();
                       }
               });
               panel_2.add(btnChiudi, "cell 2 0,alignx left,aligny top");
       }
       
       // Codice operativo
       
   /* Imposta lo stato dei comandi secondo il contesto di definizione dei
    * parametri da utilizzare per il download
    */
       private void resetComandi() {
       if (indirizzo.equals("")) {
           btDir.setEnabled(false);
       } else {
           btDir.setEnabled(true);
       }
       if (!indirizzo.equals("") && porta != 0 && !nomeDir.equals("")) {
           btRicevi.setEnabled(true);
       } else {
           btRicevi.setEnabled(false);
       }
   }
       
}



Con questo codice considero l'articolo finito ... spero che, oltre me, serva a qualcuno, sono certo che esistono metodologie più efficienti per fare la stessa cosa ma questo è ciò che mi è riuscito di fare e ... funziona ;)

Ciao, alla prox
 
Web  Top
3 replies since 5/12/2015, 12:54   129 views
  Share