Serialisiertes Objekt über Socket nachladen

Treavor

Mitglied
Moin,

ich hoffe mal ich hab das richtige Forum getroffen.
Ich möchte einen mobilen Agenten basteln. Dafür wird zwischen zwei Rechnern eine Netzwerkverbindung aufgemacht und dann wird die .class des Agenten auf den Zielrechner übertragen. Danach wird das serialisierte Objekt des Agenten übertragen und auf dem Zielrechner wieder zusammen gebaut. (heißt einfach nur, dass das Objekt jetzt auf einem Rechner existiert auf dem vorher keine .class vorhanden war)
Das übertragen der .class funktioniert und auch das Laden dieser funktioniert. Jetzt scheitert es nur noch am Laden der serialisierten Daten - und langsam hab ich da so lange draufgestarrt, dass ich keine Ahnung mehr habe wodran es liegen könnte. Wäre super wenn jemand von euch mal kurz drüber schauen könnte.

Empfangen auf dem Zielrechner (serialisierter Kram ab Zeile 21):
Java:
ServerSocket socket = new ServerSocket(port);
Socket client = socket.accept();	
DataInputStream in = new DataInputStream(client.getInputStream());
	               
// dann kommen der Klassenname und die Größe der .class
String klassenname = in.readUTF();
int paketgroesse   = in.readInt();
	
// die Daten der .class empfangen
byte[] classfile_bytes     = new byte[paketgroesse];
in.readFully(classfile_bytes);		
              
// empfangene Klasse laden
MyClassLoader cl = new MyClassLoader(classfile_bytes); // der eigene ClassLoader tut nicht mehr als das byte[] an defineClass weiterzureichen

Class<?> cls = cl.findClass("A01_Heimkontext.Agent");
cls = cl.loadClass("A01_Heimkontext.Agent");     // kann auch wieder raus, weil ändert nichts
	            
// ###################################                         
// nun die serialisierten Daten empfangen und daraus ein Objekt machen
ObjectInputStream in_ser = new ObjectInputStream(client.getInputStream());
System.out.println( in_ser.available() );  // -> und hier kommt immer nur 0 raus
Object obj = in_ser.readObject();           // -> und hier bricht er dann ab
// ###################################
	            
in.close();
in_ser.close();
client.close();	// brav wieder alles schließen
socket.close();

// Der Agent ist gelandet und kann jetzt seine Arbeit vollrichten 
Method m = obj.getClass().getMethod("tuWasTolles");
m.invoke(obj);


Der Sendenteil vom anderen Rechner (auch ab Zeile 21):
Java:
String klassenname = this.getClass().getSimpleName();

Socket verbindung = new Socket(ip, port);                  
DataOutputStream out = new DataOutputStream( verbindung.getOutputStream() );
                    
out.writeUTF( klassenname );        // schicke den Klassennamen (Agent)
out.flush();
                    
// .class öffnen und den byte-puffer erstellen
FileInputStream classfile = new FileInputStream("bin/"+paketname+"/"+klassenname+".class");
classfile_bytes = new byte[ classfile.available() ];   // puffer erstellen
                        
out.writeInt( classfile.available() );  // erstmal schicken wie viele Bytes übertragen werden sollen

classfile.read( classfile_bytes );      // Inhalt der .class in den Puffer
classfile.close();
out.write( classfile_bytes  );          // Inhalt des Puffers rausschicken
                     
// ###################################
// einen ObjectStream öffnen um die serialisierten Daten zu übertragen
ObjectOutputStream out_ser = new ObjectOutputStream(verbindung.getOutputStream());
out_ser.writeObject(this);
out_ser.flush();
out_ser.close();
// ###################################


die wichtigen Teile sind zwischen den ###
wie gesagt, das Laden der Klasse funktioniert (ich kann .newInstance machen und dann läuft das auch - nur fehlen dann die Werte in den Variablen). Problem ist dass er aus einem mir nicht ersichtlichen Grund die serialisierten Daten nicht empfängt ( in_ser.available() ist immer 0 - in.available() ist 257).

Ich würde mich riesig freuen wenn mir jemand von euch einen Tipp geben kann wo ich suchen muss, weil so langsam macht das keinen Spass mehr.

Vielen Dank,
Daniel
 
Denke das problem liegt darin, das du mehrere InputStreams zugleichv erwendest, immer nur den höhsten (in deinem fall der ObjectinputStream ) verwenden. Der sollte eigentlich auch die benötigten functionen der tieferliegenden zur verfügung stellen.

Am rande, du bist glaube ich die erste Person die tatsächlich mobile Agents benutzt, ohne würmer zu schreiben ^^.
 
man programmiert halt das was der Prof in den Übungen gern sehen will ;-)

Das mit dem Stream hab ich geändert - jetzt läuft alles über ObjectInputStreams (bzw Output). Das Problem ist dadurch aber leider nicht verschwunden.

kurzer Ausschnitt aus meinen aktuellen experimenten (Senderseite):
Java:
out_ser.writeUTF("EndBinary");
out_ser.flush();

out_ser.writeObject(this);
out_ser.flush();
                    
out_ser.writeUTF("EndSerial");
out_ser.flush();
System.out.println("fertig");

das EndBinary wird in jedem Fall übertragen und der Empfänger empfängt es auch.

Dann sollte er das Objekt schicken, aber der Empfänger bekommt nichts. Ich hab beim Empfänger ein while( in.available() == 0); gemacht und da bleibt er hängen.
Auf Senderseite sieht es aber so aus als sei das Objekt geschickt worden - er kommt bis zum sys.out("fertig")

