übergibt einen TCP-Socket (SOCK_STREAM) in C

7

Ich habe eine kleine Client-Server-Anwendung, in der ich eine ganze Struktur über einen TCP-Socket in C nicht C ++ senden möchte. Angenommen, die Struktur ist wie folgt:

%Vor%

Ich habe viele Beiträge gefunden, die sagen, dass ich das Pragma-Pack verwenden oder die Daten vor dem Senden und Empfangen serialisieren muss.

Meine Frage ist, ist es genug, um JUST Pragma Pack oder nur die Serialisierung zu verwenden? Oder muss ich beide verwenden?

Auch weil die serialisierung ein prozessorintensiver Prozess ist, führt dies dazu, dass Ihre Performance drastisch sinkt. Was ist also der beste Weg, um eine Struktur ohne Verwendung einer externen Bibliothek zu serialisieren (ich würde einen Beispielcode / Algo lieben)?

    
user434885 03.11.2011, 19:39
quelle

7 Antworten

13

Sie benötigen Folgendes, um struct's über das Netzwerk portabel zu senden:

  • Packen Sie die Struktur. Für gcc und kompatible Compiler tun Sie dies mit __attribute__((packed)) .

  • Verwenden Sie keine anderen Elemente als vorzeichenlose Integer fester Größe, andere gepackte Strukturen, die diese Anforderungen erfüllen, oder Array-Arrays. Signierte Ganzzahlen sind ebenfalls OK, es sei denn, Ihr Computer verwendet keine Zweierkomplementdarstellung.

  • Entscheiden Sie, ob Ihr Protokoll kleine oder große Endian-Codierung von ganzen Zahlen verwenden wird. Nehmen Sie beim Lesen und Schreiben dieser Ganzzahlen Konvertierungen vor.

  • nimmt auch keine Zeiger auf Elemente einer gepackten Struktur mit Ausnahme von denen mit der Größe 1 oder anderen verschachtelten gepackten Strukturen. Siehe diese Antwort .

Es folgt ein einfaches Beispiel für das Kodieren und Dekodieren. Es setzt voraus, dass die Byte-Reihenfolge-Konvertierungsfunktionen hton8() , ntoh8() , hton32() und ntoh32() verfügbar sind (die ersten beiden sind ein No-Op, aber aus Konsistenzgründen).

%Vor%

Was die Byte-Umrechnungsfunktionen betrifft, können die htons() / ntohs() und htonl() / ntohl() des Systems für 16- bzw. 32-Bit-Ganzzahlen verwendet werden, um in / aus zu konvertieren Big-Endian. Mir ist jedoch keine Standardfunktion für 64-Bit-Ganzzahlen oder die Konvertierung in / aus Little-Endian bekannt. Sie können meine Byte-Reihenfolge-Konvertierungsfunktionen verwenden; Wenn Sie dies tun, müssen Sie ihm die Byte-Reihenfolge Ihrer Maschine mitteilen, indem Sie BADVPN_LITTLE_ENDIAN oder BADVPN_BIG_ENDIAN definieren.

Soweit vorzeichenbehaftete Ganzzahlen betroffen sind, können die Konvertierungsfunktionen genauso sicher implementiert werden wie die, die ich geschrieben und verknüpft habe (Bytes direkt vertauschen); Ändern Sie einfach unsigned in signed.

UPDATE : Wenn Sie ein effizientes Binärprotokoll möchten, aber nicht gerne mit den Bytes herumspielen, können Sie etwas wie Protokollpuffer ( C-Implementierung ). Auf diese Weise können Sie das Format Ihrer Nachrichten in separaten Dateien beschreiben und Quellcode generieren, den Sie zum Codieren und Decodieren von Nachrichten des von Ihnen angegebenen Formats verwenden. Ich habe auch etwas ähnliches implementiert, aber stark vereinfacht; siehe meinen BProto-Generator und einige Beispiele (suchen Sie in .bproto-Dateien und addr.h für ein Anwendungsbeispiel).

    
Ambroz Bizjak 03.11.2011, 20:16
quelle
2

Bevor Sie Daten über eine TCP-Verbindung senden, erarbeiten Sie eine Protokollspezifikation. Es muss kein mehrseitiges Dokument mit Fachjargon sein. Es muss jedoch angegeben werden, wer wann was überträgt, und alle Nachrichten müssen auf Byte-Ebene angegeben werden. Es sollte angeben, wie die Enden von Nachrichten eingerichtet werden, ob es Zeitüberschreitungen gibt und wer sie erzwingt, und so weiter.

Ohne eine Spezifikation ist es einfach, Fragen zu stellen, die einfach nicht zu beantworten sind. Wenn etwas schief geht, an welchem ​​Ende liegt ein Fehler? Bei einer Spezifikation ist das Ende, das der Spezifikation nicht folgte, fehlerhaft. (Und wenn beide Enden der Spezifikation folgen und es immer noch nicht funktioniert, ist die Spezifikation fehlerhaft.)

