Newsgroup: it.comp.java
Titolo: ServerSocket e accept()

Salve!
Sto creando un server che lancia un thread che gestisce la comunicazione 
con client vari. Invece di farlo come si trova bene o male su tutti i 
libri o anche in qualche sito che ho visto, mi chiedevo se era possibile 
impostare anche la classe che fa da server come thread, in modo da far 
lanciare più server (su porte diverse, ovviamente). Il problema, nel mio 
caso, sorge nel momento in cui lanciando un thread del server viene 
lanciata la accept(), che "blocks until a connection is made": 
sostanzialmente il primo thread che fa partire un server si tiene la cpu 
esclusivamente lui e non la molla a nessuno, e non viene mai eseguito il 
resto del codice che istanzia altri server...
Che fare? Si può gestire in qualche modo diverso la accept()?
Grazie!!!!

Diversi anni fa (parlo del 1997!) mi ero trovato con un problema simile,
inerente piů che altro diversi thread server su Linux. Windows era piů
gentile e non mantava processi in starvation.
Avevo risolto usando il metodo setPriority(MIN_PRIORITY)
Vedi se ti aiuta!
Alex.
-- 
___________________________________________________________
a p bertolini
(
www.webwarrior.org
)



Qualunque sia il linguaggio che usi, tipicamente quando scrivi un server
TCP/IP stabile non puoi evitare di usare le capacità multithreading, quindi
la tua idea non solo è fattibile, ma è anche l'unico modo di procedere
correttamente - a meno che tu non voglia iniziare a dare un'occhiata alle
New IO Api (package java.nio.* e figli) che prevedono il supporto automatico
per serversocket non bloccanti... ma la vedo un po' troppo complicata per
ora...
Quel "blocks until a connection is made" non vuol dire che la accept port
l'uso della cpu al 100%... anzi! Internamente, alla chiamata di accept(), la
JVM delega subito la gestione dell'accettazione al sistema operativo
sottostante, che mette in pausa (e sottolineo _in pausa_)il thread chiamante
finchè non c'è una richiesta di connessione in arrivo.
Prova così:
1) per ogni tipo di server ti crei una classe che implementi Runnable e che
allochi un ServerSocket legato alla porta di tua scelta, ad esempio:
  public class Porta3000ServerManager implements Runnable {
     ServerSocket serv=null;
     ...
 
     public Porta3000ServerManager() {
        serv=new ServerSocket(3000);
        ...
     }
 
     publiv void stopserver() {
        serv.close();
     }
 
     public void run() {
        Socket conn=serv.accept();
        //Bloccante, almeno finchè qualcuno non forza la chiusura
        //del serversocket da un altro thread...
        ...
     }
  }
  public class Porta3001ServerManager implements Runnable {
     ServerSocket serv=null;
 
     public Porta3000ServerManager() {
        serv=new ServerSocket(3001);
        ...
     }
 
     publiv void stopserver() {
        serv.close();
     }
     public void run() {
        Socket conn=serv.accept();
        ...
     }
  }
Non stare a guardare ora la qualità del codice... l'ho scritto un po' di
fretta e ho volutamente omesso tutti i controlli e la gestione delle
eccezioni... qualcosa la dovrai fare pure te, non credi? ;)
2) nella classe principale (o comunque quella che avvierà i server) avrai
una sequenza di questo tipo:
  ...
  Porta3000ServerManager server1=new Porta3000ServerManager();
  Thread serverThread1=new Thread(server1);
  serverThread1.start();
  Porta3001ServerManager server2=new Porta3001ServerManager();
  Thread serverThread2=new Thread(server2);
  serverThread1.start();
 
  System.out.println("I server sono attivi. Premere invio per iniziare");
  System.out.println("le procedure di arresto");
  System.in.read();
  //Questa pausa è necessaria per permettere ai server di entrare in
  //piena operatività. Se così non fosse, il thread principale cadrebbe
  //immediatamente sulle due istruzioni qui sotto, che... mi sembra chiaro
  //quali siano le loro intenzioni...
 
  server1.stopserver();
  server2.stopserver();  
 
Non è tanto difficile se segui bene i flussi ;)
-- 
Fermiamo Scientology prima che sia troppo tardi!!!
http://xenu.com-it.net
  ---  
