webentwicklung-frage-antwort-db.com.de

Struktur durch Sockets in C übergeben

Ich versuche, die gesamte Struktur vom Client zum Server oder umgekehrt zu übertragen. Nehmen wir meine Struktur wie folgt an

struct temp {
  int a;
  char b;
}

Ich verwende sendto und sende die Adresse der Strukturvariablen und empfange sie auf der anderen Seite mit der Funktion recvfrom. Aber ich kann die Originaldaten nicht empfangen. In der sendto-Funktion speichere ich die empfangenen Daten in eine Variable vom Typ struct temp.

n = sendto(sock, &pkt, sizeof(struct temp), 0, &server, length);
n = recvfrom(sock, &pkt, sizeof(struct temp), 0, (struct sockaddr *)&from,&fromlen);

Wobei pkt die Variable vom Typ struct temp ist.

Obwohl ich 8 Bytes an Daten erhalte, werden beim Versuch zu drucken lediglich die Müllwerte angezeigt. Irgendeine Hilfe für eine Lösung?

HINWEIS:Es müssen keine Bibliotheken von Drittanbietern verwendet werden.

EDIT1: Ich bin wirklich neu in diesem Serialisierungskonzept. Aber ohne Serialisierung Kann ich keine Struktur über Sockets senden?

EDIT2: Wenn ich versuche, einen String oder eine Integer-Variable mit der - sendto und recvfrom Ich empfange die Daten auf der Empfängerseite ordnungsgemäß. Warum nicht bei einer Struktur? Wenn ich die Serialisierungsfunktion nicht verwenden muss, sollte ich dann jedes einzelne Mitglied der Struktur einzeln senden? Dies ist wirklich keine geeignete Lösung, da bei einer Anzahl von "n" Mitgliedern "n" Codezeilen hinzugefügt werden, um Daten zu senden oder zu empfangen.

47
codingfreak

Das ist eine sehr schlechte Idee. Binärdaten sollten immer so gesendet werden, dass:

Schreiben Sie niemals eine ganze Struktur auf binäre Weise, nicht in eine Datei, nicht in einen Socket.

Schreiben Sie jedes Feld immer einzeln und lesen Sie sie auf die gleiche Weise.

Sie müssen Funktionen wie haben

unsigned char * serialize_int(unsigned char *buffer, int value)
{
  /* Write big-endian int value into buffer; assumes 32-bit int and 8-bit char. */
  buffer[0] = value >> 24;
  buffer[1] = value >> 16;
  buffer[2] = value >> 8;
  buffer[3] = value;
  return buffer + 4;
}

unsigned char * serialize_char(unsigned char *buffer, char value)
{
  buffer[0] = value;
  return buffer + 1;
}

unsigned char * serialize_temp(unsigned char *buffer, struct temp *value)
{
  buffer = serialize_int(buffer, value->a);
  buffer = serialize_char(buffer, value->b);
  return buffer;
}

unsigned char * deserialize_int(unsigned char *buffer, int *value);

Oder das Äquivalent, es gibt natürlich verschiedene Möglichkeiten, dies in Bezug auf die Pufferverwaltung usw. einzustellen. Dann müssen Sie die übergeordneten Funktionen ausführen, die ganze Strukturen serialisieren/deserialisieren.

Dies setzt voraus, dass die Serialisierung zu/von Puffern erfolgt, was bedeutet, dass die Serialisierung nicht wissen muss, ob das endgültige Ziel eine Datei oder ein Socket ist. Dies bedeutet auch, dass Sie einen gewissen Arbeitsspeicher-Overhead zahlen, aber dies ist im Allgemeinen aus Leistungsgründen ein gutes Design (Sie möchten nicht jeden Wert in den Socket schreiben ()).

Wenn Sie die oben genannten Informationen haben, können Sie eine Strukturinstanz folgendermaßen serialisieren und übertragen:

int send_temp(int socket, const struct sockaddr *dest, socklen_t dlen,
              const struct temp *temp)
{
  unsigned char buffer[32], *ptr;

  ptr = serialize_temp(buffer, temp);
  return sendto(socket, buffer, ptr - buffer, 0, dest, dlen) == ptr - buffer;
}

Ein paar Punkte zu beachten:

  • Die zu sendende Struktur wird zuerst Feld für Feld in buffer serialisiert.
  • Die Serialisierungsroutine gibt einen Zeiger auf das nächste freie Byte im Puffer zurück, mit dem berechnet wird, auf wie viele Bytes es serialisiert wurde
  • Offensichtlich schützen meine Beispiel-Serialisierungsroutinen nicht vor einem Pufferüberlauf.
  • Der Rückgabewert ist 1, wenn der sendto() -Aufruf erfolgreich war, andernfalls ist er 0.
69
unwind

Die Verwendung der "Pragma" -Pack-Option hat mein Problem gelöst, aber ich bin nicht sicher, ob es irgendwelche Abhängigkeiten hat?

#pragma pack(1)   // this helps to pack the struct to 5-bytes
struct packet {
int i;
char j;
};
#pragma pack(0)   // turn packing off

Dann haben die folgenden Codezeilen problemlos funktioniert

n = sendto(sock,&pkt,sizeof(struct packet),0,&server,length);

n = recvfrom(sock, &pkt, sizeof(struct packet), 0, (struct sockaddr *)&from, &fromlen);
10
codingfreak

Es ist nicht erforderlich, eigene Serialisierungsroutinen für short- und long -Integer-Typen zu schreiben - verwenden Sie htons()/htonl() POSIX-Funktionen.

7
qrdl

Wenn Sie den Serialisierungscode nicht selbst schreiben möchten, suchen Sie ein geeignetes Serialisierungsframework und verwenden Sie dieses.

Vielleicht wäre Googles Protokollpuffer möglich?

6
Douglas Leeder

Serialisierung ist eine gute Idee. Sie können auch Wireshark verwenden, um den Datenverkehr zu überwachen und zu verstehen, was tatsächlich in den Paketen passiert.

1
eyalm

Anstatt zu serialisieren und abhängig von Bibliotheken von Drittanbietern, ist es einfach, ein primitives Protokoll mit Tags, Längen und Werten zu entwickeln.

Tag: 32 bit value identifying the field
Length: 32 bit value specifying the length in bytes of the field
Value: the field

Nach Bedarf verketten. Verwenden Sie für die Tags Aufzählungen. Und benutze die Netzwerk-Bytereihenfolge ...

Einfach zu kodieren, einfach zu dekodieren.

Wenn Sie TCP) verwenden, denken Sie daran, dass es sich um einen Datenstrom handelt. Wenn Sie also zB 3 Pakete senden, erhalten Sie nicht unbedingt 3 Pakete Ein Stream, der unter anderem vom NODELAY/NAGEL-Algorithmus abhängt, und Sie erhalten möglicherweise alle in einem Recv ... Sie müssen die Daten beispielsweise mit RFC1006 abgrenzen.

UDP ist einfacher, Sie erhalten für jedes gesendete Paket ein eigenes Paket, aber es ist viel weniger sicher.

0
hplbsh

Wenn das Format der zu übertragenden Daten sehr einfach ist, ist das Konvertieren in und von einem ANSI-String einfach und portabel.

0
the_mandrill