# Nur die erste Nachricht kommt beim Server an



## memo1990 (17. Mrz 2014)

Hallo zusammen,

bin gerade in die Netzwerkprogrammierung eingetaucht und habe für mich als Beispiel auch schon ein Client Server Programm geschrieben. Soll ein Chat-Programm sein. Das Problem aber ist, das nur die erste Nachricht übermittelt wird. Den Fehler habe ich soweit nicht gefunden.

Hier der Code:

*Server:*



Spoiler





```
package client_server;

import java.io.*;
import java.net.*;

class Server {
	private ServerSocket server;
	private Socket client;
	
	Server(int port) {
		try {
			server = new ServerSocket(port); // Server starten
			System.out.println("Server gestartet!");
			
			while(true) {
				client = server.accept(); // Wartet hier bis ein Client sich meldet
				
				// Sobald sich ein Client gemeldet hat wird ein eigener Thread für ihn erstellt, damit diese Schleife auf weitere Verbindungen warten kann
				Thread t = new Thread(new ClientHandler(client));
				t.start(); // Thread wird gestartet
			}
			
		} catch(IOException e) {
			
		}
	}
	
	public static void main(String[] args) {
		Server s = new Server(5555);
	}
}

class ClientHandler implements Runnable {
	Socket client;
	
	private BufferedReader inReader;
	private String sIn;
	
	ClientHandler(Socket c) {
		client = c;
	}
	
	public void run() {
		try {
			// Streams
			inReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
			sIn = inReader.readLine();
			System.out.println(sIn);
		} catch (IOException e) {
			
		}
	}
}
```




*Client:*



Spoiler





```
package client_server;

import java.io.*;
import java.net.*;
import java.util.Scanner;

class Client {
	private Socket client;
	
	private BufferedWriter outWriter;
	private String message;
	
	private Scanner scan;
	
	Client(String host, int port) {
		try {
			client = new Socket(host, port); // Client starten und durch Angabe von "Host" und "Port" mit Server verbinden
			System.out.println("Client gestartet!");
			
			starteChat();
		} catch (UnknownHostException e) {
			System.err.print("Server nicht gefunden!");// Falls Server nicht gefunden wurde
		} catch (IOException e) {
			System.err.print("I/O Fehler!");
		}
	}
	
	private void starteChat() {
		try {
			outWriter = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
			scan = new Scanner(System.in); // Eingabe von Konsole lesen
			
			while (true) {
				System.out.print("Nachricht eingeben: ");
				message = scan.nextLine();
				
				if (message.equals("exit")) // Wenn "exit" eingeben wird, wird der Client beendet
					break;
				
				outWriter.write(message); // Eingabe an Server schicken.
				outWriter.newLine(); // Damit auf der anderen Seite, der BufferedReader mit der Methode readLine() vom Server, auch das Ende der Zeile erkannt wird, muss explizit einen Zeilenumbruch mitgeschickt werden
				outWriter.flush(); // Damit das geschriebene, der vorerst im Puffer liegt, sofort verschickt wird
			}
			
			outWriter.close(); // Stream schließen
		} catch (IOException e) {
			
		}
		
	}
	
	public static void main(String[] args) {
		Client c = new Client("localhost", 5555);
	}
}
```




Danke schon mal im Voraus für eure Unterstützung.


----------



## Sen-Mithrarin (18. Mrz 2014)

versteh die frage nich ... dein code macht genau das was er soll
vielleicht solltest du dir noch mal das eine oder andere tutorial ansehen


----------



## Shine (19. Mrz 2014)

Um das mal etwas genauer zu verdeutlichen...
-------------------------------------------------
Dein Server wartet in einer Schleife, bis sich ein Client verbindet... (Soweit OK)

Dann erstellt dein Server einen Thread, und diesem wird der Socket als Parameter über geben... (auch noch OK)

So... Was passiert jetzt in diesem Thread?

In diesem Thread wird (in der run-Methode) eine Zeile der empfangenen Daten gelesen und ausgegeben...

Und weiter? Was genau passiert denn mit einer Methode, wenn sie durchlaufen wurde?

Und woher weiß denn der Server, dass der Client überhaupt schon Daten gesendet hat, die er lesen und ausgeben kann?

Denk mal über diese Fragen nach und überleg dir, wie du die Antworten darauf auf deinen Quellcode übertragen kannst.

Schau dazu am besten auch mal in der Java-Doc nach, was es da so für Methoden bei den Streams gibt und wofür diese gut sind.


----------



## memo1990 (20. Mrz 2014)

Danke. Bin die Fragen durchgegangen und es klappt jetzt. Ich wusste bis jetzt auch nicht, dass die Read-Methoden vom java.io-Paket blockierend sind.

Hier der abgeänderter Codeabschnitt von Klasse Server:


```
public void run() {
		try {
			// Streams
			inReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
			sIn = inReader.readLine(); // Blockiert bis eine Nachricht vom Client empfangen wird
			
			while (!sIn.equals("exit")) {
				System.out.println(sIn);
				sIn = inReader.readLine(); // Blockiert erneut bis eine Nachricht vom Client empfangen wird
			}
		} catch (IOException e) {
			
		}
	}
```

Was mir aber noch durch den Kopf geht ist die folgende Frage von dir:



> Und woher weiß denn der Server, dass der Client überhaupt schon Daten gesendet hat, die er lesen und ausgeben kann?




Ich könnte mittels der *ready()* Methode von BufferedReader prüfen, ob Bytes zur Verfügung stehen die gelesen werden können. Wenn aber *false* zurück kommt, wird ja wieder die Methode beendet und es wird später wieder nichts gelesen.


