[gelöst] Binary-Float von Java nach C++ [ Netzwerkprogammierung ]
-
Hallo,
ich bin mir nicht sicher, ob das Thema hie richtig ist, ich leg einfach mal los.
Ich schreibe eine Client/Server-Applikation. Der Server wird in C++ geschrieben, der Client in Java.
Nun bin ich soweit, dass ich Daten vom Client zum Server versenden kann.
Das Empfangen von Integerwerten ist kein Problem.
Ich muss vor allem Gleitkommazahlen an den Server schicken.Ich gehe wie folgt vor: float als binäre Zeichenkette verschicken (das umwandeln passiert in der Methode wrtieFloat() ), hier wieder nach float convertieren und die byteorder von network to host ändern.
Leider klappt das nicht wie erwartet, weil am Ende die float Variable den Wert 0 hat.Senden an Server:
DataOutputStream dos; float f; . . . dos.writeFloat(f);
char buf[MAXRECV + 1]; memset(buf, 0, MAXRECV + 1); int status = ::recv(m_sock, buf, MAXRECV, 0); float *fb = ((float * ) buf); // Byteorder: Netwrol->Host // float hostFloat = ntohl(*fb);
ntohl() funktioniert wohl nur mit int.
Wie castet man hier richtig?Gruß
Viktor
-
Verschick doch einfach die puren ASCII-Bytes, z.B. "6.32" und kovertiere es am Server in Float.
-
Ad aCTa schrieb:
Verschick doch einfach die puren ASCII-Bytes, z.B. "6.32" und kovertiere es am Server in Float.
Die absolut bessere Wahl. Dadurch erhöht sich zwar die Datenmenge, allerdings muss man sich dann nicht um
a) den internen Aufbau von float, und
b) Byte-Order
kümmern. Ein klarer Vorteil der guten alten Zeichenkette.
-
Da du ein eigenes Protokoll schreibst, legst Du am besten die Byte-Ordnung selbst fest. Die ist bei Netzwerkprotokollen, soweit ich weiß, typischerweise "Big Endian". Das ist auch genau das, was DataOutputStream zu machen scheint.
Wenn Du die Fließkommazahl nicht "per Hand" wieder zusammensetzen willst, müsstest Du prüfen, ob für den Server auch float=IEEE-754 32Bit gilt und wenn ja, wie so ein Objekt im Speicher liegt. Wenn Du Dir sicher bist, dass der Server auch mit IEEE-754 32Bit floats arbeitet und diese mit dem niederwertigsten Byte zuerst im Speicher liegen könnte man das so machen:
#include <cstring> #include <algorithm> unsigned char buffer[sizeof(float)]; /* ... buffer füllen ... */ std::reverse(buffer,buffer+sizeof(float)); float x; std::memcpy(&x,buffer,sizeof(float));
Da hier aber bestimmte Implementierungsdetails ausgenutzt werden, die nicht garantiert werden, kann es natürlich zu Problemen kommen, wenn Du Dein Programm auf einem anderen Rechner kompilieren und benutzen willst. Es empfiehlt sich also als Teil des Build-Prozesses einen Test einzubauen, der überprüft, ob die Zahlen auf diese Weise richtig rekonstruiert werden.
-
Ad aCTa schrieb:
Verschick doch einfach die puren ASCII-Bytes, z.B. "6.32" und kovertiere es am Server in Float.
Diese Methode ist bereits implementiert und funktioniert.
Später sollen relativ große Datenmengen üertragen und verarbeitet werden. ASCII-Zeichenketten würden das nur unnötig belasten.Ich habe nun einen kleinen Testclient in C++ geschrieben, der die Daten an den Server sendet, das funktioniert ebenfalls problemlos. Es muss also daran liegen, wie ein float in Java codiert wird.
Man findet zwar im Netz tausende Tutorials wie man eine Netzwerkverbindung mit Sockets aufbaut, aber nicht wie man Daten effizient versendet
-
krümelkacker schrieb:
Da du ein eigenes Protokoll schreibst, legst Du am besten die Byte-Ordnung selbst fest. Die ist bei Netzwerkprotokollen, soweit ich weiß, typischerweise "Big Endian". Das ist auch genau das, was DataOutputStream zu machen scheint.
Wenn Du die Fließkommazahl nicht "per Hand" wieder zusammensetzen willst, müsstest Du prüfen, ob für den Server auch float=IEEE-754 32Bit gilt und wenn ja, wie so ein Objekt im Speicher liegt. Wenn Du Dir sicher bist, dass der Server auch mit IEEE-754 32Bit floats arbeitet und diese mit dem niederwertigsten Byte zuerst im Speicher liegen könnte man das so machen:
#include <cstring> #include <algorithm> unsigned char buffer[sizeof(float)]; /* ... buffer füllen ... */ std::reverse(buffer,buffer+sizeof(float)); float x; std::memcpy(&x,buffer,sizeof(float));
Da hier aber bestimmte Implementierungsdetails ausgenutzt werden, die nicht garantiert werden, kann es natürlich zu Problemen kommen, wenn Du Dein Programm auf einem anderen Rechner kompilieren und benutzen willst. Es empfiehlt sich also als Teil des Build-Prozesses einen Test einzubauen, der überprüft, ob die Zahlen auf diese Weise richtig rekonstruiert werden.
Klasse! Es funktionert genau so wie oben beschrieben!
Danke vielmals, ich sitze nämlich seit Tagen an diesem Problem
-
exagi schrieb:
Klasse! Es funktionert genau so wie oben beschrieben!
Danke vielmals, ich sitze nämlich seit Tagen an diesem ProblemAber behalte im Kopf, dass dies nicht portabel ist.
-
Hallo zusammen,
Gibt es evtl. ein Macro oder so mit dem man prüfen kann, ob
der Compiler IEEE-754 32bits verwendet wird?
Dann kann man die schnelle vorgeschlagene Variante verwenden und
sonst anders berechnen.Die Bestimmung von der Byte-Order kriegt man ja so raus, wie ich nach
kurzer google-Recherche gesehen habe so raus:
http://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machineGruß,
CSpilleEDIT: ups... Die wikipedia-Variante bringt einen ja hier nicht weiter
EDIT2: Naja doch ^^
-
CSpille schrieb:
Gibt es evtl. ein Macro oder so mit dem man prüfen kann, ob
der Compiler IEEE-754 32bits verwendet wird?Es gibt std::numeric_limits<float>::is_iec559 im
<limits>
-Header. IEC559 ist nur ein anderer Name eines anderen Normungsvereins.Im Notfall kann man ein eigenes Test-Programm als Teil des Build-Prozesses kompilieren. Das eine oder andere Build-System könnte aber auch so eine Funktion anbieten und Implementierungsdetails über eine automatisch generierte Header-Datei zur Verfügung stellen.
-
Hey krümelkacker,
du bist mein Double-KonvertierungsGOTT!!!
thx