# Threads &  Streams



## JUserToto (26. Jan 2013)

Hallo zusammen,

ich wage mich heute zum ersten Mal richtig an die Netzwerkprogrammierung, komme aber nicht so richtig weiter.

Zum Einstieg wollte ich ein kleines Spiel schreiben. Die Struktur sollte wie folgt ablaufen:

Der Server läuft im Hintergrund und wird zu Beginn gestartet. 
Er läuft in einem Thread. In der run() wird zyklisch socket = server.accept(); aufgerufen, damit er ständig Verbindungen annehmen kann.

Im Spiel soll nun z.B. jede Sekunde ein Datenabgleich mit dem Server gemacht werden. Es läuft also ein Thread, der jede Sekunde die Spieldaten vom Server läd.

Hier mal die Codes (auf das Wichtigste beschränkt, so also nicht lauffähig):

Server:


```
public class ServerRunnable implements Runnable {

    private ServerSocket server;
    private Spiel spiel;
    private Socket socket;
    private ObjectInputStream in;
    private ObjectOutputStream out;

    public ServerRunnable() throws IOException {
            spiel = new Spiel();
            server = new ServerSocket(57070);
    }

    @Override
    public void run() {
        while (true) {
                socket = server.accept();
                
                out = new ObjectOutputStream(socket.getOutputStream());
                in = new ObjectInputStream(socket.getInputStream());
                
                Object input;
                if ((input = in.readObject()) != null) {
                    if (input instanceof Integer) {
                        Integer serverCode = (Integer) input;
                        if (serverCode.equals(ServerCodes.GIB_SPIEL)) {
                            out.writeObject(spiel);
                        }
                    }
                }            
        }
    }
```

Client:


```
@Override
    public void run() {
        while (true) {
             Thread.sleep(1000);
             this.spiel = gibSpiel();
        }
    }

    public Spiel gibSpiel() {
        Spiel spiel = null;

        out =  new ObjectOutputStream(this.serverSocket.getOutputStream());
        out.writeObject(ServerCodes.GIB_SPIEL);
        out.flush();

        in = new ObjectInputStream(this.serverSocket.getInputStream());
        spiel = (Spiel) in.readObject();

        return spiel;
    }
```

Der erste Aufruf von gibSpiel() liefert den gewünschten Erfolg. Es wird ein gültiges Spiel zurück gegeben. Beim zweiten Aufruf blockiert  "new ObjectInputStream(this.serverSocket.getInputStream());"
den Thread, es wird also weder der Code gesendet, noch ein Ergebnis empfangen.

Funktioniert diese Strategie generell nicht oder hab ich die Client-Server Kommunikation noch nicht verstanden?

Danke für Antworten 

edit: "Spiel" könnte übrigens besser "Spieldaten" heißen. Das sind Daten wie z.B. die Positionen der Spieler.

lg Toto


----------



## Bizarrus (26. Jan 2013)

Was mich ein wenig irritiert: Warum wird "jede Sekunde" geprüft ob Daten vorhanden sind?
Du nutzt doch ein Socket. Sobald Daten vorhanden sind, werden die doch verarbeitet. Da brauchst du nicht manuell sagen "jede Sekunde" - Dies geschieht automatisch.

Wenn der Client etwas sendet, wird dies direkt vom Server ausgeführt. Andersherum ebendso.

Du hast, wie du bereits schreibst, dies noch nicht ganz verstanden.
Der Blockiert aus dem Grund, weil du bei socket.accept() kein Thread benutzt.

Ich schreib dir da gleich mal ein wenig Source, dauert aber noch 1-2 Stunden, da ich leider noch an einem Kundenprojekt sitze.


----------



## JUserToto (26. Jan 2013)

Hallo,

danke für die Antwort. Ich studiere Informatik, aber in Sachen Netzwerkprogrammierung haben wir leider noch garnix gemacht (5. Semester) 

Im Moment versteh ich es so:

Der Server verwaltet die Spieldaten (d.h. die Positionen der Spieler, Punkte etc.), weil diese für alle Spieler im Netzwerk identisch sein müssen.

Man kann zuvor im Menü einen Server eröffnen und diesem (oder einem externen) beitreten.

