# client server ObjectOutputStream



## DeathSoul (4. Sep 2009)

Hi ho

So nach sehr langem suchen versuche ich es nun auf diesem weg.

ich schicke per client server verbindung ein Object namen tank an server.
Danach versuche ich zu erfragen was der Server bekommen hat.
Dies klappt, aber wenn sich variablen dieses Objects ändern und ich wieder dies an den
Server schicke , bekommt der Server wieder das gleiche Object mit den selben Variablen.

bzw: ich empfange nur einmal das Object, wenn ich nochmal was schicke kommt das selbe an , ich bin ratlos.

hier mein server
JAVA

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

public class Server {
	ServerSocket serverSocket;
	Socket socket;
	LinkedList<Socket> sockets;
	int cnt = 0;

	LinkedList<String> namen = new LinkedList<String>();

	public Server()

	{

		sockets = new LinkedList<Socket>();

		try {

			serverSocket = new ServerSocket(1234);

			while (true) {
				socket = serverSocket.accept();
				System.out.println("verbunden");
				++cnt;
				sockets.add(socket);

				new EchoClientThread("ClientThread:" + cnt, socket);
			}

		} catch (IOException e) {

		}

	}

	class EchoClientThread extends Thread {

		private String name;
		private Socket socket;

		ObjectOutputStream os;
		ObjectInputStream is;

		public EchoClientThread(String name, Socket socket) {

			System.out.println("echo client erstellt");

			this.name = name;
			this.socket = socket;

			try {

				os = new ObjectOutputStream(socket.getOutputStream());

				is = new ObjectInputStream(socket.getInputStream());

				this.start();

			} catch (Exception e) {
				System.out.println("fehler0" + e.getLocalizedMessage());
			}

		}

		public void run() {

			try {

				while (true) {

					Tank tank = new Tank(2, 2, 2, 2, 2);

					tank = (Tank) is.readObject();

					System.out.println("echothread liest" + tank.x);

				}

			} catch (Exception e) {
				System.out.println("fehler server ende "
						+ e.getLocalizedMessage());
			}

		}
	}

	public static void main(String args[]) {
		try {
			Server server = new Server();

		} catch (Exception e) {
		}

	}

}
```
/JAVA


meine Client methode

```
public void senden(Tank t) {

		try {
			os.writeObject(t);
			
		} catch (Exception e) {
			System.out.println("fehler client   " + e.getLocalizedMessage());
		}

	}
```

im Client passt auch noch alles 

wäre echt super , wenn mir jemand helfen kann


----------



## SlaterB (4. Sep 2009)

```
/**
     * Reset will disregard the state of any objects already written to the
     * stream.  The state is reset to be the same as a new ObjectOutputStream.
     * The current point in the stream is marked as reset so the corresponding
     * ObjectInputStream will be reset at the same point.  Objects previously
     * written to the stream will not be refered to as already being in the
     * stream.  They will be written to the stream again.
     * 
     * @throws	IOException if reset() is invoked while serializing an object.
     */
    public void reset()
```


----------



## DeathSoul (5. Sep 2009)

hi ho
hab das reset(); mal am ende eingefügt , nach dem System.out.println(tank.x)
kommt aber folgende Fehler meldung: mark/reset not supported


----------



## SlaterB (5. Sep 2009)

dann kommt von mir der Vorschlag: alles auf die harte Tour,
über die offizielle Verbindung nur echte Bytes + höchstens Strings als Info schicken,

für jedes Objekt z.B. ein neuer ObjectOutputStream/ ByteArrayOutputStream, der ein byte[] bereitstellt,
das verschicken auf am Server ähnlich zurück umwandeln,
zwischen den Byte-Arrays evtl eine Info über die Anzanh der Bytes versenden


----------



## DeathSoul (6. Sep 2009)

hi 

Danke für die Antworten, jedoch versteh ich nicht ganz weshalb ich bytes nehmen soll . Könntest du bitte mal ein Beispiel posten.


----------



## SlaterB (6. Sep 2009)

normalerweise nimmt man einen höheren Stream und schickt über den seine Objekte,
statt reset() ginge vielleicht auch noch, ein neues Objekt mit den geänderten Daten zu versenden,

wenn beides nicht in Frage kommt, musst du die Daten anderweitig versenden und da hatte ich bytes vorgeschlagen, denn bytes kann man gewiss versenden,

ein Beispiel für die Streams ohne die Übertragung ist nohc
Deep-Copy von Java-Objekten  Der Informatik Student


----------



## DeathSoul (6. Sep 2009)

hm versteh ich net , also so gehts nicht :


```
ByteArrayOutputStream baos = new ByteArrayOutputStream(767);

				
				os = new ObjectOutputStream(socket.getOutputStream());

				os=new ObjectOutputStream(baos);
