# Seltsames Verhalten beim Empfangen von Daten über DataInputStream



## skappler (20. Aug 2012)

Hallo Leute.
Ich schreibe gerade einen kleinen Netzwerk Chat mit Client und Server Application. Beide funktionieren im Prinzip gleich, nur das der eine einen Server hostet und der andere connectet.
Das Grundgerüst steht soweit und funktioniert auch, nur empfängt der Client keine Daten. Wenn ich vom Server aus eine Nachricht sende wird sie vom Client nicht gelesen. Andersrum allerdings schon. Das wundert mich, da der Code zum empfangen von nachrichten bei beiden identisch ist. 
Was aber WIRKLICH seltsam ist, ist dass im Debug Modus von Eclipse alles reibungslos läuft. Wenn ich den Client debugge empfängt er alle Nachrichten vom Server korrekt. 

Hier der Code der fürs empfangen verantwortlich ist :

```
DataInputStream in = in = new DataInputStream(serverCon.getInputStream());
PrintStream out = new PrintStream(serverCon.getOutputStream());

...

public void listenForInput(){

		while(true){
			

			try {
				String text = in.readLine();
				outputDevice.append("Partner: "+text+"\n");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
```

Kann mir jemand helfen?


----------



## jnetdev (20. Aug 2012)

Mit dem bisschen Code und dieser Erklärung : NEIN
Da müsstest du schon mal alles komplett posten incl StackTrace.

Ansonsten hier noch ein ziemlich einfaches chat-system : http://www.java-forum.org/netzwerkp...bindung-server-fehlgeschlagen.html#post929410


Was ich nicht verstehe : "einer macht server der andere client" ... ist in meinen Augen völlig falsche idee. Es gibt EINEN zentralen Server und ansonsten nur Clients die sich zu diesem verbinden. Fertig aus ende.


----------



## skappler (20. Aug 2012)

Ok dann poste ich einfach mal alles^^ 
Das Problem ist: es gibt keine StackTraces. Damit könnte ich ja noch wenigstens was anfangen. Es passiert einfach garnichts.

Das mit dem Server hab ich so gemeint, dass es im moment nur möglich ist auf den Server einen einzigen Client zu connecten. Später wollte ich das ganze dann über Threads regeln, so dass mehrere Clients connecten können. 
Im moment ist das ganze so gedacht, dass ich zwei Programme laufen hab. Oberflächlich beide identisch, nur hat eines der beiden intern einen ServerSocket, auf den der andere nach eingabe der IP Adresse connectet.

Hier nun der Code (die Klassen aufteilung und das Klassendesign ist sehr schlecht. Das ist eine meiner größten schwächen, aber ich arbeite dran )

GUI.java

```
package com.net.gui;

import java.awt.FlowLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class GUI extends JFrame{

	public String title = "GUI";
	
	public static String VERSION = "0.1.0";
	
	JTextArea area;
	JTextField commandLine;
	
	public GUI(String title){
		
		setTitle(title);
		setSize(420,320);
		setResizable(false);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		setLayout(new FlowLayout());
		
		area = new JTextArea(16,34);
		area.setLineWrap(true);
		JScrollPane pane = new JScrollPane(area);
		
		add(pane);
		
		commandLine = new JTextField(34);
		commandLine.requestFocus();
						
		add(commandLine);
		
		setVisible(true);
		
	}
	

	public static String getVERSION() {
		return VERSION;
	}

	public JTextArea getTextArea() {
		return area;
	}

	public JTextField getCommandLine() {
		return commandLine;
	}
	
	public void appendText(String s){
		area.append(s);
	}
	
	public void setText(String s){
		area.setText(s);
	}
	
}
```

ServerFunc.java

```
package com.net.server;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ServerFunc implements KeyListener{
	
	
	boolean keyTyped = false;

	ServerSocket myServer;
	Socket clientCon;
	
	DataInputStream in;
	PrintStream out;
	
	JTextArea outputDevice;
	JTextField inputDevice;
	
	public static int PORTNUMBER = 3333;
	
	public ServerFunc(JTextArea o, JTextField i){
		
		outputDevice = o;
		inputDevice = i;
		
		i.addKeyListener(this);
		
	}

	public void start() throws IOException{
		myServer = new ServerSocket(PORTNUMBER);
		outputDevice.append("Waiting for Connection\nYour IP is "+InetAddress.getLocalHost().getHostAddress());
		
		
		
		clientCon = myServer.accept();
		
		outputDevice.append("Client connected\nSay Hello!\n");
		
		in = new DataInputStream(clientCon.getInputStream());
		out = new PrintStream(clientCon.getOutputStream());
		
		listenForInput();
		
	}
	
	@SuppressWarnings("deprecation")
	public void listenForInput(){
			while(true){
				

				try {
					String text = in.readLine();
					outputDevice.append("Partner: "+text+"\n");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
	}
	
	@Override
	public void keyPressed(KeyEvent arg0) {
		
		if (!keyTyped) {
			keyTyped = true;
			switch (arg0.getKeyCode()) {
			case KeyEvent.VK_ENTER:
				handleInput(inputDevice.getText());
				inputDevice.setText("");

				break;
			}
		}
			
	}

	private void handleInput(String text) {
		
		if(text.startsWith("/")){
			//handle Commands
			if(text.equals("/help")){
				outputDevice.append("No Help available\n");
			}
			
			
		}else{
			//send Text
			
			if(out != null) {
				out.println(text);
				outputDevice.append("You: "+text+"\n");
			}
			
		}
	
	
	}
	
	
	

	@Override
	public void keyReleased(KeyEvent arg0) {
		keyTyped = false;
	}

	@Override
	public void keyTyped(KeyEvent arg0) {
				
		
	}
	
}
```

