Java Client-Server blockiert

Schattenfänger

Lt. Junior Grade
Registriert
Nov. 2010
Beiträge
273
Hallo,
Ich muss hier was fertig machen aber leider hat sich ein Fehler eingschlichen, denn ich nach ner halben Stunde gefunden habe aber nach knapp 2h noch immer keine Ahnung habe wie umgehen.
Dabei ist es heute früh schon gelaufen, nur mit ein paar Einschränkungen.

Also
Ich starte Server; Starte Client, dieser startet den Socket sowie die GUI. Bei der GUI kommt ne Anmeldung, sobald ich die Daten eingegeben habe, wird auf den nächsten send() Tick gewartet und ein Request zum Server gesendet, ob diese korrekt waren.

Das Problem ist, das zwei Teile von meinem Code blockieren und ich nicht sehe weshalb.

Code:
public void run() {

		try {
			while (true) {
			System.out.println("test");
				this.send();
				this.read();
			}
		} catch (IOException ex) {
			Logger.getLogger(MainClient.class.getName()).log(Level.SEVERE,
					null, ex);
			System.out.println("Unknown Error.");
		}
	}

public void read() throws IOException {
		String responseLine;
		System.out.println("lksdfklasdfjköl");
		//responseLine = is.readLine();
		
		
		responseLine=reader.readLine();
		
		
		if (responseLine != null) {
			System.out.println("found");
		    System.out.println(responseLine);
		}
		try {
			System.out.println("1");
			rcvMsg = (DataPacket) objStr.readObject();
			
		} catch (ClassNotFoundException ex) {
			Logger.getLogger(ClientThread.class.getName()).log(Level.SEVERE,
					null, ex);
			System.out.println("Class not found");
		}
		System.out.println("2");
		if (rcvMsg != null) {
		
			switch (rcvMsg.type) {
			// case "quit": this.close(); break;

			case loginResponse:
				this.loginAck(); // acknowledge after login
				break;
			// Client wants to know something
			case getData:
				break;
			// Client sets new Data
			case setData:
				break;
			}
		}

	}