----------



## Sen-Mithrarin (21. Mrz 2014)

finger weg von ready() ... ist ne ziemliche fehl-implementierung

ready() gibt true wenn garantiert werden kann das der nächste call von read() definitiv nicht blockierend ist ...
false hingegen ist aber kein garant dafür das ready() nächste mal blockieren wird

wozu es diese methode gibt weis ich nicht ... könnte man eigentlich als deprecated ausm verkehr ziehen ...


----------



## Curiosus (21. Mrz 2014)

Entschuldigt, dass ich mich hier mal mit einmische aber das man ready() nicht nutzen sollte wusste ich noch nicht, da man diesen Befehl auch in vielen Tutorials sieht und das hat mich jetzt ziemlich neugierig gemacht. Was gäbe es denn da für Alternativen?


----------



## Sen-Mithrarin (21. Mrz 2014)

ready() liefert einen bool

dieser bool kann zwei zustände haben : true und false ... und beide zustände haben eine bestimmte bedeutung

true : der nächste call von read() wird definitiv nicht blockieren
das heißt : man hat die gewissheit das man read() callen kann und definitiv ein ergebnis bekomt ... das geht dann so lange gut bis keine daten mehr vorhanden sind ... und in diesem fall sollte ready() dann auch false liefern ...
es macht aber keinen sinn in einem loop immer wieder zu fragen : sind daten da ? ... wenn man einfach direkt read() callt und wenn daten da sind diese halt liest und wenn nicht halt darauf wartet
außerdem kommt es hier bei vielen anfängern zu dem fehler das entweder die loop-bedingung falsch implementiert wird ... also die funktionsweise dieser methode ausgehebelt wird ... oder wenn es dann doch mal richtig ist ein einfaches sleep() fehlt um den cpu vor 100% auslastung zu bewahren

false : das system weis selbst nicht ob es sich sicher sein kann ob read() blockiert oder nicht
was ist das denn für ein schrott ? entweder das system weis : jap, read() wird blockieren ... oder : nope, read() wird nicht blockieren
hier kommt es zum fehler : false ist lediglich das gegenüber von true ... aber hier aber nicht die direkt umgekehrte aussage zur folge
nur weil true bedeutet : jo, kannst lesen ... heißt false halt im umkehr nicht gleich : nope, kannst nicht lesen ... sondern halt nur das sich das system nicht 100% sicher ist das der nächste call von read() nicht blockierend sein wird ...
false bedeutet hier nicht das read() definitiv blockieren wird


in meinen augen ist diese methode so dermaßen nutzlos ... man sollte sie wirklich aus dem verkehr ziehen

bei live-kommunikation ... sei es nun über das terminal mit nem user oder über n socket mit ner gegenstelle ... ready() macht keinen sinn ...
und bei "offline"-bearbeitung von daten kann man alles vorher "berechnen" und abgleichen > hier macht die methode noch weniger als gar kein sinn ...


----------



## Tobse (21. Mrz 2014)

Wie Sen-Marithan schon geschrieben hat ist es vollkommen legitim, einfach read aufzurufen. Read kümmer sich darum, dass Daten da sind. Es ist nur wichtig, dann auch zu beachten, wieviel tatsächluch gelesen wurde (rückgabewert).

Kleines Beispiel (Den Code aber so nicht verwenden wenn RAM wichtig ist):

```
public byte[] readData(InputStream in, int minBytes, int maxBytes)
{
    int bytesRead = 0;
    byte[] buffer = new byte[maxBytes];
    while ((bytesRead += in.read(buffer, bytesRead + 1, maxBytes - bytesRead)) < minBytes);
    byte[] fin = new byte[bytesRead];
    System.arraycopy(buffer, 0, fin, 0, bytesRead);
    return fin;
}
```


----------



## Shine (21. Mrz 2014)

@memo1990
Mal noch ne Anmerkung bezüblich des Codes in deinem 2. Post.

Du wartest zuerst, bis eine Nachricht ankommt und speicherst sie dann in eine Variable, danach gehst du in die while-Schleife und machst da genau das gleiche. Denk mal kurz darüber nach, ob es da nicht evtl. eine Schleife gibt, mit der das etwas eleganter gelöst werden kann.


----------



## memo1990 (21. Mrz 2014)

Danke soweit für eure Antworten.

@Shine
Etwa so? 


```
public void run() {
		try {
			// Streams
			inReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
			
			while (true) {
				if (sIn.equals("exit"))
					break;
				
				sIn = inReader.readLine(); // Blockiert bis eine Nachricht vom Client empfangen wird
				System.out.println(sIn);
			}
			
			inReader.close();
		} catch (IOException e) {
			
		}
	}
```


----------



## Shine (22. Mrz 2014)

Naja, die Bedingung zum Schleifenabbruch sollte wenn möglich schon in den Schleifenkopf aber nimm lieber ne do-while-Schleife. (Hmmm... heißt das bei ner do-while-Schleife eigentlich immer noch Schleifenkopf, oder muss man es da Schleifenfuß nennen? ^^)


----------



## memo1990 (23. Mrz 2014)

Danke @Shine.

Soweit klappt jetzt alles. :applaus:

Eine letzte Frage abseits hätte ich noch: Wie kann man Daten die zwischen Server und Client geschickt werden filtern? Wo her weiß ich ob jetzt eine Nachricht, Nickname, Objekt oder aber auch eine binäre Datei geschickt wird?


----------

