Kann ich die Bytereihenfolge im Netzwerk ignorieren?

24

Ich entwickle eine Server-Client-Anwendung, bei der der Client unter Windows und der Server wahrscheinlich unter Linux ausgeführt wird. Vielleicht portiere ich den Client später auf Mac und Linux, aber noch nicht.

Alle Heimcomputer laufen heutzutage auf Little-Endian. Ich habe eine Weile gegoogelt, aber ich konnte nicht wirklich eine Liste von Geräten finden, die auf Big-Endian laufen. Soweit ich weiß, verwenden einige Motorola-Chips immer noch Big-Endian und möglicherweise einige Telefone (ich habe nicht vor, die App auf Smartphones zu portieren, das ist mir also egal). Warum sollte ich die Bytes jeder ganzen Zahl, jeder kurzen Zahl, jeder Gleitkommazahl, jeder doppelten Zahl usw. zum Lesen und Schreiben neu anordnen , wenn ich bereits weiß, dass sowohl Server als auch Client auf Little-Endian ausgeführt werden?

Das ist nur unnötige Arbeit. Meine Frage lautet also: Kann ich die Endianität ignorieren und nur Little-Endian-Daten senden? Was sind die Nachteile?

tkausl
quelle
4
Woher wissen die Computer, ob sie Little-Endian-Daten anstelle der üblichen / standardmäßigen Big-Endian-Daten empfangen?
Ixrec
2
Sie müssen zwischen den Metadaten, die vom Netzwerkprotokoll benötigt werden, und den Nutzdaten unterscheiden, bei denen es sich nur um eine Reihe von nicht interpretierten Bytes für alle außer Ihrem Code handelt. Ich hoffe, Sie rollen nicht Ihren eigenen Netzwerkstapel. Folglich gehe ich davon aus, dass es sich bei der Frage nur um die Nutzlast handelt, richtig?
2
@delnan ja, rede nur über die Nutzlast. Ich werde natürlich immer noch in Netzwerk-Bytereihenfolge mit dem Netzwerk-Stack selbst sprechen .
Tkausl
3
Nebenbei ein Gedanke: Müssen Sie wirklich auf einer Abstraktionsebene arbeiten, auf der es um Endianität geht? Es könnte sich lohnen, über die Verwendung von Protokollen nachzudenken, für die entsprechende Bibliotheken existieren, die all dieses "Durcheinander" auf niedriger Ebene kapseln. Dann haben Sie auch den zusätzlichen Bonus, dass das Hinzufügen weiterer Kunden viel einfacher gemacht werden kann.
Godfatherofpolka
1
@tkausl Nur noch zwei Gedanken zur Seite: In der Regel ist IO im Vergleich zu Berechnungen extrem langsam, sodass ein durch Arbeiten auf einer höheren Abstraktionsebene verursachter Overhead höchstwahrscheinlich vernachlässigbar ist. Es kann sogar vorkommen, dass einige Bibliotheken aufgrund des cleveren Ressourcenpools und der asynchronen Verarbeitung usw. die von Hand durchgeführten Implementierungen übertreffen. Daher würde ich zunächst vorhandene Lösungen sorgfältig bewerten. In Anbetracht Ihrer Beschreibung würde ich mich auch eher mit der Skalierbarkeit als mit der Leistung befassen. Hier könnten Sie wiederum von der Verwendung von Protokollen höherer Ebenen profitieren.
Godfatherofpolka

Antworten:

29

... warum sollte ich die Bytes neu anordnen ... wenn ich bereits weiß, dass sowohl Server als auch Client auf Little Endian laufen? Das ist nur unnötige Arbeit.

Es ist nur unnötig, wenn Sie garantieren können, dass Ihr Code immer auf Little-Endian-Architekturen ausgeführt wird. Wenn Sie sich eine lange Lebensdauer wünschen, lohnt es sich, in einem Jahrzehnt, in dem einige Big-Endian-Architekturen zum "In" -Ding geworden sind und Sie der Meinung sind, dass dies ein guter Markt für Code ist, nicht mehr zu stören Ihre Bewerbung.