```

Also versenden kann ich es ja bloss kommt es beim ersten mal an und ich kann es beim 2 mal irgendwie nicht überschreiben.



Also eigentlich funzt es ja schon fast , bloss wen ich mein Object beim 2 mal schicke bleibt es gleich ! beim lesen gleich obwohl ein anderes geschickt wurde


Wie mkeinst du das mit höheren stream ?



Die Klasse die ich verschicke :

```
public class Tank implements Serializable {

	// private static final long serialVersionUID = 1L;

	int x;
	int y;

	int mx;
	int my;

	float rotation = 0;
	int width;
	int height;

	float speed = 0;
	int maxSpeed = 10;

	boolean vTaste = false;
	boolean zTaste = false;
	boolean lTaste = false;
	boolean rTaste = false;

	double nWinkel = 0;
	double tankWinkel;
	double geschützWinkel;

	double posX;
	double posY;

	transient Graphics2D tank;
	transient Graphics2D geschütz;

	public Tank(int x, int y, float rotation, int width, int height) {
		// this.setSize(500, 555);
		this.x = x;
		this.y = y;

		this.width = width;
		this.height = height;

		// this.setBackground(Color.YELLOW);

		// this.addMouseListener(new MausTasten());
		// this.addMouseMotionListener(new MausBewegungen());

	}

	public void paintComponent(Graphics g) {
		// super.paintComponent(g);

		g.drawString("x:" + x, 10, 10);
		g.drawString("y:" + y % 6.29, 10, 25);

		g.drawString("speed:" + speed, 10, 40);

		final Graphics2D ga = (Graphics2D) g;
		final AffineTransform reset = ga.getTransform();

		final AffineTransform af = new AffineTransform();

		tank = (Graphics2D) g;

		g.setColor(Color.RED);

		tank.rotate(tankWinkel * Math.PI / 180, x + 50, y + 50);
		tank.fillRect(x, y, width, height);

		ga.setTransform(af);

		geschütz = (Graphics2D) g;

		g.setColor(Color.BLUE);

		geschütz.rotate(geschützWinkel, 60 + x, y + 50);
		geschütz.fillRect(x + 50, y + 40, 120, 20);

		geschütz.setTransform(reset);

	}

	public boolean drehen(boolean links) {

		if (links == true) {
			if (speed >= 0) {
				{
					tankWinkel -= 5;
				}
			} else {
				tankWinkel += 5;
			}
		}

		if (links == false) {
			if (speed >= 0) {
				{
					tankWinkel += 5;
				}
			} else {
				tankWinkel -= 5;
			}
		}

		return false;
	}

	public boolean beschleunigen() {
		speed++;

		return true;
	}

	public boolean bremsen() {
		speed--;

		return true;
	}

	public int getX() {
		return x;
	}

}
```


----------



## SlaterB (6. Sep 2009)

> Also versenden kann ich es ja bloss kommt es beim ersten mal an und ich kann es beim 2 mal irgendwie nicht überschreiben.
+
> Also eigentlich funzt es ja schon fast , bloss wen ich mein Object beim 2 mal schicke bleibt es gleich ! beim lesen gleich obwohl ein anderes geschickt wurde

bekannte Informationen nochmal wiederholen, gleich 2x?

sorry, wenn meine Postings überhaupt nicht verstanden werden bin ich genervt,

----

> Wie mkeinst du das mit höheren stream ?

wenn man einen ObjectOutputStream über den normalen socket.getOutputStream() verwendet,
dann ist das ein höherer Stream, der mehr kann, nämlich komplette Objekte versenden

----

zum dritten Mal: ich rede davon, ein einfaches byte[] über den Socket zu schicken,
was daran unverständlich sein könnte ist mir unverständlich,
es ist nicht schön und schwierig, wenn es dir nicht gefällt dann ich kann das verstehen,
aber bitte nicht die einfachsten Dinge nachfragen

byte[] x = irgendwo separate Daten zusammenstellen, völlig unabhängig von Netzwerk oder Mondphase,
und zwar das byte[] z.B. durch einen ObjectOutputStream erstellen lassen (der aber nicht mit dem Socket verknüpft ist!), siehe Link,
baos.toByteArray() liefert dort das byte[]


über den Socket-Stream nur byte[] + vielleicht noch einfache Strings senden


----------



## DeathSoul (6. Sep 2009)

hi ho 

```
public void senden(Tank t) {

		try {
			oos = new ObjectOutputStream(baos);
			oos.writeObject(t);

			bais = new ByteArrayInputStream(baos.toByteArray());
			ois = new ObjectInputStream(bais);
			Tank deepCopy = (Tank) ois.readObject();

			System.out.println(deepCopy.x);

		} catch (Exception e) {
			System.out.println("fehler client   " + e.getLocalizedMessage());
		}

	}