Wenn ich das .writeObject(this) auskommentiere dann ist alles toll und es kommt auch das EndSerial an.

Normalerweise würd ich jetzt anfangen daran zu zweifeln, dass das .writeObject(this) funktioniert, aber ich hatte es schonmal soweit dass es lief - also das übertragen von serialisierten Daten funktioniert so (und nein, ich kann nicht zu dem Punkt zurück an dem es lief 😉 )
 
auf in.available() kannst du dich nunmal nicht verlassen, Punkt, Aus,
verwende anderen Code und poste vor allem die aktuelle Version des Empfängers
 
ok, also nehm ich das mit available() wieder raus. Dadurch sieht er jetzt wieder so aus wie im Eingangspost.
Aber was meinst du mit "anderen Code"? Ich weiß ja eben nicht wie ich das anders machen könnte.


Aber jetzt kommt ne Fehlermeldung auf der Empfängerseite mit der ich ebenfalls nix anfangen kann:
Java:
java.lang.ClassNotFoundException: A01_Heimkontext.Agent
	at java.net.URLClassLoader$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Unknown Source)
	at java.io.ObjectInputStream.resolveClass(Unknown Source)
	at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
	at java.io.ObjectInputStream.readClassDesc(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.readObject(Unknown Source)
	at A01_Arbeitskontext.Arbeitsserver.main(Arbeitsserver.java:23)

die entsprechende Zeile ist:
[JAVA=23]Object obj = in_ser.readObject();[/code]
(siehe auch Empfänger-Code im ersten Post - Zeilennummern passen)
 
ObjectInputStream in_ser = new ObjectInputStream(client.getInputStream());
Object obj = in_ser.readObject();

passt ja kaum zum Sende des
out_ser.writeUTF("EndBinary");
VOR dem Objekt, daher frage ich mich ob du da andere read-Befehle eingebaut hast,

und ja, einfach nur lesen ohne available() war auch meine Intention für einfachen lauffähigen Code,
hinsichtlich available()-Alternativen kann nicht nichts nennen,

die Klasse des gesendeten Objekts muss auch beim Empfänger bekannt sein, sonst gehts nicht,
höhere Tools wie RMI oder WebService können glaube ich automatisch benötigte Klassen übertragen,
ansonsten bleibt dir nur die Übertragung einfacher ints, Strings, Listen, XML-Text usw.
 
den Sender hab ich auch wieder in den ausgangszustand zurück gebaut. Da hab ich aufgepasst, dass lesen und schreiben immer den selben Typ haben (nicht UTF schreiben und Object lesen).


trotzdem noch mal die aktuelle Version
Java:
ObjectInputStream in_ser = new ObjectInputStream(client.getInputStream());
	            
// dann kommen der Klassenname und die Größe der .class
String klassenname = in_ser.readUTF();
int paketgroesse   = in_ser.readInt();
	
// .class vom Socket lesen
byte[] classfile_bytes     = new byte[paketgroesse];
in_ser.readFully(classfile_bytes);		// vom Socket in den Puffer

// empfangene Klasse laden        
MyClassLoader cl = new MyClassLoader(classfile_bytes);

Class<?> cls = cl.findClass("A01_Heimkontext.Agent");
cls = cl.loadClass("A01_Heimkontext.Agent");
Object obj = cls.newInstance();

obj = in_ser.readObject();

// Der Agent ist gelandet und kann jetzt seine Arbeit vollrichten
Method m = obj.getClass().getMethod("tuWasTolles");
m.invoke(obj);

die .class wird empfangen und in Zeile 16 wird ein Objekt davon erstellt (im Konstruktor ist ein println und das wird auch ausgegeben). Davon ab dass es unsinnig ist das Objekt 2 Zeilen später wieder überschreiben zu wollen (was ja aber eh nich funktioniert). Damit müsste die Klasse doch bekannt sein.

Ansonsten ist das übertragen der einzelnen Variablen auch ne Idee (serialisierte Daten wären schöner, aber wenn es halt nicht anders geht) - kam mir bisher gar nicht in den Sinn.
 
ich glaub ich hab so grob verstanden wo das Problem mit meinem Classloader liegt, allerdings bekomm ich es auch mit Hilfe etlicher Beispiele aus dem Netz nicht zum Laufen. Und weil ich keine Lust hab da noch einen Tag mit zu verschwenden hab ich das jetzt so umgebaut dass die Daten einzeln als Strings und Integer rüber geschickt werden - ganz ohne serialisieren. Funktioniert jetzt so auf jeden Fall.
trotzdem vielen Dank für die Hilfe 🙂


edit: gut, ich hab mich des Themas doch nochmal angenommen und im Nachhinein kann ich nicht mehr so richtig nachvollziehen warum es bei mir gestern Abend nicht mehr funktioniert hat.
Der Link den SlaterB rausgesucht hatte war schon genau der richtige und hier (vorletzter Post) hab ich das auch nochmal gefunden.
Im Grunde wirklich nur ne neue Klasse erstellen die den ObjectinputStream erweitert und dann die ganze Kommunikation über diesen neuen Stream laufen lassen (nur copy'n'paste aus dem sun-forum) . Da die Klasse den selbst gebauten ClassLoader kennt, kann sie mit readObject() die serialisierten Daten entgegen nehmen und sie richtig zusammen setzen.
Wenn man den neuen Stream erstmal hat und gehts quasi automatisch...
Auf jeden Fall nochmal vielen Dank :toll:
 
Zuletzt bearbeitet:

Zurück
Oben