# Problem mit Server-Client-Kommunikation



## Xeonkryptos (5. Okt 2011)

Moin Leute,

ich hab das Problem, dass ich momentan an einem Server und den dazugehörigen Client arbeite und das Programm steckt halt noch in den Kinderschuhen. 
Nun ist mein Problem, ich gebe einen normalen, einfachen Text in eine JTextArea ein und habe natürlich ein Button, mit dem ich über ein ActionEvent die Daten an meinen Server senden möchte. 
	
	
	
	





```
@Override
		public void actionPerformed(ActionEvent e) {
			if (e.getActionCommand().equals("btnSend")) {
				messageWriter.println(taMessageField.getText());
				taMessageProcess.append("\n" + messageWriter.checkError());
			}
		}
```
Nun habe ich natürlich getestet, wann der Server die Nachricht erhält und wie man sehen kann, ob es eine Fehlermeldung gibt. Die Nachricht erscheint komischerweise den Server erst nach dem 3. Buttondruck, während ich es aber schon 3x versendet habe, kommt es nur 1x (!) an. Ich habe es schon geschafft, dass es nach dem 2. Drücken des Buttons am Server ankommt, es soll aber schon nach dem einmaligen Drücken beim Server ankommen und ich weiß nicht, wieso ich erst mehrmals drücken muss???

Zur Info: meine Variable "messageWriter" ist als PrintWriter deklariert, autoFlush() ist im Konstruktor auf "true" gesetzt und er erhält den OutputStream von dem Server, sofern eine Verbindung zu diesem hergestellt werden konnte.

Hoffe ihr könnt mir bei meinem Prob helfen.


----------



## SlaterB (5. Okt 2011)

rufe doch einfach testweise flush() auf, wenn dir das schon als ein interessanter Punkt auffällt,
ansonsten wäre für mich kompletter Quellcode interessant, in einem Programm von einer main aus 
Server starten + Client aufmachen der entweder sparsamerweise mit paar sleeps Verzögerung sendet oder notfalls mit einfacher GUI und deinen Button,

Programm starten testen fertig mit Fehler oder nicht, 
mit etwas Pech kann natürlich bei anderen alles laufen, nur bei dir der Fehler auftreten..


----------



## Xeonkryptos (5. Okt 2011)

Code Client:


```
import client.ChatClient;
import client.GUI.thread.ClientGUIThread;

public class ClientGUI extends JFrame {

	private ChatClient client;
	private BufferedReader messageReader;
	private PrintWriter messageWriter;

	private ClientGUIThread readerThread;

	public ClientGUI() throws IOException, AWTException {
		super("Vampire - The Dark Ages-Chat");

		// GUI gedöns

		chatClient();
	}

	private void chatClient() {
		client = new ChatClient("localhost", 3141);
		try {
			messageReader = new BufferedReader(new InputStreamReader(
					client.getInputStream()));
			messageWriter = new PrintWriter(client.getOutputStream(), true);
		} catch (final IOException e) {
			// ... Reaktion ist unrelevant, da nur Information durch Anzeige
		initReaderThread(messageReader, taMessageProcess);
	}

	private void initReaderThread(BufferedReader messageReader,
			JTextArea taMessageProcess) {
		readerThread = new ClientGUIThread(messageReader, taMessageProcess);
		readerThread.start();
	}

	class Listener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			if (e.getActionCommand().equals("btnSend")) {
					messageWriter.println(taMessageField.getText());
					taMessageProcess.append("\n" + messageWriter.checkError());
			}
		}
	}
}
```

Code Server:


```
public class ChatServer {

	private ServerSocket serverSocket;
	private BufferedImage img;

	public ChatServer(int port) throws IOException, AWTException {
		setServerPort(port);
		waitForConnection();
	}

	private void setServerPort(final int port) {
		try {
			serverSocket = new ServerSocket(port);
		} catch (final IOException e) {
			// ...
		}
	}

	private void waitForConnection() {
		while (true) {
			Socket client = null;

			try {
				client = serverSocket.accept();
			} catch (IOException e) {
				e.printStackTrace();
			}
			ClientConn thread = new ClientConn(client);
			thread.start();
		}
	}
	
	public InetAddress getServerIP() {
		return serverSocket.getInetAddress();
	}
}
```

Testweise hab ich schon flush() aufgerufen, es hat leider nur nichts gebracht...