Es gibt eine Netzwerkstandard-Bytereihenfolge. Es ist Big-Endian, aber nichts sagt, dass Sie sich bei der Gestaltung Ihres Protokolls daran halten müssen. Wenn Sie im Voraus wissen, dass die Mehrheit der Systeme, auf denen Ihr Code ausgeführt wird, Little-Endian-Systeme sind und die Leistung kritisch ist, deklarieren Sie die "tkausl standard byte ordering" und folgen Sie ihr. Wo Sie normalerweise anrufen htons(), um die Dinge in die von Ihnen benötigte Reihenfolge zu bringen, schreiben Sie ein Makro mit dem Namen htots(), das auf Little-Endian-Architekturen unter bestimmten Bedingungen zu nichts kompiliert und auf Big-Endian neu arrangiert wird.

Es ist nicht wirklich ein großer Aufwand, den Code für die eingehenden und ausgehenden Konvertierungen beizubehalten. Wenn Sie eine sehr große Anzahl von Nachrichten haben, suchen Sie nach einer Möglichkeit, diese auszudrücken, und schreiben Sie ein Programm, um die eingehenden und ausgehenden Konvertierungen zu generieren.

Blrfl
quelle
10
Der Wortlaut when designing your protocolist wichtig, da implizit festgelegt ist, dass diese Option nur beim Entwerfen eines neuen Protokolls und nicht beim Implementieren eines vorhandenen Protokolls vorhanden ist. Wenn man die Notwendigkeit einer htots(und wirklich einer ganzen Familie von Funktionen) erwähnt, wird auch klar, dass die Auswahl einer anderen Byte-Reihenfolge nicht dazu dient, den Code zu vereinfachen, sondern ihn möglicherweise etwas schneller zu machen.
Kasperd
4
Es gibt (Nicht-Standard , aber sehr häufig in diesen Tagen) Funktionen htole32(), htole16(), le16toh()etc. Funktionen zur Verfügung. Die Datei, die eingeschlossen werden muss, um diese deklariert zu bekommen, ist leider noch weniger standardisiert: <endian.h>oder <sys/types.h>abhängig von der Plattform.
Torek
Diese Antwort ist in Ordnung, aber ich denke, die Annahme, dass die Leistung im gegebenen Fall kritisch sein könnte, ist höchstwahrscheinlich eine falsche Annahme, die mehr auf Aberglauben als auf Fakten beruht.
Doc Brown
1
@DocBrown: Ich möchte immer darauf hinweisen, dass das X-Protokoll die Auswahl Ihrer eigenen Bytereihenfolge seit 30 Jahren unterstützt und so knapp die Ressourcen damals waren, niemand hat sich jemals darüber beschwert, dass es ein Problem war.
Blrfl
7

Es ist dein Protokoll.

Sie können es nicht sicher ignorieren. Aber Sie können es sicher beschriften. Sie steuern den Client und den Server. Sie steuern das Protokoll. Ist es nicht sinnvoll, sich nicht darum zu kümmern, ob es sich um Big-Endian oder Little-Endian handelt, solange Sie wissen, ob beide Seiten einverstanden sind?

Das bedeutet Overhead. Jetzt musst du deine Endianness irgendwie markieren. Tun Sie das, und ich kann es auf alles lesen.

Wenn Sie keinen Daten-Overhead wünschen und Ihre CPU gelangweilt ist und etwas zu tun sucht, passen Sie sich an .

kandierte_orange
quelle
6

Meine Frage lautet also: Kann ich die Endianess ignorieren und nur Little-Endian-Daten senden?

Dafür gibt es zwei Interpretationen:

  • Wenn Sie Ihre Anwendungen / Protokolle so gestalten, dass immer 1 Little-Endian gesendet wird, ignorieren Sie Endianess NICHT.

  • Wenn Sie Ihre Anwendungen / Protokolle so entwerfen, dass sie unabhängig von der nativen Endianess gesendet / empfangen werden, funktionieren sie, solange Sie Ihre Anwendungen auf Plattformen mit derselben nativen Endianess ausführen.

    Ist das "sicher" 2 ? Das müssen Sie beurteilen! Aber sicherlich gibt es gängige Hardware-Plattformen, die Little-Endian, Big-Endian oder ... Bi-Endian verwenden.

    Referenz:

Was sind die Nachteile?