Der Client muss also irgendwie an die Daten des Servers "ran kommen". Praktisch gesehen: Die GUI will den Spieler abbilden und brauch die Koordinaten. Sie fragt also den Server nach den Koordinaten.
Oder soll etwa der Server das neuzeichnen veranlassen, wenn er neue Koordinaten bekommt? Dann müsste der Server alle seine Clienten kennen und die größte Arbeit übernehmen?


----------



## Bizarrus (27. Jan 2013)

So, hier mal nun ein Beispiel:
Der Server akzeptiert jeden Clienten und setzt diesen in ein Thread-Objekt:

```
public void run() {
	        while (true) {
	        	try {
					socket = server.accept();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
					
	            new Client(socket, spiel).start();     
	        }
    }
```

Und jeder Client ist dann über eine Klasse instanziert:

```
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class Client extends Thread implements Runnable {
	protected Socket s;
	protected Spiel game;
    private ObjectInputStream in;
    private ObjectOutputStream out;
	
	Client(Socket sock, Spiel game) {
		this.s = sock;
		this.game = game;
	}
	
	@Override
	public void run() {
		try {
			out = new ObjectOutputStream(s.getOutputStream());
			in = new ObjectInputStream(s.getInputStream());
	        
	        Object input;
	        if ((input = in.readObject()) != null) {
	            if (input instanceof Integer) {
	                Integer serverCode = (Integer) input;
	                if (serverCode.equals(ServerCodes.GIB_SPIEL)) {
	                    out.writeObject(this.game);
	                }
	            }
	        }
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
         
	}

}
```

Die ganzen Clients könntest du nun noch in einer HashMap (o.ä.) beim Server mit hereinsetzen, damit du vom Server immer alle Clients ansprechen kannst. Die Client-Klasse selber müsste nun beispielsweise auch eine send() methode besitzen.


----------



## JUserToto (27. Jan 2013)

Hallo nochmal,

ich glaube ich verstehe in welche Richtung das geht. Einen groben Denkfehler habe ich nun schon erkannt. (Ich dachte die Accept Methode reagiert auf eingehen Daten und nicht auf eingehende Verbindungen)
Ich erzeuge also bei jedem erfolgreichen Verbindungsversuch einen neuen Client als Thread...
Ich sammle die Clients im Server.

Mir ist aber nicht ganz klar was der Client tut. Er muss ja trotzdessen an die aktuellen Spieldaten kommen und die sind ja nicht "einfach da", er muss sie irgendwie vom Server anfragen... mir ist nicht klar wie ich da an einer "alle paar Sekunden fragen" Struktur wegkomme...

lg Toto


----------



## Bizarrus (27. Jan 2013)

> Ich dachte die Accept Methode reagiert auf eingehen Daten und nicht auf eingehende Verbindungen
> [...]
> Ich erzeuge also bei jedem erfolgreichen Verbindungsversuch einen neuen Client als Thread...


Genau, die accept aggiert ausschließlich auf einem connect und blockt dann den Prozess. Deswegen muss zwangsläufig die "gerade verbundene Socket" in einem Thread untergebracht werden, damit weitere connections von anderen Clienten angenommen werden kann.



> Ich sammle die Clients im Server.


Genau, wäre empfehlenswert, da du ja irgendwie die Clienten im nachhinein "bedienen" möchtest.



> Er muss ja trotzdessen an die aktuellen Spieldaten kommen und die sind ja nicht "einfach da", er muss sie irgendwie vom Server anfragen...


Jein. Der Client empfängt. Sobald neue "Daten" verfügbar sind, musst du dem Clienten dies nur mitteilen.

Pass auf, kleines Beispiel an einem "Spiel" - Beispielsweise bei einem Schach-Spiel.
Zwei Clienten connecten zum Server und werden zusammen (da das Spiel mit Zweispieler noch nicht "voll" ist) in einer "AktuellesSpiel" Klasse gesetzt (Jedes Spiel, was gerade läuft besitzt eine Instanz und wird im Server in einer HashMap gestored). Beide Clienten melden sich bei einem laufendem Spiel (noch nicht vollem Spiel) an und bei diesem vorgang teilt der Server die aktuellen Spielstände mit (Beispielsweise Positionen der einzelnen Püppchen).

Wenn nun Client A eine Figur "bewegt", teilt dieser das dem Server mit, so nach dem Motto "Puppe 7 auf X,Y". Der Server reagiert darauf und speichert die neue Position in der Klasse "AktuellesSpiel" und teilt danach dem anderen Clienten die veränderung mit.