```

Auch so bleibt das Object gleich


----------



## SlaterB (6. Sep 2009)

na das glaub ich nun nicht, ein vollständiges Testprogramm mit

Tank t = ..
senden()
t.aenderung()
senden()

und Ausgabe der Ergebnisse wäre hilfreich

oder es liegt daran, dass baos nicht neu initialisiert wird, jeder neue Tank wird dort angefügt, 
am Ende sind 2, 3, 4 und noch mehr drin, ausgelesen wird immer der erste


----------



## DeathSoul (6. Sep 2009)

vielen dank für die hilfe

client:

```
public synchronized void run() {

		try {

			while (true) {

				tank = (Tank) is.readObject();
				gamePanel.spieler2 = tank;

				System.out.println("client run x: " + tank.x);

			}

		} catch (Exception e) {
		}

	}

	public void senden(Tank t) {
		try {

			baos = new ByteArrayOutputStream(4096);

			oos = new ObjectOutputStream(baos);
			oos.writeObject(t);

			bais = new ByteArrayInputStream(baos.toByteArray());
			ois = new ObjectInputStream(bais);

			os.writeObject(ois.readObject());

		} catch (Exception e) {
			System.out.println("fehler client   " + e.getLocalizedMessage());
		}

	}
```

server:

```
public void run() {

			try {

				while (true) {

					tank = (Tank) is.readObject();

					os.writeObject(tank);

				}

			} catch (Exception e) {
				System.out.println("fehler server ende run schleife "
						+ e.toString());
			}

		}
	}