ServerMain.java

```
package com.net.server;

import java.io.IOException;

import com.net.gui.GUI;

public class ServerMain {

	
	public static void main(String[] args) {
		GUI gui = new GUI("Server "+GUI.VERSION);
		ServerFunc function = null;
		
		function = new ServerFunc(gui.getTextArea(), gui.getCommandLine());

		//gui.getCommandLine().addKeyListener(function);
		
		try {
			function.start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

}
```

ClientFunc.java

```
package com.net.client;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ClientFunc implements KeyListener{
	
	boolean connected = false;
	boolean keyTyped = false;

	String text;
	
	Socket serverCon;
	
	DataInputStream in;
	PrintStream out;
	
	JTextArea outputDevice;
	JTextField inputDevice;
	
	public static int PORTNUMBER = 3333;
	
	public ClientFunc(JTextArea o, JTextField i){
		
		outputDevice = o;
		inputDevice = i;
		
		i.addKeyListener(this);
		
	}

	public void start() throws IOException{
		outputDevice.append("Type \"/connect IPADRESS\" to connect\n");
		
		
	}
	
	public void connect(InetAddress i){
		try {
			
			serverCon = new Socket(i, PORTNUMBER);
			in = new DataInputStream(serverCon.getInputStream());
			out = new PrintStream(serverCon.getOutputStream());
			
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		outputDevice.append("> Connected to Server\n");
		outputDevice.append("> Say Hello!\n");
		connected = true;
	}
	
	@SuppressWarnings({ "deprecation", "deprecation" })
	public void listenForInput(){

		while(true){
			

			try {
				String text = in.readLine();
				outputDevice.append("Partner: "+text+"\n");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
	@Override
	public void keyPressed(KeyEvent arg0) {
		
		if (!keyTyped) {
			keyTyped = true;
			switch (arg0.getKeyCode()) {
			case KeyEvent.VK_ENTER:
				handleInput(inputDevice.getText());
				inputDevice.setText("");

				break;
			}
		}
			
	}

	private void handleInput(String text) {
		
		if(text.startsWith("/")){
			//handle Commands
			if(text.equals("/help")){
				outputDevice.append("No Help available\n");
			}
			if(text.startsWith("/connect")){
				String[] arr = text.split(" ");
				
				//TODO Regex for IP adresses
				try {
					InetAddress i = InetAddress.getByName(arr[1]);
					connect(i);

				} catch (UnknownHostException e) {
					outputDevice.append("Illegal IP\n");
				}
				//listenForInput();
			}
			
			
		}else{
			//send Text
			
			if(out != null)
				
					out.println(text);
					outputDevice.append("You: "+text+"\n");

				
		}
	
	
	}
	
	
	

	@Override
	public void keyReleased(KeyEvent arg0) {
		keyTyped = false;
	}

	@Override
	public void keyTyped(KeyEvent arg0) {
				
		
	}
	
}
```

ClientMain.java

```
package com.net.client;

import java.io.IOException;

import com.net.gui.GUI;

public class ClientMain {

	
	public static void main(String[] args) {
		GUI gui = new GUI("Client "+GUI.VERSION);
		ClientFunc function = null;
		
		function = new ClientFunc(gui.getTextArea(), gui.getCommandLine());

		
		try {
			function.start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		while(!function.connected){
			
		}
		function.listenForInput();
		
	}

}
```

Wie gesagt beim debuggen läuft alles. Zuerst dacht ich es liegt am Breakpoint in der listenForInput Methode, ohne ihn gings aber auch.


----------



## Michael... (21. Aug 2012)

Also ich finde den Code etwas verwirrend, aber wenn ich's richtig überblicke hängt der Client doch in einer Endlosschleife und baut nie eine Verbindung zum Server auf:


skappler hat gesagt.:


> [JAVA=23]        while(!function.connected){                     }[/code]


Abgesehen davon, dass die Schleife grundsätzlich merkwürdig ist. Wenn überhaupt regelt man sowas über Listener/Observer o.ä.


----------



## SlaterB (21. Aug 2012)

bei mir geht alles bestens, wenn aus einer main alles gestartet auf einen Rechner, mit Threads

die readLine()-Methode in DataInputStream ist deprecated, vielleicht gibts Probleme mit Zeilenumbrüchen,
testweise solltest du erstmal einzelne Bytes mit read() lesen, und z.B. zählen, kommt überhaupt irgendwas an?
mehr Bytes bei längeren Nachrichten?

