# Multithreading Client / Server erklärt



## chrilux (22. Dez 2013)

Ich sitze von der Uni aus an einer Aufgabe...

Chatserver / CLient programmierung...


Kann mir jemand struktiert in worten erklären wie ich es realisiere mehrere Clients zu connecten?


Mit der Server horcht mit einer endlosschleife und accept()... soweit so gut...

Wie bekomme ich aber mehrere Clients an den Server ran und besteht dauerhaft eine Verbindung oder wird diese immer wieder neu hergestellt?


Hier geht es erstmal nicht um Coding sondern ums Verständnis...

Habe bisher keine gute Seite im Inet gefunden mit der ich zurecht komme...
Ich muss mir das alles erstmal bildlich vorstellen können...

Das ist derzeit noch mein größtes Problem mit Java:cry:


Hierbei geht es nicht darum was ein Socket oder sowas ist... das ist mir klar...


----------



## turtle (22. Dez 2013)

> Mit der Server horcht mit einer endlosschleife und accept()... soweit so gut...
> Wie bekomme ich aber mehrere Clients an den Server ran und besteht dauerhaft eine Verbindung oder wird diese immer wieder neu hergestellt?



Genau, du hast einen ServerSocket auf einem Port erstellt. Dann wartet der ServerSocket darauf, dass sich ein Client verbindet. Diesen Verbindungsaufbau akzeptierst du 

```
public Socket accept()
```
und du siehst, dass du einen neuen Socket zurück bekommst. Mit diesem können nun Client/Server beliebig Daten austauschen. Über den Socket kannst du dir über getInputStream bzw. getOutputStream() Streams liefern lassen, um Daten zu lesen oder zu schreiben.

Da nun aber weitere Clients sich ebenfalls verbinden möchten, muss der ServerSocket wieder accept() aufrufen, um auch für diese Clients eine Socket-Verbindung aufzubauen. Daher wird oft der Code nachdem der Socket von accept zurück kommt in einem eigenen Thread ablaufen. Dieser Thread wird dann durch einen entsprechenden Befehl, beendet.

PS: TCP Sockets bleiben offen, bis sie geschlossen werden. Es ist nicht so einfach von einer der beiden Seiten zu erkennen, das die jeweilige andere Seite die Verbindung abgebrochen hat (z.B. wg. Absturz des Rechners).


----------



## chrilux (22. Dez 2013)

Hey turtle danke erstmal...




```
public class Server implements Runnable {

    private final int port;
    private Server serverObject;
    volatile boolean gestartet;
    volatile boolean unterbrochen;
    ServerSocket serverS;
    Socket clientS;
    public static int teilnehmerCount;

    /**
     * Konstruktor, setzt den gewünschten Port des ChatServers.
     *
     * @param port
     */
    public Server(int port) {
        this.port = port;
    }

    @Override
    public void run() {

        gestartet = oeffneServerSocket();

        if (gestartet) {
            System.out.println("Server ist gestartet....! Höre auf dem Port: " + port);
        }

        // solange unterbrochen nicht true ist...
        System.out.println(unterbrochen);
        while (!unterbrochen) {
            try {

                this.clientS = serverS.accept();

                // erstellen der Streams 
                BufferedReader reader = new BufferedReader(new InputStreamReader(clientS.getInputStream()));
                PrintWriter writer = new PrintWriter(clientS.getOutputStream(), true);
                // Ende der Streams

                String s;
                while ((s = reader.readLine()) != null) {
                    System.out.println("Empfangen vom Client: " + s);
                    writer.write(s);
                    writer.flush();
                }

            } catch (SocketTimeoutException e) {

                System.out.println("Kein Client gehört... versuche es weiter!");

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private boolean oeffneServerSocket() {
        try {
            this.serverS = new ServerSocket(this.port);
            this.serverS.setSoTimeout(2000);
            return true;
        } catch (IOException e) {

            return false;
        }
    }

    public static void main(String[] args) {
        new Thread(new Server(7777)).start();
    }
}
```


Was sagst du dazu?

Sobald sich ein Client anmeldet, hört der Server auf obwohl er im eigenen Thread läuft...

Ich verstehe wahrscheinlich hier grundlegen was falsch...


(Ich poste jetzt erstmal den Client nicht weil der macht gerade noch nicht viel  )


----------



## turtle (22. Dez 2013)

Bitte sehr, aber...
bitte auch genau lesen, was ich schrieb

Die Zeilen

```
// erstellen der Streams 
                BufferedReader reader = new BufferedReader(new InputStreamReader(clientS.getInputStream()));
                PrintWriter writer = new PrintWriter(clientS.getOutputStream(), true);
                // Ende der Streams
 
                String s;
                while ((s = reader.readLine()) != null) {
                    System.out.println("Empfangen vom Client: " + s);
                    writer.write(s);
                    writer.flush();
                }
```
sollten in einem gesonderten Thread ablaufen, weil sonst der Server EINEN Client bedient und der nächste warten muss.

Sorry, hab leider deinen Post nicht richtig gelesen...