Die restlichen Klassen in meinem Code oben: ClientConn ist nur eine Klasse, die von Thread erbt und in der run()-Methode einfach nur eine Endlosschleife ausführt, die dann halt aus dem BufferedReader alles ausliest und es über den PrintWriter wieder an den Client schickt... Thread für den Server.

ClientGUIThread arbeitet mit dem selben Prinzip, wie es die ClientConn für den Server tut, nur dass dieser einfach nur die Infos ausliest und die Infos dann auf meinem JTextArea taMessageProcess anzeigt.

Die main-Methoden hab ich halt auf andere  Klassen ausgelagert, aber die sind ja schnell geschrieben


----------



## Michael... (5. Okt 2011)

Bevor man sich dem "GUI Schnickschnack" widmet, sollte man sich erst mal auf's wesentliche Beschränken: Der Kommunikation zwischen Server und Client.
Ich habe dann Code gesehen und plötzlich keine Lust mehr diesen zu lesen und nach relevantem Inhalt zu suchen ;-)

Ich verstehe, dass man sich eher dem zuwendet was man kann und wo man sofort Erfolgserlebnisse hatte, aber das ganze drum herum erschwert Dir und allen anderen die Fehlersuche.


----------



## Xeonkryptos (5. Okt 2011)

Hab meinen Beitrag mal editiert und auf den wirklich relevanten Code reduziert.



			
				Michael... hat gesagt.:
			
		

> Bevor man sich dem "GUI Schnickschnack" widmet, sollte man sich erst mal auf's wesentliche Beschränken



Ich hab damit angefangen, da ich einfach im Grunde diesen SendButton und das Textfeld haben wollte, um mir die Arbeit über die Konsole sparen zu können. Daraufhin hab ich halt einfach den groben Rest gemacht und dann mit der Kommunikation begonnen, aber wie man es macht ist im Endeffekt egal, nur dass man an sein Ziel kommt und der Code so gut wie möglich abstrakt ist 

Ist aber auch egal. Code ist reduziert und alles Frame-unrelevante rausgenommen, sodass das lesen jetzt leichter fallen sollte =)


----------



## Michael... (5. Okt 2011)

und den relevanten Thread, der den InputStream des Clients ausliest vergessen ;-)


----------



## Xeonkryptos (5. Okt 2011)

Michael... hat gesagt.:


> und den relevanten Thread, der den InputStream des Clients ausliest vergessen ;-)



Den hab ich eigentlich oben erklärt, aber nun gut, dann noch ein bisschen mehr =)

ServerThread (Auslesen und weiterleiten): 
	
	
	
	





```
public class ClientConn extends Thread {

	private Socket client;
	private static BufferedReader serverRead = null;
	private static PrintWriter serverWrite = null;

	public ClientConn(Socket client) {
		this.client = client;
	}

	@Override
	public void run() {

		try {
			serverRead = new BufferedReader(new InputStreamReader(
					client.getInputStream()));
			serverWrite = new PrintWriter(client.getOutputStream(), true);
		} catch (final IOException e) {
			// ...
		}

		try {
			while (true) {
				if (serverRead.readLine() != null) {
					System.out.println(serverRead.readLine());
					serverWrite.println(serverRead.readLine());
				}
			}
		} catch (final IOException e) {
			//...
		}
	}
}
```

Zur Info, bevor Kritik kommt, ich hab serverRead und serverWrite static gemacht, damit ich diese in einer statischen Disconnect-Methode verwenden kann!

Und der ClientThread (ausschließlich einlesen und ausgeben) 
	
	
	
	





```
public class ClientGUIThread extends Thread {

	private BufferedReader messageReader;
	private JTextArea taMessageProcess;

	public ClientGUIThread(BufferedReader messageReader,
			JTextArea taMessageProcess) {
		this.messageReader = messageReader;
		this.taMessageProcess = taMessageProcess;
	}

	@Override
	public void run() {
		while (true) {
			try {
				if (messageReader != null) {
					taMessageProcess.append(messageReader.readLine() + "\n");
// einfach nur für den Verlauf wichtig
				}
			} catch (IOException e) {
				// ...
			}
		}
	}
}
```


----------



## SlaterB (5. Okt 2011)

ChatClient fehlt noch, den GUI-Code wegzukürzen so dass man das Programm dann gar nicht mehr ausführen kann ist auch nicht gerade ein Ziel in meinen Augen,  wenn dann durch einfacheren Konsolen-Ablauf ersetzen,