eine Schleife
> while (!function.connected)   {   }
ist wirklich ein GAU, wird millardenfach pro Sekunde wiederholt, läßt einen CPU-Kern auf dem Rechner auf 100% Belastung schmoren, bis endlich Verbindung aufgebaut,
mit einem einfachen Thread.sleep(10) darin schaffst du ummerkbare Pausen so das trotzdem in einem ganzen Menschenleben an Zeit nicht mehr so viel gearbeitet werden muss wie sonst in einer Sekunde


----------



## skappler (21. Aug 2012)

Ja ich weiß das ist sehr sehr unschön  Aber es funktioniert. Es funktioniert weil die änderung von connected durch den KeyListener ausgelöst wird. 
Ich wusste einfach nicht wie ich ihm sagen soll, auf die Änderung von connected zu warten.
Er verlässt die Schleife jedenfalls und baut auch eine Verbindung auf, sonst könnte ich ja garkeine Nachrichten schicken

EDIT:
@SlaterB
Auf die Idee mit Thread.sleep() bin ich garnicht gekommen. Schande über mich 
Die Idee mit den Bytes lesen war gut. Er empfängt etwas. Ich empfange alles in ein byte[] und gebe das dann aus. Wie erwartet standen die ASCII Codes der einzelnen Buchstaben darin, sowie am schluß noch der Zeilenumbruch. Auch wird jetzt im Chatfenster was ausgegeben, nämlich die toString Variante des Arrays.
Kann ich das jetzt Irgendwie wieder auf Strings umstellen? Vor allem damit die Länge beliebig sein kann. Ich könnte ja jetzt aus dem array einen String zusammenbasteln, das kommt mir aber sehr umständlich vor.


----------



## skappler (21. Aug 2012)

Ok hab das Problem gelöst. Hab einfach, wie von Eclipse vorgeschlagen, einen BufferedReader genommen. Wenn jemand trotzdem noch einen Tipp hat wie ich das Problem mit der Endlosschleife "verschönern" kann, bitte melden


----------



## SlaterB (21. Aug 2012)

oh, Posting heute morgen gar nicht gesehen,
eine Alternative wäre noch, das listenForInput() in ClientFunc, in connect() zu verschieben,

eigentlich naheliegend und symmetrisch, für den Server steht der Aufruf doch auch in ServerFunc..


----------



## skappler (21. Aug 2012)

So hatte ich das ganze vorher, aber da das ganze aus dem KeyListener raus aufgerufen wird. Geht er dann in die while schleife zum Empfangen, steckt er von der Aufrufhierarchie immernoch im Listener fest, und kann somit keine weiteren Kommandos entgegennehmen. Das Textfeld ist dann auch inaktiv, bzw sieht so aus als ob es hängt.


----------



## SlaterB (21. Aug 2012)

auch wieder richtig, so bist du dorthin gekommen,
die Wahl ist nun einen zweiten Thread zu starten, der main-Thread parallel zur GUI schon lange abgelaufen,
oder mit der Schleife main warten zu lassen, das ist schon ok so

um die Symmetrie dennoch zu wahren, könnte das Warten + der Aufruf in start() in ClientFunc, wirklich weitgehend gleich zu ServerFunc,
andererseits unschön, wenn eine start()-Methode quasi die ganze Zeit läuft 

falls in start() nur eine GUI-Ausgabe steht, rechtfertigt das auch das try/catch drumherum nicht wirklich,
freilich bleibt es ohne Funktion, solange listenForInput() selbst auch abfängt


----------



## skappler (21. Aug 2012)

SlaterB hat gesagt.:


> auch wieder richtig, so bist du dorthin gekommen,
> die Wahl ist nun einen zweiten Thread zu starten, der main-Thread parallel zur GUI schon lange abgelaufen,
> oder mit der Schleife main warten zu lassen, das ist schon ok so
> 
> ...



Ich hab noch nie wirklich mit THreads gearbeitet, deshalb hab ich das ganze ohne versucht. Mit wäre es wahrscheinlich schöner, aber ich wollte erstmal die grundlegenden Funktionen zum laufen bringen und mich dann um Threads kümmern, auch um evtl. mehrere Clients zu akzeptieren. (Ich bräuchte dann ja weiterhin nur einen ServerSocket, aber mehrere normale Sockets in der Server Klasse, seh ich das richtig?)

Der Code wie ich ihn hier gepostet habe ist noch sehr unschön. Jetzt wo alles läuft will ich das noch verbessern und optimieren. Das try/catch für die start() Methode ist nur ein überbleibsel, als ich noch das connect darin aufgerufen habe, wird also auch noch entfernt bzw. angepasst.

Vielen Dank für eure/deine Hilfe!


----------



## jnetdev (22. Aug 2012)

grundsätzlich gilt : blocking I/O-Ops in seperate Threads !

ansonsten freezed die GUI oder der rest der logik ...


----------



## skappler (22. Aug 2012)

jnetdev hat gesagt.:


> grundsätzlich gilt : blocking I/O-Ops in seperate Threads !
> 
> ansonsten freezed die GUI oder der rest der logik ...



Ich hab halt noch nie wirklich ein Programm mit GUI geschrieben. In Zukunft weiß ichs =)


----------

