Newsgroup: it.comp.java
Titolo: Multithreading client/server

Ciao,
ho il seguente problema da 1 mese ormai e non sono ancora riuscito a 
venirne a capo...qualcuno è disposto a darmi un aiuto?
(Chiedo anticipatamente scusa per la lunghezza del POST ma penso che sia 
   meglio lungo e un po' più chiaro che corto e incomprensibile)
Sto realizzando un programmino client/server che funziona più o meno così:
il client si collega a più server e attraverso stringhe ascii stabilisce 
se i diversi server sono liberi e disponibili.
Se il server è libero il client e il server iniziano a mandarsi dei ping 
e a rispondersi con dei pong. Questo è ciò che dovrebbero fare, almeno 
in teoria.
Il problema è che ogni connessione che viene aperta e che entra nella 
fase 2 (cioe' dei ping e pong) dura poco e poi ricevo un End Of Stream 
che la chiude. Talvolta mi rimane aperta nella fase 2 solo una 
connessione delle n iniziate.
Siccome la fase 1 è eseguita in un Thread (che istanzia un socket e i 
relativi streams) il quale lancia poi un secondo Thread (che si occupa 
dei ping e pong) non vorrei che socket e streams si chiudessero non 
appena il Thread 1 termina l'esecuzione del metodo run() anche se il 
Thread 2 sta ancora lavorando con gli streams e la sua istanza è 
inserita in un Vector creato nella classe Principale (quella con il 
metodo main()).
E' vero però che nel Thread_2 sono passati solo gli streams e non il 
socket. Questo può essere un problema? Quali altre cause possono 
determinarmi questa anomala chiusura degli streams.
La struttura che ha il programma client è la seguente:
public class Principale {
	Vector vettore_connessioni;
 
	public Principale() {
		vettore_connessioni = new Vector();
		//legge una serie di indirizzi IP da un file
		...
 
		//e con un ciclo for lancia per ogni IP
		//presente sul file il relativo Thread che si occupa 				//della prima 
fase della connessione
		//(dialogo con stringhe ascii)
		for(...) {
			synchronized(vettore_connessioni) {
				vettore_connessioni.add(new Thread_Fase_1(IP, vettore_connessioni));
			}
		}
	}
}
public class Thread_Fase_1 extends Thread{
	Vector vettore_connessioni;
	OutputStream to_host;
	InputStream from_host;
	public Thread_Fase_1(IP, vettore_connessioni) {
		this.vettore_connessioni = vettore_connessioni;
		start();
 
	}
	public void run() {
		//crea il Socket con l'IP ricevuto
		//e inizializza gli streams
		Socket host = new Socket(IP);
		to_host = new OutputStream(host.getOutputStream());
		from_host = new InputStream(host.getInputStream());
 
		synchronized(to_host) {
			synchronized(from_host) {
				//dialoga con stringhe ascii
				//per vedere se il Server e' libero
				...
 
				//se tutto è OK lancia il Thread_Fase_2
				//passandogli come argomento gli streams 				//e il vettore_connessioni
 
				synchronized(vettore_connessioni) {
					vettore_connessioni.add(new Thread_Fase_2(from_host, to_host, 
vettore_connessioni));
				}
			}
		}
	}
}
public class Thread_Fase_2 extends Thread{
	Vector vettore_connessioni;
	OutputStream to_host;
	InputStream from_host;
	public Thread_Fase_2(from_host, to_host, vettore_connessioni) {
		this.vettore_connessioni = vettore_connessioni;
		this.from_host = from_host;
		this.to_host = to_host;
		start();
 
	}
	public void run() {
		synchronized(to_host) {
			synchronized(from_host) {
				//legge e invia ripetutamente ping e
				//pong finche' non incontra un'eccezione
				//di fine stream e allora rimuove il
				//Thread_Fase_2 dal vettore_connessioni
				try{
					while(true) {
						to_host.write(ping);
						from_host.read(pong);
						...
					}
				}
				catch(EndOfStreamException e) {
				synchronized(vettore_connessioni) {
				vettore_connessioni.remove(this);
				}
				}
			}
		}
	}
}
GRAZIE ANTICICPATAMENTE,
Edoardo.

Penso succeda più o meno quello che hai detto tu. Quando un
thread muore, il suo reference viene forzato a null, quindi si
perde anche tutto ciò che c'è dentro... nel tuo caso gli stream.
Ci sono due soluzioni: la prima è creare gli stream nella classe
principale. Il main() viene infatti aperto automaticamente in un
thread, e la fase due verrebbe ugualmente eseguita parallelamente
alla fase una... pur non creando esplicitamente due thread.
La seconda possibilità (più macchinosa...) è creare un secondo
vettore nella classe principale, nel quale "salvare" gli stream creati
nel thread della fase 1, in modo che possano sopravvivere alla
morte dello stesso thread.
Spero di esserti stato d'aiuto...

 
Grazie Kamesh, sei stato molto gentile.
A questo punto se mi confermi che quando il Thread_Fase_1 termina 
vengono annullate le istanze degli streams e del socket e di tutto ciň 
che era stato creato nel Thread_Fase_1 (compreso quindi il 
Thread_Fase_2), allora mi vengono i seguenti dubbi:
1) salvare gli streams in un vettore nella classe principale salva anche 
il socket oppure venendo settato a null il socket gli streams vengono 
ugualmente chiusi?
2) anche il Thread_Fase_2 viene dunque annullato essendo lanciato dal 
Thread_Fase_2 oppure i Thread fanno eccezione rispetto alle altre istanze?
E infine un dubbio correlato alla questione:
in un codice di questo tipo:
for(int i=0; i<10; i++) {
	new MyThread().start();
}
che fine fanno i Thread creati? I precedenti all'ultimo vengono 
eliminati non essendo mantenuto un riferimento a loro oppure rimangono 
normalmente attivi finchč non terminano il loro metodo run()?
Sebbene mi sia letto un paio di libri sulla programmazione di rete e su 
IO che affrontavano entrambi i Threads non sono ancora sicuro su diversi 
aspetti dei Threads e facendo delle prove mi pare di ottenere risultati 
sempre diversi.
Grazie nuovamente,
Edoardo

 normalmente attivi finchč non terminano il loro metodo run()?
Come ho detto sopra, i thread vengono creati, partono e, quando terminano,
muoiono. Il fatto che il reference non sia piů accessibile (quindi non si
possono piů effettuare operazioni su di essi) non gli impedisce di andare
avanti sino a che il codice all'interno del metodo run() termina.