private void initialize() {
		try {

			clientSocket = new Socket(host, portNumber);
			inputLine = new BufferedReader(new InputStreamReader(System.in));
			
			os = new PrintStream(clientSocket.getOutputStream());
			is = new DataInputStream(clientSocket.getInputStream());
			objStr = new ObjectInputStream(is);
			objOut = clientSocket.getOutputStream();
			oos = new ObjectOutputStream(objOut);

			
			reader=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

So, hier blockiert read()
lksdfklasdfjköl, wird noch ausgegeben, aber danach geschieht nichts mehr. Das einzige was ich machen kann ist, von Zeile 22 bis 37 auskommentieren und dann läuft es.
Wenn ich responseLine auskommentiere komme ich bis "1".

Daher vermute ich, dass reader.readline als auch objStr.readObject() das Programm blockieren, ich weiß aber nicht was ich dagegen machen kann.
Jemand eine Idee?

Edit:
Ok, ich fange respnseLine jetzt mit
Code:
		while(reader.ready()){
			responseLine=reader.readLine();
		}
ab, und es scheint bisher zu funktionieren.
 
Zuletzt bearbeitet:
Ich kenn mich jetzt mit java uns Sockets nicht so aus, aber ich vermute, dass der Server mit einem Aufruf von read() eben auf Dateneingang wartet (und dann weiter wartet, bis alle Daten gelesen wurden). Abhilfe: threads nutzen oder einfach mal in die Dokumentation schauen. Darum scheint auch deiner reader.ready() zu funktionieren, da dann wohl erst angefangen wird zu lesen, wenn Daten da sind,

Schau einfach in die Dokumentation. Dort steht meistens wie man so etwas handhaben muss.
 
Bei solch einem Problem solltest du tatsächlich threads verwenden.

Warum?
readObject ist eine sehr komplexe Funktion. Non-blocking IO setzt voraus, dass du den Zustand des Programmteils, welcher für das Einlesen verantwortlich ist, abspeichern kannst. Dies kann man in manchen Fällen mithilfe einer State-Machine lösen, readObject ist dafür aber zu komplex (ein Thread IST eine State-Machine).

Falls du tatsächlich unbedingt Non-Blocking IO haben willst, solltest du das Netzwerkprotokoll soweit vereinfachen, dass du z.B. weißt, wie viel Byte du lesen musst, um ein Datenblock/Objekt zu lesen.
Das richtige Interface dafür sind dann SocketChannels, bei denen du configureBlocking(false); aufrufst.

Deine jetzige Lösung funktioniert, weil dein Datenpaket zur Zeit! als ein Paket übertragen wird und du daher, falls dein Socket lesen kann (ready()==true), dann auch readObject nicht blockiert. Sollte jemand aber mal dir ein großes Objekt vor die Nase setzten/mittendrin crashen/mittendrin mal nichts senden (z.B. während des Debuggings), dann blockiert deine Lösung trotzdem.
 
In wie fern soll ich hier einen Thread verwenden? Eine eigene Klasse für die eine Zeile? :O Hab nämlich schon knapp 4 Threads gleichzeitig gestartet.
Soll man dann sowohl für read als auch für write einen eigenen Thread anlegen?

Jedenfalls habe ich es jetzt mit:
if(objStr.available()) abgefragt und funktioniert anscheinend.
Wobei... Kann es da sein, dass praktisch Pakete übersprungen werden?

Das einzig positive ist, dass ich durch den Fehler auch nen Fehler im Server gefunden habe.

Dafür hat sich aber ein neuer Fehler aufgetan :(
Welcher zudem nur hin und wieder entsteht und schwer nach zu vollziehen ist.

Ich schicke jetzt eben mein Loginpacket, der Server erhält es. Und bekomme bei responseLine "gotLogin" zurück. Zudem wird sogleich ein Packet mit dem Type loginAck gesendet. Aber dieses wird nicht erkannt.

Und hin und wieder erhalte ich ein Streamcorruptedexception: code 57; So wie ich es verstanden habe, geht es darum, dass ich eine Klasse Serialisiere, die nicht seialisierbar ist. Obwohl die Klasse nur aus Strings besteht.

Edit:
Falls du tatsächlich unbedingt Non-Blocking IO haben willst, solltest du das Netzwerkprotokoll soweit .....
In wie fern ist es sinnvoll blocking IO zu verwenden?

Jetzt bin ich draufgekommen, dass wenn ich auf meinem Server eben if(objStr.available()!=0) verwende und von meinem Client das Login Packet schicke nie in die Bedingung reinkomme....
 
Zuletzt bearbeitet:
Du hast das Problem, dass readObject mglw. sehr lang dauert. Das ist im UI-Thread blöd (unter Android z.B. bekommst du da "Die Anwendung reagiert nicht").

available() hat das Problem, das da ganz viele Implementationen dir einfach Müll erzählen.
Für ready() und available() bedenke immer, dass du mglw. nur 1 Byte schreiben/lesen kannst, aber viel mehr schreiben/lesen willst. Das bedeutet, dass du nicht vorhersagen kannst, ob du wirklich nicht blockieren wirst.

Zur Zeit verwendest du Blocking-IO, da dein Code wartet, wenn du was lesen willst, aber nichts da ist.

StreamCorruptedException kann mehrere Gründe haben, bspw. kann es sein, dass du den binären Stream aus Versehen rekordierst (normalerweise nimmt man nicht DataInputStream.readLine(), da deprecated).

EDIT:
Noch was gefunden, es ist nicht clever, zwei Streams auf das gleiche Object zu erzeugen, weil wenn da einer von beiden Streams puffert, haste Datensalat.
 
Das man im UI Thread nicht unbedingt so eine Verbindung aufbaut ist mir bewusst :)
Ich lasse ja auch von nem Controller die UI erstellen und dann die Communication Klasse, die da oben, welche ein eigener Thread ist. Nur bringt mir das halt nix wenn dann schon wieder eine einzelne Zeile blockiert, aber Daten zum Senden da sind.

available() hat das Problem, das da ganz viele Implementationen dir einfach Müll erzählen.
Für ready() und available() bedenke immer, dass du mglw. nur 1 Byte schreiben/lesen kannst, aber viel mehr schreiben/lesen willst. Das bedeutet, dass du nicht vorhersagen kannst, ob du wirklich nicht blockieren wirst.

In wie fern Müll erzählen? ^^
Gut, aber wenn ich mit available überprüfe, und wennn was da ist, ist es doch egal wie viel oder? Schließlich gehe ich dann in readObject rein und sollte so lange lesen, bis das Object fertig ist. Wie viel ich lese mache ich doch nicht davon abhängig was available sagt.

Zur Zeit verwendest du Blocking-IO, da dein Code wartet, wenn du was lesen willst, aber nichts da ist.
Jup, und genau das will ich umgehen.

StreamCorruptedException kann..........
Tritt das bei mir auf? Und DIS.readLine verwende ich ja nicht mehr sondern BufferedReader.

So, wie ich das sehe, mache ich das doch eh nicht?

Ach, der Server schaut so aus:
Server gestartet; wartet auf CLients; wenn Client da wird ein Thread erzeugt; Und dieser Thread schaut dann so aus, wie die Klasse da oben.


EDIT:
So, mal ganz kurz, wie würdet ihr das machen?

Jetzt habe ich es mal so getestet:

Code:
public void read() throws IOException {
		String responseLine = null;
		System.out.println("asdfasdfsddf");

	/*	while (reader.ready()) {
			System.out.println("lkasdf");
			responseLine = reader.readLine();
		}*/
		/*if (responseLine != null) {
			System.out.println("Response from Server: " + responseLine);
		}*/
		try {
			
			  if (objStr.available() != 0) { System.out.println("loooo");
			 rcvMsg = (DataPacket) objStr.readObject(); }
			 
			/*rcvMsg = null;
			if (input.gotData) {
				System.out.println("got Dataaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
				rcvMsg = input.rcvMsg;
				input.gotData = false;
			}*/

		} catch (Exception ex) {
			Logger.getLogger(ClientThread.class.getName()).log(Level.SEVERE,
					null, ex);
		}

private void initialize() {
		try {
			clientSocket = new Socket(host, portNumber);
			objOut = clientSocket.getOutputStream();
			InputStream stream = clientSocket.getInputStream();
			os = new PrintStream(objOut);
			 is = new DataInputStream(stream);
			 objStr = new ObjectInputStream(is);

			oos = new ObjectOutputStream(objOut);
			//reader = new BufferedReader(new InputStreamReader(stream));

		//	input = new CommunicationReader(stream);
			//input.start();

Ich starte eben und erhalte zehntausenmal asdfasdf, sobald ich aber das Login Packet schicke erhält der Serverr dieses, aber wenn der mir jetzt ein Packet zurückschickt bekomm ich einen StreamCorruptetException bei objStr.available().

Zudem habe ich jetzt nachgelesen und available sat mir wohl nur wie viele Byte ohne blocking gelesen werden können und nicht wie viele hier sind. Also wohl nicht das was ich benötige.
Aber trotzdem seh ich nicht wo die Exception zusammenkommt.

Nun habe ich mal die read() Methode in einen eigenen Thread ausgelagert:
Client
Code:
public void read() throws IOException {
		String responseLine = null;
		System.out.println("asdfasdfsddf");

	/*	while (reader.ready()) {
			System.out.println("lkasdf");
			responseLine = reader.readLine();
		}*/
		/*if (responseLine != null) {
			System.out.println("Response from Server: " + responseLine);
		}*/
		try {
			
			//  if (objStr.available() != 0) { System.out.println("loooo");
			// rcvMsg = (DataPacket) objStr.readObject(); }
			 
			rcvMsg = null;
			if (input.gotData) {
				System.out.println("got Dataaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
				rcvMsg = input.rcvMsg;
				input.gotData = false;
			}

		} catch (Exception ex) {
			Logger.getLogger(ClientThread.class.getName()).log(Level.SEVERE,
					null, ex);
		}
		if (rcvMsg != null) {
			System.out.println("bin drin");
			switch (rcvMsg.type) {


	private void initialize() {
		try {
			clientSocket = new Socket(host, portNumber);
			objOut = clientSocket.getOutputStream();
			InputStream stream = clientSocket.getInputStream();
			os = new PrintStream(objOut);
			 is = new DataInputStream(stream);
			// objStr = new ObjectInputStream(is);

			oos = new ObjectOutputStream(objOut);
			//reader = new BufferedReader(new InputStreamReader(stream));

			input = new CommunicationReader(stream);
			input.start();

Thread:
Code:
public CommunicationReader(InputStream ois) {

		try {
			this.ois = new ObjectInputStream(new DataInputStream(ois));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("IOException");
			e.printStackTrace();
		}

	}

	public void run() {
		while (true) {
			try {
				this.rcvMsg = (DataPacket) ois.readObject();
			} catch (ClassNotFoundException | IOException e) {
				// TODO Auto-generated catch block
				System.out.println("IOException");
				e.printStackTrace();
			}

			if (rcvMsg != null) {
				gotData = true;
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
Ich hab auch bereits rumgespielt welchen Stream ich der Klasse übergeben soll aber das macht anscheinend keinen unterschied.

Wenn ichs so verwende dann kommt es zu zwei Sachen.
Entweder ich bekomme einfach gar keine Ausgabe und die Gui wird auch nicht gestartet, obwohl sie von nem separatem Thread gestartet wird.

Oder asdfasdf wird mir brav ausgegeben, bis ich einen login abschicke. Und dann bekomm ich wieder einen Streamcorrupted bei readObject. asdfasd wird dann natürlich weiterhin ausgegeben.
Dabei habe ich dann auch noch ein Tutorial gefunden bei dem die read Methode auch so ausgelegt ist.


Kann es ein Problem sein, dass der Server und Client am gleichen PC ist? Wobei das seltsam wäre, da das Programm ja praktisch schon lief, ohne diesen Fehler.
 
Zuletzt bearbeitet:
Nun ja, die meisten Implementationen liefern entweder 0 oder 1 bei available zurück. Das ist für deine Anwendung aber zu mager.

Das Problem ist, dass wenn du denkst, dass was da ist (available() >0), dann kannst du zwar in readObject reingehen, niemand garantiert dir aber, dass das in einem Rutsch durchgeht. Diese Problem kannst du nur lösen, indem du sicherstellst, dass tatsächlich alle Daten schon übertragen wurden (was bei read/writeObject nicht geht).

Die StreamCorruptedException hast du erwähnt. Das könnte einer der Gründe sein.

Bei deinem Client sollte es überhaupt kein Problem sein, einen Thread zu erzeugen. Der Overhead dadurch wird durch die Einfachheit der Lösung mehr als wettgemacht. Bei deinem Server ist das was anderes, solltest du mal sehr viele Clients haben, wird die Anzahl der Threads deinen Server in die Knie zwingen.

Mein Vorschlag: Wenn du readObject verwenden willst, dann mach es mit Threads, falls du definitiv non-blocking-IO machen willst, solltest du deine Datenpakete irgendwie kapseln und dann in einen Puffer lesen, aus dem du die dann deserialisieren kannst.


Non-blocking-IO muss so sein, dass es nicht blockieren darf, falls du jeweils nur ein einziges Byte lesen bzw. schreiben kannst.

Wenn du was schreiben kannst und zwei Bytes schreibst, wird es irgendwann mal den Fall geben, dass genau diese Funktion blockiert.

Wenn du einfach wartest, bis du genügend Bytes schreiben kannst, kommt irgendwann jemand und will nicht nur 100 Byte sondern 100 MB damit versenden und du hast nie genügend Platz, um diese Nachricht zu versenden.
 
readObject() hab ich ja jetzt ausgelagert.
Das sollte doch so passen, was ich da oben habe oder?

Wie würde diese Variante dann am Server ausschauen? Ich habe ja einen Thread der auf neue Clients wartet und wenn ein neuer Client kommt wird ein neuer Thread erzeugt. Wenn ich dann das lesen auch noch auslagere, habe ich dafür auch noch nen Thread. Das einzige was ich machen könnte ist den Lese Thread nur einmal zu erstellen und dann noch beim Packet zuzuordnen zu welchem Client es gehört.
Aber im Moment ist mir das eigentlich auch egal. - Ich will nur das es läuft, ich muss ja noch das ganze Protokoll implementieren.

Was genau wäre da deine alternative mit Verkapseln?

Und auf Deutsch. Du hast auch keinen Plan was die Exception wirft?
 
Exception ohne Call-Stack: Nein, hab ich nicht :D.

Wenn du readObejct auslagerst, kannst du auf den available-Kram verzichten. Aber sonst ist das dann eine mögliche Lösung.


Auf dem Server ist das gleiche Problem auch da, aber wenn's keine Geschwindigkeitsprobleme gibt (http://stackoverflow.com/questions/177122/how-can-i-speed-up-my-perl-program), mach es genau so wie bei deinem Client.
 
Die Lösung mit "normalen" Sockets und Threads ist eigentlich nicht mehr zeitgemäß... Klar, sie ist zweckmäßig, aber ich würde davon absehen und lieber einen Blick ins NIO1/2 Package werfen. Alleine für die Skallierbarkeit.

Auch empfehlenswert ist die Netty library. Sie ist weit verbreitet und ausgereift.
 
Ja, ich wollte eh fragen ob ihr ne gute Networking Lib empfehlen könnt, wenn ich mein Problem beseitigt habe. Ich will bzw muss das jetzt mal so fertig machen.

Ich habe jetzt das hier gefunden, aber es hat den anschein, dass ich diese Fehler nicht mache.
Zudem habe ich auf dem Server bei der write den ObjOutputStream geclosed nachdem ich ein Packet gesendet habe, aber das schein auch nix zu bringen.
Soll ich vielleicht am Client bei jedem Druchlauf den Stream öffnen und dann wieder schließen?

@Hancock
Gibts ne schöne Möglichkeit den Call Stack auszugeben, wenn ich einfach getAllStackTraces verwende erhalte ich dutzende von Ausgaben.
 
Okay, interessant.

Ich habe jetzt mal den Reader Thread auf dem Server deaktiviert, aber die Streams geöffnet.

Zudem knapp 15 TestCases geschrieben, wo ich Packets vom Server abschicke.
Jedes Packet kommt korrekt an und es wird kein Fehler gemeldet.
Das einzige was ich geändert hab ist, dass ObjectInputStream.readObject() nicht mehr aufgerufen wird .....
Das selbe passiert wenn ich den InputStream am Client deaktiviere und dem Server was schicke.
 
Zurück
Oben