Sobald Sie eine Spezifikation haben, ist es viel einfacher, Fragen darüber zu beantworten, wie das eine oder das andere Ende entworfen werden sollte.

Ich empfehle auch dringend nicht , ein Netzwerkprotokoll um die Besonderheiten Ihrer Hardware zu entwickeln. Zumindest nicht ohne ein erwiesenes Leistungsproblem.

    
David Schwartz 03.11.2011 19:45
quelle
2

Es hängt davon ab, ob Sie sicher sein können, dass Ihre Systeme an beiden Enden der Verbindung homogen sind oder nicht. Wenn Sie sicher sind, für alle Zeit (die meisten von uns können nicht sein), dann können Sie einige Abkürzungen nehmen - aber Sie müssen sich bewusst sein, dass sie Verknüpfungen sind.

%Vor%

und das analoge read() .

Wenn jedoch die Wahrscheinlichkeit besteht, dass sich die Systeme unterscheiden, müssen Sie festlegen, wie die Daten formell übertragen werden. Sie könnten die Daten gut linearisieren (serialisieren) - möglicherweise mit etwas wie ASN.1 oder wahrscheinlich einfacher mit einem Format, das einfach wieder gelesen werden kann. Dafür ist Text oft von Vorteil - es ist einfacher zu debuggen, wenn Sie sehen können, was schief läuft. Andernfalls müssen Sie die Byte-Reihenfolge definieren, in der ein int übertragen wird, und sicherstellen, dass die Übertragung dieser Reihenfolge folgt, und die Zeichenfolge erhält wahrscheinlich eine Byteanzahl gefolgt von der entsprechenden Datenmenge (überlegen Sie, ob ein Terminal übertragen werden soll) Null oder nicht) und dann eine Darstellung des Floats. Das ist findiger. Es ist nicht so schwer Serialisierungs- und Deserialisierungsfunktionen zu schreiben, um die Formatierung zu handhaben. Der schwierige Teil ist das Entwerfen (Entscheiden) des Protokolls.

    
Jonathan Leffler 03.11.2011 19:49
quelle
1

Sie könnten ein union mit der zu sendenden Struktur und einem Array verwenden:

%Vor%

Auf diese Weise können Sie nur arr. senden und empfangen. Natürlich müssen Sie sich um Endianess-Probleme kümmern, und sizeof(struct something) kann sich von Maschine zu Maschine unterscheiden (aber Sie können dies leicht mit #pragma pack überwinden).

    
BlackBear 03.11.2011 19:44
quelle
1

Warum würdest du das tun, wenn es gute und schnelle Serialisierungsbibliotheken wie Nachrichtenpakete gibt, die all die harte Arbeit für dich erledigen? als Bonus bieten sie Ihnen Sprachkompatibilität Ihres Socket-Protokolls?

Verwenden Sie hierzu Message Pack oder eine andere Serialisierungsbibliothek.

    
Michael Dillon 03.11.2011 19:48
quelle
1

Normalerweise bringt die Serialisierung mehrere Vorteile gegenüber z.B. Senden der Bits der Struktur über die Leitung (mit z.B. fwrite ).

  1. Es geschieht individuell für jede nicht aggregierte atomare Daten (z. B. int).
  2. Definiert genau das serielle Datenformat, das über die Leitung gesendet wird
  3. Es geht also um heterogene Architektur: Sende- und Empfangsmaschinen können unterschiedliche Wortlängen und Endianzen haben.
  4. Es kann weniger spröde sein, wenn sich der Typ ein wenig ändert. Wenn also auf einer Maschine eine alte Version Ihres Codes ausgeführt wird, kann möglicherweise mit einer Maschine mit einer aktuelleren Version, z. Eins mit einem char b[80]; anstelle von char b[64];
  5. Es kann mit komplexeren Datenstrukturen - Vektoren mit variabler Größe oder sogar Hash-Tabellen - auf eine logische Art und Weise umgehen (für die Hash-Tabelle die Assoziation übertragen, ..)

Sehr oft werden die Serialisierungsroutinen erzeugt. Auch vor 20 Jahren existierte RPCXDR bereits zu diesem Zweck, und XDR-Serialisierungsprimitive befinden sich immer noch in vielen libc.

    
Basile Starynkevitch 03.11.2011 19:50
quelle
0

Pragma Pack wird für die binäre Kompatibilität Ihrer Struktur an einem anderen Ende verwendet. Da der Server oder der Client, an den Sie die Struktur senden, möglicherweise in einer anderen Sprache geschrieben oder mit einem anderen C-Compiler oder mit anderen C-Compileroptionen erstellt wurde.

Serialisierung, wie ich verstehe, macht Strom von Bytes von Ihrer Struktur. Wenn Sie struct in den Socket schreiben, machen Sie serialiazation.

    
Art Spasky 03.11.2011 19:49
quelle

Tags und Links