Ich meinte mit eigenem Thread NICHT den Thread zum Aufbau des ServerSockets, sondern nach dem Accept sollte ein neuer Thread, üblicherweise mit input/output-Streams im Konstruktor starten, damit das Handling komplett vom ServerSocket getrennt ist. Letztere kann dann wieder accept() machen und auf einen weiteren Client warten.
Pseudo-Code

```
Socket clientSocket=serverSocket.accept();        
    AcceptClient obClient=new AcceptClient(clientSocket.getInputStream(), clientSocket.getOutputStream());
}
class AcceptClient extends Thread
```


----------



## chrilux (22. Dez 2013)

```
public void run() {
        synchronized (this) {
            this.runningThread = Thread.currentThread();
        }
        // solange unterbrochen nicht true ist...
        while (!isShutdown) {

            gestartet = oeffneServerSocket();

            if (gestartet) {
                System.out.println("Server ist gestartet....! Höre auf dem Port: " + port);
            }

            try {

                this.clientS = serverS.accept();

                Handler handler = new Handler(clientS);
                Thread thread = new Thread(handler);
                thread.start();

            } catch (SocketTimeoutException e) {

                System.out.println("Kein Client gehört... versuche es weiter!");

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
```

So gehts schonmal...

im handler steht jetzt nur der code der vorher in der rund des servers stand...

er horcht aber auf jeden fall weiter 

Jetz müsste man nur noch wissen, wer die nachricht geschickt hat und ALLE Clients müssen nachrichten empfangen...


----------



## turtle (23. Dez 2013)

> Jetz müsste man nur noch wissen, wer die nachricht geschickt hat und ALLE Clients müssen nachrichten empfangen...



Wer die Nachricht geschickt ist ja eindeutig. Es ist der Thread, den du erzeugt hast bei dessen Verbindungsaufbau.

Und der Server hat ja für alle Clients Threads erstellt und deshalb weißt du auch deren OutputStreams und kannst da was hin senden.

Aber aufgemerkt, du musst hier mit konkurrierenden Zugriff auf Variable des Servers rechnen. Es kann ja passieren, das ein Thread eine Nachricht verschicken möchte UND gleichzeitig kommt ein neuer Client hinzu.  Das bedeutet, das du dir Gedanken über die Synchronisierung im Server machen musst.


----------



## chrilux (23. Dez 2013)

Kann ich mir ne Liste von den Chatteilnehmern erstellen, dessen Nickname und Streams darin speichern oder ändern sich die Streams bei jedem neuen Accept?


----------



## turtle (23. Dez 2013)

Gute Idee

TCP-Verbindungen bleiben bestehen, solange bis einer die Verbindung beendet. Also bleiben auch die Streams bestehen. 

Hier kann es passieren, das ein Client einfach abstürzt und der Server glaubt, das am anderen Ende des Streams noch der Client ist und den Text anzeigt. Aber dem ist nicht immer so und der Server schickt Daten dann ins Nirwana. Daher kann es zu jedem Zeitpunkt zu Problemen mit dem Netzwerk geben und praktisch alle Methoden schmeißen daher eine IOException oder SocketException bei Problemen.


----------



## chrilux (23. Dez 2013)

Okay... dann kann man ja sagen, dass die Liste max. 3 Teilnehmer groß sein darf und dann beim server ne funktion sendToAll synchronized machen der die liste durchgeht in an die inputstreams sendet... ist das schonmal ne idee? werde mir das wohl alles nochmal aufzeichnen müssen


----------



## turtle (23. Dez 2013)

Hört sich sehr gut an:toll:


----------



## chrilux (24. Dez 2013)

Habe mir jetzt mal um den Server gedanken gemacht:

Klasse Server:
Wird als Thread gestartet, besitzt die Methode shutDown() zum herunterfahren

Klasse ConnectionHandler:
Ist ein Thread, der Verbindungen entgegegen nimmt....

Klasse UserListe:
Ist eine Liste die die Streams, Username evtl. IP der aktiven Teilnehmer speichert.


Ist alles erstmal sehr primitiv...

Problem ist nur dass ich noch überprüfen muss wann ein User nicht mehr aktiv ist....


----------



## turtle (24. Dez 2013)

> Problem ist nur dass ich noch überprüfen muss wann ein User nicht mehr aktiv ist....



Es gibt zwei Möglichkeiten, das ein User nicht mehr aktiv ist.

Er sendet ein spezielles Kommando zum Server (beispielsweise "Exit"). Damit weiß der Server das diese Verbindung inaktiv ist und aus der Liste entfernt werden kann. (Normales Logout)

Zweite Möglichkeit: Der User schaltet seinen Rechner einfach aus. (Crash-Szenario)

Dies "kann" der Server mitbekommen, indem normalerweise Clients, während der ganzen Verbindungszeit, periodisch ein Lebenszeichen (sogenannter Heartbeat (HB)) senden. Daran erkennt der Server, da der HB ausbleibt, das der Client wohl tot ist und kann diesen dann ebenfalls aus der Liste entfernen.

Vielleicht helfen diese Infos?


----------

