Dynamisches char im struct
-
net schrieb:
btw: structs übers netzwerk schicken kann sowieso problematisch werden weil jeder compiler die speichern darf wie er will...
IMHO zu ungenau, da man Padding und Byte-Alignment bei jedem Compiler beeinflussen können dürfte. Viel problematischer ist, dass der im struct verwendete Datentyp int auf unterschiedlichen Plattformen unterschiedliche Endianness und sogar Größe haben kann.
-
iirc ist sogar die Reihenfolge der Member nicht genau festgelegt. Iirc nur das erste ist garantiert.
-
LordJaxom schrieb:
IMHO zu ungenau, da man Padding und Byte-Alignment bei jedem Compiler beeinflussen können dürfte.
bei manchen nicht, weil der prozessor z.b. keine words an ungeraden adressen verkraftet. wenn du's doch brauchst, dann musste die structs ausschliesslich aus char-arrays aufbauen
-
Danke für eure Antworten. Das struct ist schon so vorgegeben, da lässt sich nichts mehr dran ändern.
Wie soll das gehen mit dem einzeln abholen(Vorschlag von net)? Kannst du oder jemand anderes mir ein kurzes Beispiel geben?
-
... so mal als Grundidee und unter der Voraussetzung, dass beide Kommunikationspartner die Typen int und char identisch behandeln (endian, Länge und 16/32/64-Bit-Ausrichtung):
recvfrom(s, &test.zahl, sizeof(test.zahl), 0, 0, 0); recvfrom(s, &test.zahl2, sizeof(test.zahl2), 0, 0, 0); recvfrom(s, &test.laenge, sizeof(test.laenge), 0, 0, 0); data *test2 = (struct data*)malloc(sizeof(test)-sizeof(test.paket)+test.laenge); if (test2 == NULL) ... Fehler test2->zahl = test.zahl; test2->zahl2 = test.zahl2; test2->laenge = test.laenge; recvfrom(s, &test2->data[0], test2->laenge, 0, 0, 0);
Alternativ (aber unsauberer) könnte man die ersten 3 recvfrom() auch so zusammenfassen:
recvfrom(s, &test, sizeof(test.zahl)+sizeof(test.zahl2)+sizeof(test.laenge), 0, 0, 0);
Anmerkung: "char test.paket[1]" ist nur ein Platzhalter, damit man test.paket wie ein normales Array behandeln kann (deshalb bei malloc() diesen Platzhalter abziehen, sonst wird ein Byte zuviel alloziiert!)
P.S.: ... und nach jedem recvfrom immer schön die Fehler abfangen, insbesondere eine EINTR-Behandlung für sporadische Interrupts
-
Erstmal ein ganz großes Dankeschön an jox!!
Leider scheint das mit deinen recvfrom so nicht zu funktionieren, denn es wird nicht ein ankommendes Paket zerlegt, sondern bei jedem recvfrom wird das nächste Paket genommen und davon die sizeof. Somit gehen natürlich alle Daten verloren bis auf die jeweils erste zahl.
Verstehst du was ich meine?
-
... hab ich voll verstanden.
Mit recvfrom() habe ich allerdings auch schon ewig nicht mehr gearbeitet. Ich nehme immer die Grundroutinen (read bzw. write).Überprüf mal, was Deine recvfrom() Aufrufe als Return-Wert zurückgeben (sollte man immer machen ;), etwa:
rc = recvfrom(...); if (rc < 0) { fprintf(stderr,"line=%d, rc=%d, errno=%d (%s)\n",__LINE__,rc,errno,strerror(errno)); }
Ich vermute, dass als Returncode -1 zurückkommt und errno == MSG_TRUNC ist. Was soviel bedeutet, dass wir einen Datagram-Socket haben und der Empfangspuffer zu klein ist.
Nach kurzer Durchsicht der Manual Pages zu recvfrom() nehme ich allerdings an, dass es an der Generierung/Anlage des Sockets liegt. Versuch doch mal:
sockfd = socket(AF_INET,SOCK_STREAM,0);
Mach ich jedenfalls so bei der Server- und der Client-Komponente.
Sonst müssen wir es mir mal mit read() probieren, obwohl ich hier ähnliche Probleme vermute und keine Lösung des Problems sehe.
Wenn Du allerdings die Server-Seite nicht beeinflussen kannst, dann muss man einen anderen Lösungsweg beschreiten. Hierzu müsste ich wissen, ob Du die maximale Größe des Datenpakets kennst.
Doch schau erst einmal, was für ein Returncode zurückkommt.
-
Einen Fehler gibt mein recvfrom nicht zurück. Übrigens fange ich natürlich dort immer die Fehler ab, aber der Code sollte nicht zulang werden. Trotzdem danke für den Hinweis!
read hatte ich schonmal getestet ohne das sich was geändert hat.
Die socket-Methode habe ich auch so schon getestet, ohne Veränderung.Die maximale Größe kenne ich nicht wirklich, aber die Größen bewegen sich im Bereich von 1000-1500. Mehr als 2000 ist es auf jeden Fall nie. Da kann man die Größe ja einfach groß genung wählen z.B. 2000.
Wie das dann aber funktionieren soll weiß ich trotzdem nicht und hoffe da nochmal auf deine Mithilfe. Soweit schonmal vielen Dank!
-
jox schrieb:
recvfrom(s, &test.zahl, sizeof(test.zahl), 0, 0, 0);
man muss den rückgabewert von recv checken. ist der grösser 0 aber kleiner als die ses 'sizeof(test.zahl)' dann wurde nur ein teil empfangen. in diesem fall also recv wiederholt aufrufen bis man alles hat.
-
Der erste Teil (eine Zahl) wird doch auch korrekt empfangen. Gerade das wiederholte aufrufen von recv klappt ja nicht, weil dann das nächste empangene Paket genommen wird und nicht das 'alte' aus dem der Rest gelesen werden soll.
-
germangeek schrieb:
Der erste Teil (eine Zahl) wird doch auch korrekt empfangen. Gerade das wiederholte aufrufen von recv klappt ja nicht, weil dann das nächste empangene Paket genommen wird und nicht das 'alte' aus dem der Rest gelesen werden soll.
dann benutzt du udp (datagramme) und nicht tcp (stream)?
-
jup, ganz genau. Ich hätte nicht erwartet, dass das einen Unterschied macht.
-
germangeek schrieb:
jup, ganz genau. Ich hätte nicht erwartet, dass das einen Unterschied macht.
einen gewaltigen unterschied macht das. tcp schiebt alles empfangene in einen FIFO aus dem der user sich das häppchenweise holen kann. bei udp bekommste pakete und wenn man nicht alles abholt ist der rest weg. guckst du: http://www.c-worker.ch/tuts/udp.html
-
Verdammt! Ich muss an der Stelle leider bei UDP bleiben.
Gibt ess eine Möglichkeit das zu umgehen? Ansonsten hoffe ich ja noch auf jox mit einer genialen Lösung
-
mach ein recv oder recvfrom unter angabe der vollen länge:
unsigned char buffer[1500]; ... recv_result = recv (socket, buffer, sizeof(buffer), 0); ...
dann haste alle daten.
-
Ich bekomme ja nun aber gerade einen struct geschickt! In diesem stehen am Anfang mehrere Zahlen, dann kann ich doch nicht einfach alles in einen char schreiben, oder doch?
-
struct data *test = (struct data*) buffer; printf("Laenge der Daten: %d\n", test->laenge);
-
Da gibt aber den Fehler:
error: dereferencing pointer to incomplete type
-
germangeek schrieb:
Ich bekomme ja nun aber gerade einen struct geschickt! In diesem stehen am Anfang mehrere Zahlen, dann kann ich doch nicht einfach alles in einen char schreiben, oder doch?
Doch, würde ich so auch versuchen. Einfach einen Buffer mit der maximalen Länge vorsehen und dann alles einlesen:
unsigned char buffer[MAX_LENGTH]; struct data *test; rc = recvfrom(s, buffer, sizeof(buffer), 0, 0, 0); test = (struct data *) buffer; if (rc < (sizeof(test->zahl)+sizeof(test->zahl2)+sizeof(test->laenge)) => Error else if (rc != (test->laenge+sizeof(test->zahl)+sizeof(test->zahl2)+sizeof(test->laenge))) => Error else => alles korrekt empfangen ...
Aber Achtung: UDP ist ein ungesichertes Protokoll. Es ist nicht sichergestellt, dass alle Daten rüberkommen! Deswegen der Vergleich: "rc != test->laenge"
-
germangeek schrieb:
Ich bekomme ja nun aber gerade einen struct geschickt! In diesem stehen am Anfang mehrere Zahlen, dann kann ich doch nicht einfach alles in einen char schreiben, oder doch?
das geht (im idealfall so wie lordJax vorschlägt, aber unter vorbehalt, gleicher compiler, gleiche architektur auf beiden seiten usw.). rückgabewert von recv ist dann die paketlänge (also alle struct members und der datenblock)