Hierbei müsstest du nun dir eine Art Kommunikations-Protokoll erarbeiten. Denn beide Seiten müssen in irgendeiner Form ja wissen, was die andere Seite senden kann.

In dem Falle könnntest du verschiedene Methoden nutzen um dies zu Visualisieren. entweder du nimmst beispielsweise XML oder du erarbeitest dir eine einfache Textbasierte Kommunikation mit irgendwelchen Trennzeichen:

[XML]
<spieler>
	<action>Wechsle Position</action>
	<wasdenn>Eine Puppe</wasdenn>
	<position>
		<von_x>0</von_x>
		<von_y>0</von_y>
		<nach_x>1</nach_x>
		<nach_y>1</nach_y>
	</position>
</spieler>
[/XML]


```
// AKTION:VON_X:VON_Y:ZU_X:ZUY;
change_position:0:0:1:1
```

Dies wäre einmal ein grobes Beispiel, wie du derartiges Visualisieren könntest..


----------



## JUserToto (27. Jan 2013)

Hallo,

vielen, vielen Dank für die ausführliche Erklärung. Ich denke ich habe es nun verstanden und schaue mal, wie ich zurecht komme. 
lg Toto


----------



## JUserToto (27. Jan 2013)

Hallo nochmal,

ich dachte eigentlich meine Fragen hätten sich erledigt. Da es aber nach wie vor nicht so funktioniert, wie ich es gerne hätte frag ich besser nochmal nach.

Ich habe nun den Server entsprechend umgeschrieben.


```
public Server()  {
            /* Sammlung der verbundenen Sockets. */
            sockets = new HashMap();

            /* Server erstellt ein neues Spiel */
            spiel = new Spiel();

            /* Erstellen des Serversockets. */
            server = new ServerSocket(57070);

            /* Thread der auf Verbindung zu Clients wartet. */
            new Thread(new ClientListener()).start();
}
```

Der Server startet also einen Thread, der auf neue Clienten wartet. Die run Methode sieht folgendermaßen aus:


```
@Override
        public void run() {
            while (true) {
                    /* Server nimmt neue Clienten an... */
                    Socket socket = server.accept();
                    
                    /* Client wird erzeugt und gestartet. */
                    Client con = new Client(socket,
                            socket.getInetAddress().getHostName(), spiel);
                    new Thread(con).start();
                    
                    /* Socket wird der Socketsammlung hinzugefuegt. */
                    sockets.put(socket.getInetAddress().getHostName(), socket);
            }
        }
```

Hier ist mir schon nicht ganz klar: Wieso startet der Server den Thread des Clienten? Zum Clienten gehört eine GUI, müsste diese nicht mit diesem Code immer auf dem Server-System erstellt werden? Würde ich den Thread aus dem Menü heraus starten lassen, würde mir jedoch der Socket des Clienten fehlen... Da drehe ich mich gedanklich im Kreis.

Der eigentliche Fehler der nun auftritt ist aber Folgender... Im Konstruktor des Clients versuche ich, mir die Streams geben zu lassen.


```
public Client(Socket socket, String spieler, Spiel spiel) {
        this.serverSocket = socket;
        out =  new ObjectOutputStream(serverSocket.getOutputStream());
        in =  new ObjectInputStream(serverSocket.getInputStream());
...
}
```

Hier stürzt das Programm ab mit dem Fehler: "java.net.SocketException: Software caused connection abort: recv failed" 


lg Toto


----------



## Stroker89 (27. Jan 2013)

in und out gehören in einen try catch Block.


```
try {
    in = new ObjectInputStream(socket.getInputStream());
    out = new ObjectOutputStream(socket.getOutputStream());
} catch (StreamCorruptedException e1) {
    e1.printStackTrace();
} catch (IOException e1) {
    e1.printStackTrace();
}
```

Kannst mal den code Zeigen wie du Daten empfängst und wie du zu dem Server connectest? Sind Server und Client in einem Programm oder was?

Gruß


----------



## Bizzi (27. Jan 2013)

Ich bins, Bizarrus.
Du hast die accept methode immer noch im server.
ich schick gleich mal einen teil meiner server/client architektur.

Entferne den Thread im Server, packe alles vom serverthread dort rein, und erstelle ein Thread bei accept!


----------

