# Multithreaded Server ueber Konsole beenden



## vsk (1. Dez 2009)

Ich bin dabei, eine kleine Client-Server Anwendung um zu setzen (Studienprojekt).

Ich habe mit Hilfe verschiedener Beispiele aus dem Netz bereits eine Serveranwednugn realisiert, welche jedem Client einen eigenen Thread zuweist.

In den Beispielen lief der Server immer ewig 
	
	
	
	





```
while(true){...}
```

Ich moechte aber den Server ueber eine Konsoleneingabe beenden koennen.
Das lieft auch ueber 
	
	
	
	





```
while(!myString.equals("H"){...}
```
 ganz gut, nur sobald ein Client verbunden ist, funktioniert das nicht mehr. Zudem scheint die Schleife für unnötige CPU Last zu sorgen. Teilweise nimmt die Anwendung dann 99% eines Kerns in Anspruch.

Hier meine ServerHauptprogramm

```
//import nicht aufgelistet
public class tcpServer extends Thread{
    static Vector ThreadVector = new Vector();//hier lege ich alle Clients ab um Sie spaeter sauber zu beenden, Methode fehlt noch
    static String stopString; //wird von der Konsole eingelesen

    public tcpServer(){
//leer
    }

    public static void main(String[] args) throws IOException {
        tcpServer myServer = new tcpServer(); //neues tcpServer Object
        myServer.start(); //starten als Thread
        stopString=("");
//jetzt kommt die Problemschleife
        while(!stopString.equals("H")){
           stopString=StdInput.readString().toUpperCase().trim();
        }
        try{
            //myServer.join();// hiermit funktioniert es, aber dann lauft der Server ewig
            myServer.interrupt(); //funktioniert nicht
            currentThread().interrupt(); //funktioniert nicht
            System.out.println("myServer "+myServer.getName()+" : "+myServer.getState()); //Ausgabe Runnable....
            System.out.println("current "+currentThread().getName()+" : "+myServer.getState());  //Ausgabe Runnable....
            System.exit(0);
        }catch(Exception e){
            e.printStackTrace();
            System.out.println(e);
        }
    }

    @Override
    public void run(){
        System.out.println("Ich bin Thread: "+currentThread().getName()+" warte auf Clients");
        int i=0;
        try{
            ServerSocket myServerSocket = new ServerSocket(4200);
            System.out.println(myServerSocket.getInetAddress());//Kontrollausgabe fuer mich
   
            while(!isInterrupted()){ //muss ich vielleicht hier etwas aendern?
                Socket aSocket = new Socket();
                aSocket = myServerSocket.accept(); //wartet auf den Client
                System.out.println("Ein Client ist da " +aSocket.toString());
                tcpServerThread aThread = new tcpServerThread(aSocket);//fuer jeden Client ein neuer Thread
                synchronized(aThread){
                    ThreadVector.add(i, aThread);
                    aThread.start();
                }
                i++;
            }
        }catch (Exception e){
                System.out.println(e);
            }
    }
}
```

Die Klasse fuer die Client Threads

```
public class tcpServerThread extends Thread{
    
    private Socket mySocket;

    public tcpServerThread(Socket aSocket){
        this.mySocket = aSocket;
    }

    @Override
    public void run(){
        System.out.println("Ich bin Thread: "+currentThread().getName()+" :"+mySocket.toString());//Kontrollausgabe fuer mich
        
        try{
            BufferedReader myback = new BufferedReader(new InputStreamReader(mySocket.getInputStream()));
            String back = myback.readLine(); //lesen vom Client , der schickt ein "hello\n"
            System.out.println(back);
        }catch(Exception e){
            e.printStackTrace();
            System.out.println(e);
        }
    }
}
```


----------



## SlaterB (1. Dez 2009)

> nur sobald ein Client verbunden ist, funktioniert das nicht mehr. 

kannst du das näher definieren? was soll passieren/ was passiert stattdessen?
interrupt() hat sicherlich wenig Auswirkungen, würde laufende Client-Threads gar nicht betreffen,
aber System.exit(0) ist doch recht final

> Zudem scheint die Schleife für unnötige CPU Last zu sorgen. Teilweise nimmt die Anwendung dann 99% eines Kerns in Anspruch.

prüfe per Ausgabe in Zeile 15-17, ob die Schleife millionenmal durchlaufen wird, oder ob besser die ganze Zeit auf neue Eingaben gewartet wird,
was genau passiert, hängt natürlich von der jedermann unbekannten Klasse StdInput ab


----------



## vsk (1. Dez 2009)

SlaterB hat gesagt.:


> kannst du das näher definieren? was soll passieren/ was passiert stattdessen?
> interrupt() hat sicherlich wenig Auswirkungen, würde laufende Client-Threads gar nicht betreffen,
> aber System.exit(0) ist doch recht final



Die Schleife:
[JAVA=15]while(!stopString.equals("H")){
           stopString=StdInput.readString().toUpperCase().trim();
        }
eineNachfolgendeMethode();
[/code]
soll dafuer sorgen, dass der Server ueber die Eingabe der Buchstaben "h" beendet werden kann.
Nachfolgende Methoden sollen erst dann ausgeführt werden.

Problem:
sobald ein Client am ServerSocket Verbunden hat, kann ich soviel "h"+ENTER eingeben wie ich will, das interresiert den Server dann nicht mehr.