```

Nur sehr komisch das ich das beim client senden machen muss und beim server nicht


----------



## DeathSoul (7. Sep 2009)

so , fahren jetzt 2 panzer , sehr schön , vielen dank für die hilfe.
Eine Frage noch : Da ich nicht die ganze zeit mein pc an lassen möchte als server. gibt es da irgendwie kostenlose server oder so  ? kenn mich da überhaupt nicht aus


----------



## SlaterB (7. Sep 2009)

so hatte ich es nicht gerade gedacht, jetzt nutzt du den ByteStream nur, um das Objekt zu kopieren,
durchaus denkbar, aber ein normaler Java-Aufruf
new Tank(oldTank) mit entsprechenden Konstruktor scheint mir einfacher


----------



## DeathSoul (7. Sep 2009)

hm leider versteh ich nicht wie du das meinst.
Ausserdem hab ich nun das Problem das die client server verbindung nach ein paar sekunden unterbrochen wird.
Folgende exception tritt im client run auf : java.net.SocketException: Connection reset.


----------



## SlaterB (7. Sep 2009)

die bytes versenden um auf der anderen Seite der Connection mit  ByteArrayInputStream weitermachen,

zu deinem Fehler kann ich nix sagen, lauffähige komplette Codes könnten evtl. helfen


----------



## DeathSoul (7. Sep 2009)

hm :-( , ich krieg das anders irgendwie nicht hin.

kannst du vielleicht mir die senden methode schreiben + das empfangen im server ?

das wär echt super.


----------



## tuxedo (7. Sep 2009)

DeathSoul hat gesagt.:


> Hi ho
> bzw: ich empfange nur einmal das Object, wenn ich nochmal was schicke kommt das selbe an , ich bin ratlos.
> 
> wäre echt super , wenn mir jemand helfen kann




Object*Stream cached die Daten. weitere Infos hier:

[JavaSpecialists 088] - Resetting ObjectOutputStream

Was den Workaround mit den bytes etc. angeht:

Naja, wäre es nicht besser heraus zu finden warum reset() hier nicht geht (wobei es doch gehen sollte), statt gleich einen anderen Weg zu gehen? 

- Alex


----------



## SlaterB (7. Sep 2009)

@DeathSoul
das von Gestern, 23:12, funktioniert doch nun? wieso unnötig noch mehr Aufwand, 
glaube nicht dass das mit der SocketException zu tun hat, oder gabs das bisher nicht?
teste wie gesagt den alten Code recht einfach mit mehreren verschiedenen Tanks, die können ja unmöglich gecacht werden,

selber möchte ich dabei nicht mitprogrammieren, wenn man die bytes direkt sendet, dann muss der Server auch wissen, wann das fertig ist, wann die nächste Nachricht kommt usw.,
ziemliche Protokoll-Arbeit, von mir nur die Idee


----------



## DeathSoul (7. Sep 2009)

hm nagut, dass die verbingung nach ein paar sekunden abgebrochen ist liegt wohl an der Windows Firewall.
So wo kann man seinen Server laufen lassen ? ausser an seinem eigenem pc


----------



## tuxedo (7. Sep 2009)

> So wo kann man seinen Server laufen lassen ? ausser an seinem eigenem pc



Na auf jedem anderen PC auf dem man eine JVM laufen lassen kann.

- Alex


----------



## DeathSoul (7. Sep 2009)

hm gibt es da was im Internet ?

Wenn ich es im mit jemanden übers Internet probiere ruckelt es stark, was könnte dafür verantwortlich sein  ?


----------



## tuxedo (7. Sep 2009)

Serialisierung ist nicht optimal für "echtzeit" Transfer. 

Da steckt zu viel reflection drin um die gesamte Objektstruktur zu analysieren, zulegen und in bytes zu stecken (und umgekehrt).

Du kannst allerdings optimieren indem du zusiehst, dass die zu übertragenden Objekte möglichst keine weiteren Abhängigkeiten haben. Ein JPanel z.B. hat sehr viele abhängigkeiten. Ein einfaches Objekt das kein "extends" Benutzt und nur primitive Datentypen, Strings und Arrays aus diesen enthält hat (fast) keine Abhängigkeiten.

Dann kommts natürlich noch auf die Bandbreite und die Frequenz/Häufigkeit mit der die Objekte übertragen werden drauf an... Je mehr Bandbreite und je weniger Objekte, desto besser.

Was auch hilft: Den Nagle-Algo auf dem Socket abschalten. Damit hast du rund 100ms weniger Latenz (mein Erfahrungswert). Hilfe dazu bietet die SuFu im Forum, oder auch google. 

Alternativ: RMI oder SIMON benutzen 
Oder ganz einfach nix serialisieren 

- Alex


----------



## DeathSoul (7. Sep 2009)

Naja ohne Serialisierung ist es eine ganz schöne schreib arbeit.
Aufjeden Fall hat dein Tipp sehr geholfen, sehe nun kein geruckel mehr.
Aber Leider kommen da noch mindestens 10 Objekte dazu ^^.
Mal schauen wie es dann aussieht.
Nochmal die Frage zu dem Server , gibts es keinen anbieter der meinen server laufen lässt  ?


----------



## tuxedo (7. Sep 2009)

Es gibt keine Hoster die deine Java-Anwendung laufen lassen. Da musst du schon nen richtigen Server mieten:

Hetzner.de
server4you.de
...

- Alex


----------



## DeathSoul (8. Sep 2009)

aha k.

noch was , jetzt möchte ich noch andere Objecte verschicken. Wie und was soll ich am besten schicken um klar zustellen was ich gleich schicke ?


----------



## SlaterB (8. Sep 2009)

im einfachsten Falle Objekt an Objekt und dann
if (o instanceof X) {
..
} else if..


im schöneren Falle: Protokoll, Protokoll, Protokoll,
als aller erstes kann man immer einen String mit Informationen oder noch besser ein höheres Objekt lesen,
welches die aktuelle Nachricht und direkt folgenden Objekte beschreibt,
z.B. könnte man nach einem Enum-Wert viel schöner aufteilen als mit instanceof

switch(nachrichtentyp) {
case News:
Wetterlage x = read...;

case Tankbewegung:
Tank k = read..;
}

extrem wäre ein Beschreibung aus gewünschter Klasse + Methodenname + genau alle Parameter,
dann wäre man fast bei RMI, einem Java-Methodenaufruf, nur über die Entferngung hinweg


----------



## tuxedo (8. Sep 2009)

> als aller erstes kann man immer einen String mit Informationen oder noch besser ein höheres Objekt lesen,



Das hatte ich auch mal so gemacht...
Ich hatte ein Transportobjekt das in etwa so aussah (ganz grob und vereinfacht):


```
public class Transporter {
public byte msgType;
public Object o;
}
```

Auf der Empfängerseite konnte ich jedes gelesene Objekt erstmal in "Transporter" casten. Dann hab ich nachgeschaut welchen Wert "msgType" hatte und wusste somit, in welchen Typ ich "o" casten muss um an die wirkliche Nachricht zu kommen.

Aber alles in allem stellt sich die Frage ob es den Aufand lohnt solch ein rudimentäres Protokoll selbst zu stricken oder ob man nicht gleich zu etwas bewährtem wie RMI greift. 

- Alex


----------