http://xenu.freewinds.cx
(per rispondere personalmente leva IlDitoDalCulo)

 Non è tanto difficile se segui bene i flussi ;)
Sisi, guarda, come idea e come implementazione diciamo che la mia è 
sostanzialmente simile, e cmq aggiunge cose (ovviamente!), però fatto 
sta che si impianta sulla accept... ricontrollerò meglio i vari 
passaggi, ed eventuali "errori" che capitano spesso e volentieri...
Non mi pronuncio, cmq, perchè ho seguito esattamente il tuo 
ragionamento.... solo che non mi funziona!! ehehe
Grazie mille ancora!

 inerente più che altro diversi thread server su Linux. Windows era più


 un altro server!!).
Questa frase mi fa pensare parecchio... e anche il fatto nel post originale
hai menzionato un'occupazione altissima del tempo di CPU... Di solito un
problema simile si presenta se vi sono cicli for/while che vengono eseguiti
all'infinito senza un attimo di pausa.
Perchè non posti un po' di codice anche tu? Basta anche solo il metodo run()
che richiama accept().
-- 
Fermiamo Scientology prima che sia troppo tardi!!!
http://xenu.com-it.net
 - 
http://xenu.freewinds.cx/


burzum wrote:

Non e' il processo che "blocks until a connection is made" ma e' il 
metodo (cioe' il thread) che "blocks until a connection is made".
Quindi basta avere threads differenti e il gioco e' fatto.
- Dario


premettendo che ho fatto un solo programma client/server anche a me
sembrerebbe che il problema stia in qualche run() che non si ferma mai e
mangia tutta la cpu. Nle programma che avevo fatto io il server stesso era
composto da 3 thread, uno dei quali rimaneva in ascolto delle connessioni
dei client, un'altro si occupava di aggiornare la grafica del server, e il
terzo smistava i messaggi in broadcast. Mi pare di non aver mai avuto
particolare problemi con la accept() (ma probabilmente si, solo che me ne
sono dimenticato... ;-)
per inciso vorei sapere se un'approccio del genere è giusto o si può fare di
meglio...
class Grafica {
 public void update() {  // disegna la grafica }
 public void aggiornaSchermo() {
    repaint();
 }
 run() {  // il metodo run mi serve solo a tenere in vita l'oggetto
 B b = new B(this);
 b.start();
 while (true) {
   sleep(100);
     }
 }
class B extends Thread {
Grafica grafica;
B(Grafica g) {
    grafica = g;
}
 run () {
      // fai quello che ti pare e poi quando serve
      grafica.aggiornaSchermo()
     }
 }



La documentazione ufficiale di Sun consiglia sempre di non estendere la
classe Thread, bensì di implementare l'interfaccia Runnable. Se ci pensi
bene, non stai implementando nessuna funzionalità aggiuntiva che riguardi
strettamente la gestione a basso livello dei thread, bensì stai
implementando una parte della tua applicazione.
Estendere la classe Thread (IMHO) avrebbe senso se tu stessi creando, ad
esempio, una versione particolare di Thread che è possibile salvare in un
pool per essere riutilizzata.
Per prima cosa dunque userei "implements Runnable" sulla classe B.
Secondo, guardando bene le tue classi, mi sembra che tutto il lavoro di
aggiornamento debba essere fatto dalla classe Grafica, mentre B è solo un
mero esecutore. Perchè non mettere tutto in una sola classe?
Terzo, il "while (true)" di Grafica lo vedo moooolto brutto, soprattutto
perchè non vedo nessuna condizione di uscita tramite break o return.
public class Grafica extends JFrame implements Runnable {
  private Thread th=null;
  private volatile boolean timetostop=false;
  //altre variabili private
  public Grafica() {
    //tutte le tue inizializzazioni
    th=new Thread(this);
  }
  public void update() {
    //disegna la grafica
  }
  public void aggiornaSchermo() {update();}
  public void go() {th.start();}
  public void halt() {timetostop=true;}
  public void run() {
    while(!timetostop) {
      //fai quello che ti pare e poi quando serve
      grafica.aggiornaSchermo();
      //un minimo di pausa (anche se penso che
      //non sia necessario)
      Thread.sleep(100);