immer ein vollständiges lauffähiges Programm inklusive main-Methode posten, dann gibts weniger zu diskutieren,
möglichst kurz natürlich

---------


aber die fehlenden Klassen haben ja nun doch Hinweise gebracht:

```
if (serverRead.readLine() != null)
                {
                    System.out.println(serverRead.readLine());
                    serverWrite.println(serverRead.readLine());
                }
```
3 readLine()-Aufrufe verlangen 3 Eingaben, oder?


----------



## Xeonkryptos (5. Okt 2011)

SlaterB hat gesagt.:


> 3 readLine()-Aufrufe verlangen 3 Eingaben, oder?



Hmm... das macht Sinn.  Werde das mal anpassen und wenn weitere Fehler bestehen, den Code konsolenbasiert aufbauen und hier bearbeiten.  Leider ruft das RL =)

Gebe nochmal Auskunft =)


----------



## Michael... (5. Okt 2011)

Xeonkryptos hat gesagt.:


> Den hab ich eigentlich oben erklärt, aber nun gut,


Und genau da steckt schon mal ein Fehler:


Xeonkryptos hat gesagt.:


> ```
> if (serverRead.readLine() != null) {
> System.out.println(serverRead.readLine());
> serverWrite.println(serverRead.readLine());
> ```


Hier wird drei mal in Folge readLine() auf den Input aufgerufen, mit der Folge:
- jede erste gelesene Zeile wird verworfen
- jede zweite wird auf System.out ausgegeben
- jede dritte Zeile wird über den Output zurückgeschrieben.


Xeonkryptos hat gesagt.:


> Zur Info, bevor Kritik kommt, ich hab serverRead und serverWrite static gemacht, damit ich diese in einer statischen Disconnect-Methode verwenden kann!


Keine Kritik, nur ein Hinweis: Wenn daraus ein Chat werden soll muss das static weg.


----------



## Xeonkryptos (5. Okt 2011)

Michael... hat gesagt.:


> Keine Kritik, nur ein Hinweis: Wenn daraus ein Chat werden soll muss das static weg.



Hab es geändert und es funktioniert einwandfrei. Das static habe ich auch rausgenommen, dennoch bleibt mir jetzt die Frage, wieso muss bei einem möglichen Chat das static raus? Mir ist bis jetzt keine Einschränkung daran aufgefallen? Du hast einen Grund und ich würde ihn gern hören trotz erledigtem Thema. =)


----------



## SlaterB (5. Okt 2011)

offensichtlich weil der Server mehrere ClientConn gleichzeitig hat mit jeweils eigenen serverRead usw., 
unabhängig davon wie schlecht das benannt ist und ob man das generell umbauen sollte

static ist nur einmal da, mehrere werden gebraucht -> nicht static, Objekte eben


----------



## Xeonkryptos (5. Okt 2011)

SlaterB hat gesagt.:


> static ist nur einmal da, mehrere werden gebraucht -> nicht static, Objekte eben



Stimmt stimmt. An so etwas denkt man leider nicht, wenn man sich da erst hereinarbeitet... Über dieses Prob wäre ich dann wohl erst später gestolpert.  Das nächste mal denkt man eher an so etwas


----------



## Xeonkryptos (5. Okt 2011)

SlaterB hat gesagt.:


> unabhängig davon wie schlecht das benannt ist und ob man das generell umbauen sollte



Mir lässt das jetzt irgendwie keine Ruhe mehr.  Ich habe es mal anderst umbenannt, wodurch es klarer werden sollte, wofür die Variablen jetzt genau stehen, aber ich würde gerne wissen, wie du das generell umgebaut hättest? Ich habe den Ansatz aus einem CodeBeispiel von einem programmierten einfachen Chat herausgeholt (Kein Copy-Paste!) und darauf halt aufgebaut.

Mir geht es nicht um vorgefertigten Code, sondern um das Prinzip, da ich ja selber das Lernen will. Ich bin jetzt kein Java-Anfänger, aber auch kein Java-Profi... irgendetwas dazwischen halt und bin für Ratschläge und Verbesserungsvorschläge offen 

Gruß
Xeonkryptos


----------



## SlaterB (5. Okt 2011)

ich habe keinen bestimmten Änderungsvorschlag, wenn du so fragst ist das für mich ok umgesetzt, 
ich wollte das nur nicht als gegeben bestätigen, nur static weg und final


----------