Der offensichtliche Nachteil des Ignorierens von Endianess besteht darin, dass Sie ein Problem haben, wenn Sie / Ihre Benutzer Ihre Anwendungen / Protokolle zwischen Plattformen mit unterschiedlichen nativen Endianess ausführen müssen. Die Anwendungen brechen ab, und Sie müssen sie ändern, um das Problem zu beheben. Und mit Versionskompatibilitätsproblemen usw. umgehen.

Natürlich sind die meisten Plattformen der aktuellen Generation von Haus aus Little-Endian-Plattformen, aber 1) einige sind es nicht, und 2) wir können nur raten, was in Zukunft passieren wird.


1 - Immer ... auch auf Plattformen, die von Haus aus Big-Endian sind.

2 - Was bedeutet eigentlich "sicher"? Wenn Sie uns bitten, die zukünftige Richtung von Hardwareplattformen vorherzusagen ... Ich fürchte, das ist objektiv nicht zu beantworten.

Stephen C
quelle
3

Endianness ist nicht die einzige Überlegung. Es gibt die Größe von Ganzzahlen, das Packen von Strukturen, die Sie senden oder empfangen möchten, und so weiter.

Sie können das alles ignorieren. Niemand kann dich zwingen. Auf der anderen Seite besteht die sichere und zuverlässige Möglichkeit darin, ein externes Format zu dokumentieren und dann Code zu schreiben, der das externe Format korrekt liest oder schreibt, unabhängig von Ihrem Prozessor, Ihrer Programmiersprache und der Implementierung Ihrer Programmiersprache.

Normalerweise ist es nicht viel Code. Aber es hat einen enormen Vorteil: Leute, die Ihren Code lesen, ahnen nicht, dass Sie ahnungslos sind, wissen nichts über den Austausch externer Daten und schreiben Code, dem im Allgemeinen nicht vertraut werden kann.

gnasher729
quelle
3

Der Standard-BSD-Networking-Stack in C verfügt über die Funktion hton/ ntoh( network-to-host/ host-to-network), die auf netzwerkgebundenen Rechnern (Big Endian) zu No-Ops erweitert wird. Für das Szenario, in dem die netzwerkspezifische Bytereihenfolge Little Endian ist, benötigen Sie Ihre eigenen Gegenstücke.

Das ist der robuste Weg.

Es wäre unkonventionell, aber ich sehe nichts falsch daran. Netzwerkcomputer erhalten immer Byteströme und müssen sich auf Protokolle einigen, wie diese Bytes interpretiert werden sollen. Dies ist nur ein Teil davon.

PSkocik
quelle
3

Verschiedene Protokolle zur Datenübertragung zwischen Servern verwenden Little-Endian-Nummern:

  1. BSON
  2. Protokollpuffer
  3. Capn Proto

Unter https://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats finden Sie Details zu verschiedenen Formaten, von denen einige Little-Endian-Zahlen und einige Big-Endian-Zahlen aufweisen.

Es ist absolut nichts Falsches daran, ein Protokoll zu verwenden, das auf Little-Endian-Zahlen basiert. Ein Big-Endian-Rechner kann kleine Endian-Zahlen genauso lesen wie ein Little-Endian-Rechner große Endian-Zahlen. Viele Leute haben es speziell getan, um die zusätzlichen Berechnungskosten für das Decodieren von Big-Endian-Zahlen auf Little-Endian-Maschinen zu vermeiden.

Wenn Sie Ihr Protokoll auf einem dieser vorhandenen Protokolle aufbauen, müssen Sie sich nicht einmal um das Problem selbst kümmern, es ist bereits erledigt. Wenn Sie sich entscheiden, Ihren Code auf einer Big-Endian-Plattform auszuführen, sorgen die Bibliotheken, die diese Protokolle implementieren, automatisch dafür, dass Sie die Werte korrekt dekodieren.

Winston Ewert
quelle
2

Ein Beispiel für ein Big-Endian-System ist das in Routern verwendete MIPS. Sowohl ARM als auch MIPS können auf Endian umgeschaltet werden. Häufig handelt es sich bei MIPS jedoch um Big Endian, da dies die Netzwerkhardware vereinfacht (der wichtigste Teil eines Wortes ist der Teil, den Sie zuerst erhalten und der eine Routing-Entscheidung treffen kann, bevor Sie den Rest erhalten haben) das Wort, anstatt das ganze Wort puffern zu müssen).

