Binärdaten nach struct casten, wie geht das in Java??
-
Hallo,
angenommen ich habe folgendes struct
struct Data { int32_t foo; float bar; }
Und das verschicke ich über einen Socket:
Data d; d.foo = 42; d.bar = 3.14159f; send(socket, (void*)d, sizeof(d), flags);
Dann nehme ich es in C ja wieder relativ einfach entgegen:
void* message; size_t sz = sizeof(Data); std::size_t bytes = recvfrom(socket, message, sz, flags, ...); assert(bytes==sz); Data* d = static_cast<Data*>(message);
Dabei muss man sich natürlich noch um den network-byte order kümmern und aufpassen, wie der Compiler das struct aligned.
Soweit so einfach in C.
Aber was mache ich, wenn ich genau diese Binärdaten auf einem Socket in Java empfange? Wie prokele ich da die int und float member des structs raus??
Gruß
Philipp
-
In Java ist es einfach mit ObjectInput/-OuputStream.
class Foo implements Serializable { private String someData; private int someInt; } // ... ByteArrayOutputStream bstream = new ByteArrayOutputStream(); ObjectOutputStream stream = new ObjectOutputStream(bstream); stream.writeObject(new Foo()); // ... ByteArrayInputStream bstream = new ByteArrayInputStream(bstream.toByteArray()); ObjectInputStream stream = new ObjectInputStream(bstream); foo = (Foo)stream.readObject();
Wichtig ist, dass alle Klassen das Serializable Interface implementieren.
PS: Ich denke nicht dass du die STL und static_cast in C hast.
-
DEvent schrieb:
In Java ist es einfach mit ObjectInput/-OuputStream.
...Das sollte aber nur funktionieren, wenn die Daten auch per Java geschrieben wurden, oder? Ansonsten wirst du das Member für Member - vllt. sogar Byte für Byte wieder zusammensetzen müssen.
-
Meint er, dass er ein C struct in ein Socket schreibst und es dann mit Java ausliest?
Dann ist wohl DataInputStream besser. Man muss nur dafür sorgen, dass die Bytes richtigen endian haben.
http://codeguru.earthweb.com/java/articles/366.shtml
-
DEvent schrieb:
Meint er, dass er ein C struct in ein Socket schreibst und es dann mit Java ausliest?
Dann ist wohl DataInputStream besser. Man muss nur dafür sorgen, dass die Bytes richtigen endian haben.
http://codeguru.earthweb.com/java/articles/366.shtmlGenau das habe ich vor. Ein C++ Programm (daher auch der reinterpret_cast) schreibt einen C-kompatiblen struct auf den Socket und in einem Java-Programm nehme ich das entgegen. Dann möchte ich die Daten rausprokeln. Derzeit mache ich das mit byteweisem rumgeschiebe, scheitere aber am float member:
int index = 0; // hole mir vier einzelne bytes aus dem Buffer bytes[0] = (0x000000FF & ((int) buf[index])); bytes[1] = (0x000000FF & ((int) buf[index + 1])); bytes[2] = (0x000000FF & ((int) buf[index + 2])); bytes[3] = (0x000000FF & ((int) buf[index + 3])); index = index + 4; //baue aus vier bytes einen unsigned integer mit 32bit länge, der aber in Java als 64bit langer integer abgespeichert werden muss, da Java kein unsigned hat long l = ((long) (bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3])) & 0xFFFFFFFFL; // jetzt hole mir wieder vier Bytes aus dem Buffer bytes[0] = (0x000000FF & ((int) buf[index])); bytes[1] = (0x000000FF & ((int) buf[index + 1])); bytes[2] = (0x000000FF & ((int) buf[index + 2])); bytes[3] = (0x000000FF & ((int) buf[index + 3])); // baue wie oben 32bit zusammen und interpretiere das als float. Geht nicht. float f = (float) ((long) (bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]) & 0xFFFFFFFFL);
Darum, das alles als Big-Endian ankommt, kümmert sich das C++-Programm. Was über Netzwerk kommt hat network byte-order.
Phil
-
Der DataInputStream sieht interessant aus. Wie initialisiere ich den von einem Byte-buffer?
-
PhilippM schrieb:
Der DataInputStream sieht interessant aus. Wie initialisiere ich den von einem Byte-buffer?
ByteArrayInputStream bstream = new ByteArrayInputStream(array); DataInputStream stream = new DataInputStream(bstream);
Ich würde die Daten als ein String schicken. Dann erspart man sich das ganze mit den Byteorder und Floats/Doubles Konvertierung usw. Einfach die Zahlen als "0.545344565" schicken oder im CSV Format schicken. Kannst auch als XML schicken, dann hast du ein DTD und kannst es einfach parsen und validieren.
-
Danke.
Und siehe da, dann klappts auch mit dem float. Sehr gut.
Was mich jetzt nur maßlos ankotzt, ist das ich alle integer in doppelt so breiten Typen abspeichern muss, weil Java kein unsigned kennt. Argh.... Das ist keine Sprache, sondern 'ne Zumutung!
-
DEvent schrieb:
Ich würde die Daten als ein String schicken. Dann erspart man sich das ganze mit den Byteorder und Floats/Doubles Konvertierung usw. Einfach die Zahlen als "0.545344565" schicken oder im CSV Format schicken. Kannst auch als XML schicken, dann hast du ein DTD und kannst es einfach parsen und validieren.
Hmm, ich kann auch einen HTTP-Server einbauen, ein XML-Grundgerüst drumherumbauen und dann die 4 bytes Nutzlast noch gzip komprimieren, die nach den 1,5 kilobytes Protokoll-Header kommen. Vielleicht sollte ich aber auch gleich eine CORBA-Infrastruktur aufbauen...
Oder mir ein Loch ins Knie bohren und warten bis Öl rauskommt.Mach du mal ne XML-Validierung auf einem 16bit Microcontroller, dann reden wir weiter
-
http://javolution.org/ bietet auch die Möglichkeit Structs aus C/C++ direkt nachzubauen und hat dabei auch unsigned Typen. Ich weiß allerdings gerade nicht wie die realisiert sind.
-
Im Package nio gibt es Stream-Klassen, die auf Big- bzw. Little-Endian eingestellt werden können. Damit habe ich auch mal ein proprietäres Protokoll umgesetzt, welches vorher nur in C++ realisiert war.
Leider fallen mir die Namen der Klassen gerade nicht ein. Aber das solltest du ja schnell finden.