Newsgroup: it.comp.java
Titolo: AIUTO, sono disperato su Hashtable

Salve a tutti,
scusate sono un giovane programmatore di java e ancora
questo linguaggio mi rimane ostico nonostante debba consegnare un
progetto tra 36 ore.
Il programma implementato per questo progetto è un server FTP
che utilizza il protocollo UDP anziché TCP. Per far questo ho utilizzato
le classi DatagramSocket e DatagramPacket di 
java.net
 e fin qui tutto bene.
Poiché il server deve gestire più clienti è stato implementato con i thread.
Questi thread utilizzano una struttura dati condivisa che viene chiamata
descrGestioneAck all'interno del quale la maggior parte dei metodi non sono
sincronizzati eccetto 2. Tuttavia questa classe viene utilizzata comunque in
regioni critiche del tipo synchronized{...}. Siccome mi serve tenere un
descrittore di Gestione Ack per ciascun cliente, li ho messi nella tabella
hash
fornita in java.util. E qui sorge il problema: il thread che si occupa di
gestire le richieste, prende un pacchetto per volta. Quando il pacchetto è
una richiesta di tipo "GET filename" alloca un nuovo descrittore nella
tabella hash, e per i successivi messaggi provenienti dallo stesso client,
dovrebbe controllare che il descrittore di gestione ack associato al client
esista già nella tabella hash. Ma il programma (di cui allego il codice) al
pacchetto successivo, dopo aver controllato che il pacchetto sia nel formato
corretto (e facendo il debugging abbiamo verificato che i pacchetti erano
proprio nel formato corretto) fa una ricerca nella tabella hash e non trova
il descrittore che pure viene tranquillamente trovato dagli altri thread.
So non più che pesci pigliare,
se qualcuno ci capisce qualcosa mi faccia un fischio. Grazie.
Questa è il descrittore di gestione degli ack, ovvero gli elementi che sono
nella tabella hash
class descrGestioneAck { // le modifica alle istanze di questa classe devono
essere fatte in mutua esclusione
    private boolean[] finestraACK;
    private int contatore_ack;     //contatore degli ack ricevuti
    private int n_pacchetto;       //numero pacchetto da rispedire
    private boolean wakeup;
    public int ack_da_ricevere_b; //numero di ack da ricevere relativi al
blocco corrente
    public int primo_pack_b;      //primo pacchetto del blocco corrente
    public long ora_ult_p_b;      //ora alla quale e' stato spedito l'ultimo
pacchetto del blocco corrente
    public descrGestioneAck(int dimensione_finestra) {
 finestraACK = new boolean[dimensione_finestra+2];
 for (int i = 0; i < finestraACK.length; i++)
     finestraACK[i] = true;
 contatore_ack = 0;
 n_pacchetto = -3;
 wakeup = false;
 ack_da_ricevere_b = dimensione_finestra;
 primo_pack_b = 0;
    }
    public void setACKOK()
 finestraACK[SFTP_server.OK] = true;
 contatore_ack++;
    }
    public void setACKEND() {
 finestraACK[SFTP_server.END] = true;
 contatore_ack++;
    }
    public boolean getACKEND() {
 return finestraACK[SFTP_server.END];
    }
    public boolean getACKOK() {
 return finestraACK[SFTP_server.OK];
    }
    // deve essere usata solo dal processo sender
    public void resetACKOK() {
 System.err.println("unga: metto a false l'ack ok");
 finestraACK[SFTP_server.OK] = false;
    }
    // deve essere usata solo dal processo sender
    public void resetACKEND() {
 finestraACK[SFTP_server.END] = false;
    }
    public void setACK(int posizione) {
 finestraACK[posizione] = true;
 contatore_ack++;
    }
    // deve essere usata solo dal processo sender
    public void resetACK(int posizione) {
 finestraACK[posizione] = false;
    }
    // deve essere usata solo dal processo sender
    public void resetContatore() {
 contatore_ack = 0;
    }
    public boolean getACK(int posizione) {
 return finestraACK[posizione];
    }
    public boolean ack_ricevuti_tutti() {
 return (contatore_ack == ack_da_ricevere_b);
    }
    public synchronized void setPacchettoDaInviare(int num){
 // questa viene eseguita dal Master e dal TO_Manager
 // il metodo e' bloccante ma si ipotizza che
 // se viene usata dal master il processo sender che attende
 // debba fare solo l'operazione di inviare di nuovo il pacchetto indicato
     while ( wakeup ) {
     try {
  wait();
     } catch (InterruptedException e) {
  System.err.println("Errore: ricevuta una InterruptedException");
  // dovrebbe avvenire se un altro thread lancia l'eccezione
  // indicata mentra il thread corrente e' sospeso sulla wait
     }
 }
 n_pacchetto = num;
 wakeup = true;
 notify();
    }
    public synchronized int getPacchettoDaInviare() {
 while ( !wakeup ) {
     try {
  wait();
     } catch (InterruptedException e) {
  System.err.println("Errore: ricevuta una InterruptedException");
  // dovrebbe avvenire se un altro thread lancia l'eccezione
  // indicata mentra il thread corrente e' sospeso sulla wait
     }
 }
 wakeup = false;
 notify();
 return n_pacchetto;
    }
}
// questo è il thread principale
public class SFTP_server {
    public final static int dim_finestra = 20;
    public final static int dim_pacchetti = 8192;
    public final static int n_porta = 9999;
    public final static int timeout_pacchetti = 5000;
    public final static int timeout_sender = 30000;
    public final static int OK = dim_finestra;
    public final static int END = dim_finestra+1;
    public static int decodifica (byte[] b)
    {
 int back = 0;
 int z;
 for (int i=3; i>=0; i--)
     {
  z=(int) b[i];
  if (z<0) z = z +256;
  for (int j=3;j>i;j--) {z=z*256;}
  back = back +z;
     }
 return (back);
    }
public static void main(String args[]) {
 try {
     // inizializzazione strutture dati condivise
     CodaBuffer receiverBuffer = new CodaBuffer();
     CodaBuffer codaScadenze = new CodaBuffer();
     Hashtable tabellaGestioneACK = new Hashtable();
     DatagramSocket theSocket = new DatagramSocket(n_porta);
     // creazione e lancio del gestore dei timeout
     TO_manager gestore_timeout = new TO_manager(codaScadenze,
tabellaGestioneACK);
     gestore_timeout.start();
     // creazione e lancio del receiver
     Receiver theReceiver = new Receiver(theSocket, receiverBuffer);
     theReceiver.start();
     System.out.println("\fServer ready on port " + n_porta + "\n\n");
     descrGestioneAck dga;
     while(true) {
  System.err.println("unga: Master: sto prendendo un pacchetto dal receiver
buffer");
  // ottiene un datagram dal receiver buffer e se questo e' vuoto si
sospende
  DatagramPacket dp = (DatagramPacket) receiverBuffer.get();
  System.err.println("unga: Master: ho preso un pacchetto dal receiver
buffer");
  // estrae lo IAP
  KeyIAP kiap = new KeyIAP(dp.getAddress(), dp.getPort());
  System.out.println("unga: ia=" + kiap.ia + " porta=" + kiap.p);
  // controlla il tipo di richiesta
  byte[] dati = dp.getData();
  String richiesta = new String(dati, 0, 4);  //da gestire un eccezione
  boolean contiene_chiave = false;
  // synchronized(tabellaGestioneACK) {
  contiene_chiave = tabellaGestioneACK.containsKey(kiap);
  // }
  System.out.println("unga: Master: contiene_chiave e' " + contiene_chiave);
  dga = null;
  // synchronized (tabellaGestioneACK) {
  dga = (descrGestioneAck) tabellaGestioneACK.get(kiap);
  // }
  System.out.println("unga: Master: dga è null? " + (dga == null));
  if (richiesta.equals("GET "))
      // boolean contiene_chiave = false;
      // synchronized(tabellaGestioneACK) {
      // contiene_chiave = tabellaGestioneACK.containsKey(kiap);
      // }
      if (!contiene_chiave) {
   // crea un oggetto che descrive la gestione degli ACK
   // e lo inserisce nella tabella hash
          dga = new descrGestioneAck(dim_finestra);
   // synchronized (tabellaGestioneACK) {
   tabellaGestioneACK.put(kiap, dga);
   System.out.println("unga: inserisco la chiave nella tabella hash " +
tabellaGestioneACK.containsKey(kiap));
   //}
   dga = (descrGestioneAck) tabellaGestioneACK.get(kiap);
   // }
   System.out.println("unga: Master: dga è null? " + (dga == null));
   String filename;
   filename = new String(dati, 4, dati.length - 4);
   File theFile = new File(filename.trim());
   System.out.println("Receiving: " + richiesta + filename);
   if (theFile.exists()) {
       System.out.println ("ricevuta richiesta " + filename + " da " +
kiap.ia + " dalla porta " + kiap.p);
       // creazione del processo sender
       Sender unSender = new Sender(kiap, theSocket, theFile,
tabellaGestioneACK, codaScadenze);
       unSender.start();
   }
   else {
       System.out.println("Errore: il file richiesto " + filename + " non
esiste");
   }
      }
      else {
   System.out.println("Errore: il server sta gia' servendo la richiesta di "
+ kiap.ia +" " + kiap.p);
      }
  }
  else
      if (richiesta.equals("ACK ")) {
   System.err.println("unga: sono il master, ho ricevuto un ack");
   // esiste la chiave nella tabella hash?
   // boolean contiene_chiave_2 = false;
   // synchronized(tabellaGestioneACK) {
   // System.out.println("unga: controllo se esiste dga");
   // System.out.println("unga: ia=" + kiap.ia + " porta=" + kiap.p);
   // contiene_chiave_2 = tabellaGestioneACK.containsKey(kiap);
   // }
   // System.out.println("unga: la tabella hash contiene la chiave?" +
contiene_chiave_2 + tabellaGestioneACK.containsKey(kiap));
   System.out.println("1");
    // il problema sorge a questo punto, al secondo giro del while il
programma non trova il descrittore nella tabella hash
   if (contiene_chiave) {
       System.out.println("2");
       String tipoACK = new String(dati, 4, dati.length - 4);
       System.out.println("Receiving: " + richiesta + " " + tipoACK);
       boolean ok = false, end = false;
       dga = null;
       // synchronized (tabellaGestioneACK) {
       dga = (descrGestioneAck) tabellaGestioneACK.get(kiap);
       // }
       if (dga == null) continue;
       // controlla il tipo di ACK
       if ((ok = tipoACK.equals("OK")) || (end = tipoACK.equals("END"))) {
    synchronized(dga) {
        if (ok && !dga.getACKOK()) {
     // imposta l'ack ok a true e incrementa il contatore
     dga.setACKOK();
        }
        if (end && !dga.getACKEND()) {
     // imposta l'ack end a true e incrementa il contatore
     dga.setACKEND();
        }
        if (dga.ack_ricevuti_tutti()) {
     // risveglia il sender
     dga.setPacchettoDaInviare(-1);
        }
    }
       }
       else {
    // l'intero deve essere codificato con codifica(), quindi deve essere 4
byte, se e' piu' lungo, va scartato
    if ((dati.length - 4) > 4) continue;
    byte[] numero_ack_in_byte = new byte[dati.length - 4];
    for (int i = 4; i < dati.length; i++)
        numero_ack_in_byte[i - 4] = dati[i];
    int numero_ack = decodifica(numero_ack_in_byte);
    // se il numero del pacchetto e' al di fuori
    // dell'intervallo che si sta aspettando il
    // sender, oppure ACK corrispondente e' gia'
    // arrivato, il pacchetto e' scartato
    synchronized (dga) {
        if ((numero_ack < dga.primo_pack_b) || (numero_ack >
dga.primo_pack_b + dga.ack_da_ricevere_b) || dga.getACK(numero_ack))
continue;
        // altrimenti setta l'ack e incrementa il contatore
        dga.setACK(numero_ack);
        if (dga.ack_ricevuti_tutti()) {
     // risveglia il sender affinche' passi al blocco successivo
     dga.setPacchettoDaInviare(-1);
        }
    }
       } // else
   }// if (tabellaGestioneACK...
   System.out.println("unga: non ho trovato la chiave");
      } // if (richiesta.equals...
     } // while
 } // try
 catch (SocketException e) {
     System.out.println("Ricevuto un SocketException\nQuit");
 }
    } // main
} // SFTP_server

[...]
Non ho, purtroppo, il tempo di leggere tutto il tuo codice. 
Tieni solo presente che, affinche' un oggetto possa essere usato come
chiave in una Hashtable, deve definire *sia* equals() *che* hashCode().
Spesso il secondo metodo viene dimenticato, e questo genera problemi
apparentemente "strani" nelle successive operazioni di recupero. 
-- 
God is Real, unless declared Integer.
ObjectZone - 
http://space.tin.it/computer/csadun