Die Klasse StdInput beinhaltet einfach nur einen Buffer zum Einlesen der Tastatureingabe:

```
//StdInput.readString() fuehrt zu:
public static String readString()
 {
  BufferedReader br;
  String inputString = "";

  br = new BufferedReader(new InputStreamReader(System.in));

  try
  {
   inputString = br.readLine();
  }
  catch(IOException _uh)
  {
   System.out.println("Lesefehler !");
  }
  return inputString;
 }
```



SlaterB hat gesagt.:


> prüfe per Ausgabe in Zeile 15-17, ob die Schleife millionenmal durchlaufen wird, oder ob besser die ganze Zeit auf neue Eingaben gewartet wird



Die Schleife hält bei
[JAVA=16]stopString=StdInput.readString().toUpperCase().trim();[/code]
an, so wie ich mir das vorstelle. Ausgaben werden erst nach erfolgreicher Eingabe von "h" ausgegeben, vorausgesetzt kein Client ist verbunden, denn dann tritt ja das Problem auf...


----------



## SlaterB (1. Dez 2009)

ich kann es im Moment nicht testen und sehe direkt keine Ursache


allgemein ist es sehr gefährlich, bei jedem Lesevorgang einen neuen BufferedReader auf System.in zu legen,
wenn zu einem Zeitpunkt 10 Zeilen auf einmal vorhanden sind, dann liest ein BufferedReader alles in seinen großen Buffer/ Puffer
die erste Zeile davon wird zurückgegeben, der Rest geht für immer verloren, da der BufferedReader nicht wiederverwendet wird,

ein nächster readLine()-Aufruf schaut wieder auf die Eingabe

allgemein sowas wie new BufferedReader(new InputStreamReader(System.in)) immer nur genau einmal z.B. in einer statischen Variable definieren,
vielleicht hilft diese Änderung auch hier, 
wobei ich mir aber bei einzelnen zeitlich getrennten Eingaben nicht so vorstellen kann, dass das zum Fehler führt,
zumal es auch anscheinend um die allererste Eingabe geht


bei
new BufferedReader(new InputStreamReader(mySocket.getInputStream()))
ist das ähnlich,
bisher ist es nur ein Lesevorgang, falls es aber mal mehrere werden, dann auf sowas achten,
übers Netzwerk können viel leichter mehrere Zeilen zusammen ankommen,
statische Variable fällt hier aus, Klassenattribut dann eher


----------



## vsk (1. Dez 2009)

##EDIT### :autsch: ganz toll kompiliert und über konsole aufgerufen macht das programm fast was es soll, in der IDE netbeans dagegen tritt das beschriebene problem auf...

Es muss wohl die while-Schleife in der main sein.
Auch wenn ich es ohne einen BufferedReader mache gibt es Probleme.
Warum auch immer, jetzt kann auf einmal mehr kein Client connecten...
#EDIT# mit der while Schleife kommt er noch bis Zeile 27, aber schon nicht mehr zu Zeile 31..:autsch:

```
//imports gekuerzt
public class tcpServer extends Thread{
    static Vector ThreadVector = new Vector();
    static String stopString;
    static int port = 42420;

    public tcpServer(){

    }

    public static void main(String[] args) throws IOException {
        tcpServer myServer = new tcpServer();
        myServer.start(); //starten eines ThreadObjectes vom Typ tcpServer [main]
        boolean stopit=true;

        while (stopit){
            int c=0;
            c = System.in.read();
            System.out.println(c);
            if(c==120)// entspricht "x"
            stopit = false;
        }
    }

    @Override
    public void run(){
        System.out.println("Ich bin Thread: "+currentThread().getName()+" warte auf Clients");
        int i=0;
        try{
            ServerSocket myServerSocket = new ServerSocket(port);
            System.out.println(myServerSocket.getInetAddress());
          
            while(true){
                Socket aSocket = new Socket();
                aSocket = myServerSocket.accept();
                System.out.println("Ein Client ist da " +aSocket.toString());
                tcpServerThread aThread = new tcpServerThread(aSocket);
                synchronized(aThread){
                    ThreadVector.add(i, aThread);
                    allThreads();//gibt eine Liste aller Threads und deren Status aus
                    aThread.start();
                }
                i++;
            }
        }catch (Exception e){
                System.out.println(e);
            }
    }

    private void allThreads(){
        int size = tcpServer.ThreadVector.size();
            for (int i =0; i<=(size-1); i++){
                System.out.println(((Thread)tcpServer.ThreadVector.get(i)).getName() + 
" "+((Thread)tcpServer.ThreadVector.get(i)).getState());
            }
    }
}
```

irgendwie muss ich diese while schleife anders realisieren oder in die run() unterbringen... nur wie


----------



## SlaterB (1. Dez 2009)

hmm, das hatte ich zwischenzeitlich auch bei mir (deshalb schrieb ich erst, ich könne es nicht testen)
und konnte das noch weniger erklären,

mit
Thread.sleep(1000);
vor der Eingabe-Schleife gehts aber, warum auch immer..


----------



## vsk (1. Dez 2009)

oh man, da bin ich aber irgendwie froh, dass das nicht nur bei mir so ist!

:toll: Thread.sleep(1000);

jetzt muss ich wenigstens nicht immer zwischen Konsole und IDE hin und herwechseln.

Grosses DANKE @ SlaterB

damit hat sich das Thema erledigt


----------