Es hängt also davon ab, was Sie unter "Linux" verstehen. Wenn Sie Ihre Server-App jedoch jemals auf einem kleineren System wie einem Router mit OpenWRT ausführen möchten, müssen Sie möglicherweise die Big-Endian-Unterstützung in Betracht ziehen.

Wie üblich ist die Vereinfachung von Annahmen eine absolut sinnvolle Optimierung, bis Sie auf etwas stoßen, das nicht zu den Annahmen passt. Nur Sie können sagen, wie schmerzhaft es wäre, sie abzuwickeln, wenn Sie jemals auf ein solches Problem stoßen.

user1908704
quelle
0

Ich glaube nicht, dass eine der Antworten genau genug ist. Endianness ist laut Wikipedia die Reihenfolge der Bytes, aus denen ein Wort besteht.

Nehmen wir 4 Bytes und interpretieren sie als int. Bei einem Little-Endian-System werden die Bytes von rechts nach links und bei einem Big-Endian-System umgekehrt interpretiert. Offensichtlich ist es wichtig zu vereinbaren, welches Ende ein Int. Interpretieren soll.

Lassen Sie uns ein bisschen auf moderne Netzwerkprotokolle eingehen, die json oder xml verwenden könnten. Keines dieser Formate überträgt ein Int mit 4 Bytes. Sie übertragen die Daten als Text, der auf der Empfängerseite als int analysiert wird.

Am Ende spielt Endianness also keine Rolle, wenn Sie json oder xml verwenden. Wir müssen immer noch Big Endian für TCP-Header verwenden, weshalb es als Netzwerk-Bytereihenfolge bezeichnet wird, aber die meisten Programmierer müssen sich nicht täglich damit herumschlagen.

Die am weitesten verbreitete Codierung ist heute meistens utf-8, die auch vor Problemen in Bezug auf Endianität gefeit sein kann .

Also würde ich ja sagen. Es ist sicher, Endianness zu ignorieren, wenn textbasierte Formate verwendet werden, die mit utf-8 übertragen wurden.

Esben Skov Pedersen
quelle
zwei Stimmen und keine Kommentare. Groß.
Esben Skov Pedersen
1
Ich war nicht der Abwähler, aber diese Antwort scheint eine vollkommen gültige Frage zu ignorieren / abzulehnen. Nur weil einige Protokolle textbasiert sind, müssen dies nicht alle Protokolle sein.
Peter Green
2
Ich habe das positiv bewertet, weil es die Tatsache berührt, dass das Payload-Format nichts mit den zugrunde liegenden Protokollen zu tun hat. Manche Leute lieben es, sich mit erfundenen Problemen auseinanderzusetzen.
Zdenek
0

Big-Endian-Systeme scheinen auf dem Weg nach draußen zu sein. Viele der traditionellen Unixe verwendeten Big Endian, aber sie sind seit Jahren zugunsten von Linux auf x86 rückläufig.

Arm ist Bi-Endian, aber die Big-Endian-Variante scheint selten zu sehen zu sein.

Mips gibt es in beiden Varianten. Die Big-Endian-Variante wird hauptsächlich bei Netzwerkanwendungen verwendet (aus historischen Gründen verwenden Internetprotokolle im Allgemeinen Big-Endian).

ppc war traditionell Big-Endian mit einigen Teilen, die beide Endian unterstützen, aber IBM scheint nun den Little-Endian-Modus für 64-Bit-ppc voranzutreiben (sie haben kürzlich ppc64el-Ports in Debian und Ubuntu verschoben).

sparc ist normalerweise big endian, scheint aber wieder rückläufig zu sein.

Wenn Sie ein vorhandenes Protokoll implementieren, müssen Sie dessen Spezifikationen natürlich befolgen. Wenn Sie möchten, dass die IETF Ihr neues Protokoll segnet, ist Big Endian wahrscheinlich einfacher, da dies bereits in den vorhandenen Protokollen verwendet wird. IMO für ein neues "Greenfield" -Protokoll ist Little Endian jedoch der richtige Weg.

Sie können entweder von Anfang an Makros einfügen, die auf Little-Endian-Systemen nicht ausgeführt werden, oder Sie können sich erst darum kümmern, wenn Sie auf ein Big-Endian-System portieren müssen.

Peter Green
quelle