Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

11

20.03.2013, 08:43

Für die Byte Reihenfolge gibt es sowas wie

htonl [] (3) - convert values between host and network byte order
htons [] (3) - convert values between host and network byte order
ntohl [] (3) - convert values between host and network byte order
ntohs [] (3) - convert values between host and network byte order

Das wird aber nicht automatisch von SDL_net gemacht werden können, Da dazu ja ein Wissen um die Interpretation des Inhalt existieren müsste. Das hat SDL_net aber nicht,


Naja, ich dachte eher an endianness, oder verdreh ich da was?

Wenn man nur ein relativ einfaches Event transportieren will, dann reicht vielleicht in weiten Bereichen tatsächlich sizeof() und memcpy().


Ich überlege gerade folgendes: ich verwende ja diese Kopierkonstruktor-ähnlichen Gebilde, die nur einen Pointer statt einer const Reference bekommen. Kann ich die Implementierung des Kopierkonstrukturs (im meinem Fall von Primitivdatentypen) mit sizeof() und memcpy() vermeiden?

Also:
  • Event-ID mit switch überprüfen
  • im entsprechenden Case ein neues, leeres Objekt des Typs erstellen.
  • und mit memcpy(new_event, old_event, sizeof(MyEventType)) dann einfach kopieren?

Oder bekomme ich da an irgendeiner Stelle Probleme (z.B. bei []-Arrays) ?

LG Glocke

simbad

unregistriert

12

20.03.2013, 09:08

Für die Byte Reihenfolge gibt es sowas wie

htonl [] (3) - convert values between host and network byte order
htons [] (3) - convert values between host and network byte order
ntohl [] (3) - convert values between host and network byte order
ntohs [] (3) - convert values between host and network byte order

Das wird aber nicht automatisch von SDL_net gemacht werden können, Da dazu ja ein Wissen um die Interpretation des Inhalt existieren müsste. Das hat SDL_net aber nicht,


Naja, ich dachte eher an endianness, oder verdreh ich da was?

Das versteckt sich dahinter. Da die Network byte order ja für alles Systeme einheitlich sein muss, diese also fest definiert ist. Für Ethernet ist das soweit ich das jetzt im Kopf habe big-endian. Der Trick daran ist, das abhängig von der byte order der Maschine auf der man seinen Software übersetzt die Funktionsaufrufe per Pre-Prozessor entweder auf die Funktion gemapped werden oder einfach leer sind.
Auf einer big-endian Maschine würde also garkein Funktionsaufruf stattfinden.

Zitat


Wenn man nur ein relativ einfaches Event transportieren will, dann reicht vielleicht in weiten Bereichen tatsächlich sizeof() und memcpy().


Ich überlege gerade folgendes: ich verwende ja diese Kopierkonstruktor-ähnlichen Gebilde, die nur einen Pointer statt einer const Reference bekommen. Kann ich die Implementierung des Kopierkonstrukturs (im meinem Fall von Primitivdatentypen) mit sizeof() und memcpy() vermeiden?

Also:
  • Event-ID mit switch überprüfen
  • im entsprechenden Case ein neues, leeres Objekt des Typs erstellen.
  • und mit memcpy(new_event, old_event, sizeof(MyEventType)) dann einfach kopieren?

Oder bekomme ich da an irgendeiner Stelle Probleme (z.B. bei []-Arrays) ?

LG Glocke

Der Compiler erzeugt normalerweise automatisch entsprechende Copy-Constructors. Die sind für die Basistypen alle vorhanden. Für Arrays der Basistypen sollte das auch gelten.
Solange du keine Pointer in deinen Events verwendest und keine virtuellen Methoden, sollte der Compiler dir die Arbeit abnehmen. Das dürfte dann auch die effektivste Kopiermethode sein.
Virtuelle Methoden sind ein Problem, da dazu eine Tabelle (vtable) im Objekt abgelegt wird, die du normalerweise nicht siehst. Ich weiß nicht ob das in C++11 schon anders geregelt worden ist.
Früher hast du bei einem memcpy eines Objekts mit virtuellen Methoden auch gleich noch die vtable mitkopiert. Schickst du das Ding übers Netzwerk werden die darin enthaltenen Pointer auf die andere Seite mit übertragen. Nur das der Client ganz andere Addressen verwendet....das knallt dann beim Aufruf der ersten virtuellen Methode.

Aber wie schon erwähnt. C++11 ist mir bei weitem nicht so sehr bekannt wie es vielleicht müsste.

13

20.03.2013, 09:26

Ich glaube wegen der Byte-Order muss ich mal weiter recherchieren ... ^^

Der Compiler erzeugt normalerweise automatisch entsprechende Copy-Constructors. Die sind für die Basistypen alle vorhanden. Für Arrays der Basistypen sollte das auch gelten.


Tatsache :D Okay, danke für den Hinweis ^^

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

14

20.03.2013, 13:28

Host und Network Byteorder sind interessant für das Ausfüllen der Paketheader, für den Inhalt aber irrelevant. Für die übertragenen Daten zählt nur, dass beide Seiten sie verstehen. Im Falle eines Binärprotokolls, wirst du dich sehr wahrscheinlich auf eine Byteorder festlegen müssen. Welche das ist, ist aber völlig dir überlassen. Da die meisten Maschinen heutzutage wohl Little Endian sind, würde ich mich dafür entscheiden...

simbad

unregistriert

15

20.03.2013, 13:50

Host und Network Byteorder sind interessant für das Ausfüllen der Paketheader, für den Inhalt aber irrelevant. Für die übertragenen Daten zählt nur, dass beide Seiten sie verstehen. Im Falle eines Binärprotokolls, wirst du dich sehr wahrscheinlich auf eine Byteorder festlegen müssen. Welche das ist, ist aber völlig dir überlassen. Da die meisten Maschinen heutzutage wohl Little Endian sind, würde ich mich dafür entscheiden...

Wie du die Byte-Order benennst spielt keine Rolle. Sobald du in der Kommunikation zwischen zwei Maschinen mit unterschiedlicher Byte-Order zutun hast, wird diese für die Anwendung über das Netzwerk-Protokoll hinaus relevant.
Meine Aussage irgendwo am Anfang war, das man, wenn man Maschinen mit unterschieldlicher Byte-Order kommunizieren lassen willst, dies in den zu übertragenden Daten zu berücksichtigen ist. Die von mir genannten Funktionen sind zwar aus den Netzwerk header files, erfüllen aber ihren Zweck, wenn es darum geht Daten in eine einheitliche Byte-Order zu transferieren. Man kann das auch mit eigenen Funktionen/Makros machen und verwendet dann die bswap funktionen.

Es mag auch sein, das die breite Masse mit little-endian maschinen daher kommen wird. Aber in einem Produkt, das ich vor nicht all zu langer Zeit pflegen und erweitern durfte, hat man genau eine solche Problematik unterschiedlicher endiannes zwischen den Kommunikationspartner gemacht. Einfach aus kostengründen. Auf der einen Seite ein PPC System auf der anderen Seite ein ARM. Dort wurde das Problem durch die Übertragung von ASCII Daten umgangen. Auch ein weg.
Serialisierung, auch in binärer Form, kann auch in solchen Fällen so gestaltet werden, das die beiden Kommunikationspartner immer die Daten in der für sie richtigen Byte-Order erhalten.

Um eben nicht zuviel Aufwand zu betreiben hatte ich ja schon angeregt, bei einfachen Strukturen sizeof() und memcpy zu verwenden und sich eine Menge Aufwand zu sparen. Es kommt eben immer drauf an, was man damit erreichen will. Wenn du ganze threads übers Netzwerk verschieben willst, dann wird das wohl nur über Serialisierung funktionieren. Wenn es nur die Uhrzeit ist, kann man das auch mit einem statischen Speicher erledigen.

Immer eine Frage der Anwendung.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

16

20.03.2013, 14:39

Sobald du in der Kommunikation zwischen zwei Maschinen mit unterschiedlicher Byte-Order zutun hast, wird diese für die Anwendung über das Netzwerk-Protokoll hinaus relevant.

Nicht unbedingt, wenn das der Fall ist, dann machst du imo etwas falsch.

Serialisierung, auch in binärer Form, kann auch in solchen Fällen so gestaltet werden, das die beiden Kommunikationspartner immer die Daten in der für sie richtigen Byte-Order erhalten.

Das ist eine Möglichkeit, imo aber keine wirklich gute. Denn was dann als erstes mal gemacht werden muss, ist rauszufinden, in welcher Form die jeweilige Gegenstelle die Daten gerne hätte. Du brauchst also ein eigenes, Byte-Order-unabhängiges, Protokoll nur dafür. Zusätzlich müssen sämtliche Anwendungen, die über dein Protokoll kommunizieren wollen, auf sämtlichen Hardwarearchitekturen beide Varianten implementieren, was unnötigen Code, unnötige Bugs, unnötige Komplexität bedeutet. Abgesehen davon, leidet die Flexibilität des ganzen Systems, denn wenn du z.B. irgendwann mal auf einer exotischen Middle-Endian Maschine laufen willst, kann keine vorhandene Software mit dieser Maschine reden...

Bessere Lösung: Die Byte-Order als Teil des Protokolls spezifizieren. Software weiß, auf was für einer Architektur sie läuft und kann, falls nötig, die Daten vor dem Senden bzw. nach dem Empfangen umwandeln... ;)

Um eben nicht zuviel Aufwand zu betreiben hatte ich ja schon angeregt, bei einfachen Strukturen sizeof() und memcpy zu verwenden und sich eine Menge Aufwand zu sparen. Es kommt eben immer drauf an, was man damit erreichen will. Wenn du ganze threads übers Netzwerk verschieben willst, dann wird das wohl nur über Serialisierung funktionieren. Wenn es nur die Uhrzeit ist, kann man das auch mit einem statischen Speicher erledigen.

Das ist problematisch, weil nicht portabel (Byte-Order, Alignment, Padding)...

simbad

unregistriert

17

20.03.2013, 15:15

Sobald du in der Kommunikation zwischen zwei Maschinen mit unterschiedlicher Byte-Order zutun hast, wird diese für die Anwendung über das Netzwerk-Protokoll hinaus relevant.

Nicht unbedingt, wenn das der Fall ist, dann machst du imo etwas falsch.

Nö. Denn wenn du dir das ISO-Schichten Modell anschaust wirst du feststellen, das die Sockets eben lange vor der Anwedungsschicht liegt. Die Anwendung sollte nach Möglichkeit völlig transparent mit den Daten umgehen. Sprich der ist es einfach mal wurscht, wie die Daten transportiert wurden.
Auf der Socket-Ebene, TCP/IP oder UDP, ist es einfach mal völlig wurscht, wie die Nutzlast codiert ist. Aber genau dazwischen haben ein paar gewitzte Leute zwei Schichten eingelegt, die eben genau dazu da sind um die Anpassung der Daten an die lokalen Gegebenheiten zu erledigen. Auch wenn das im ersten Augenblick fürchterlich kompliziert scheint, ist der Aufwand für eine Anpassung der Byte-Order verhältnismässig simple.

Zitat


Serialisierung, auch in binärer Form, kann auch in solchen Fällen so gestaltet werden, das die beiden Kommunikationspartner immer die Daten in der für sie richtigen Byte-Order erhalten.

Das ist eine Möglichkeit, imo aber keine wirklich gute. Denn was dann als erstes mal gemacht werden muss, ist rauszufinden, in welcher Form die jeweilige Gegenstelle die Daten gerne hätte. Du brauchst also ein eigenes, Byte-Order-unabhängiges, Protokoll nur dafür. Zusätzlich müssen sämtliche Anwendungen, die über dein Protokoll kommunizieren wollen, auf sämtlichen Hardwarearchitekturen beide Varianten implementieren, was unnötigen Code, unnötige Bugs, unnötige Komplexität bedeutet. Abgesehen davon, leidet die Flexibilität des ganzen Systems, denn wenn du z.B. irgendwann mal auf einer exotischen Middle-Endian Maschine laufen willst, kann keine vorhandene Software mit dieser Maschine reden...

Byte-Order Abhängigkeit entsteht immer erst dann wenn die zu übertragende Information mehr als ein Byte umfasst. Folglich ist es ein leichtes ein einzelnes Bit im ersten Byte der Nutzdaten zu definieren, das mir die Endianness der Nutzlast anzeigt. Alles weitere ist dann Aufgabe der (De-)Serialisierung. Da sich alle Datenstrukturen die 2,4,8 Byte Werte, meinetwegen auch 16 Byte, beschränken lassen benötige ich nur für diese 4 Datentypen ein re-ordering. Die Serialisierung wird dann immer bis auf die Basistypen heruntergebrochen und kann dann leicht geprüft und bereinigt werden, so denn darin Bugs enthalten sind.

Zitat


Bessere Lösung: Die Byte-Order als Teil des Protokolls spezifizieren. Software weiß, auf was für einer Architektur sie läuft und kann, falls nötig, die Daten vor dem Senden bzw. nach dem Empfangen umwandeln... ;)

Um eben nicht zuviel Aufwand zu betreiben hatte ich ja schon angeregt, bei einfachen Strukturen sizeof() und memcpy zu verwenden und sich eine Menge Aufwand zu sparen. Es kommt eben immer drauf an, was man damit erreichen will. Wenn du ganze threads übers Netzwerk verschieben willst, dann wird das wohl nur über Serialisierung funktionieren. Wenn es nur die Uhrzeit ist, kann man das auch mit einem statischen Speicher erledigen.

Das ist problematisch, weil nicht portabel (Byte-Order, Alignment, Padding)...

Wenn du die Byte-Order als Protokollbestandteil definierst, dann verlagerst du die Zuständigkeit für die Einhaltung genau auf das schwächste Glied, den Programmierer. Denn der muss entsprechenden Code einbauen. Der Zweck solcher von mir angedachten Lösungen ist aber genau den Programmierer zu entlasten und die Zuständigkeit in den Bereich von Tools und Libraries zu verlagern, denn da gehört das hin.
Durch entsprechende Tools, statische Code-Analyse, Code-Reviews und Test-Programme kann man die Menge der Fehler deutlich gegen null bringen und entbindet den Programmierer von der Beachtung von Dingen, die ihn ja eigentlich nicht interessieren. Er hat zwei Anwendungen und möchte, das diese in der Lage sind Daten auszutauschen, thats all. Wenn das auch mit Brieftauben geht, wird ihn nur die geringe Geschwindigkeit stören.

Wenn ich eben keine Byte-Order beachten will, weil ich definiere, das meine Kommunikation nur zwischen little-endian Maschinen funktioniert, brauche ich diese nicht berücksichtigen. Alignment und Padding sind zwei Dinge, die man geschickt durch ein alignment 1, wie immer das beim übersetzen eingeschaltet wird, entsorgen kann. Für einfache Events ist dann also sizeof() und memcpy() durchaus eine adäquate Lösung. Durch pre-prozessor Anweisungen in den entsprechenden Header-Files kann man dann auch eine Übersetzung im Falle eines anderen Alignments abbrechen. Wer dann die entsprechende Schutzvorrichtung entsorgt ist selber schuld.

Es ist immer eine Frage des Einsatzgebiets ob und wieviel Aufwand bei der Serialisierung der Daten betreibt. Endianes ist in jedem Fall eines der Dinge, die ich nicht berücksichtigen würde wollen.

Schau dir mal OSI Schicht 6 an. Die ist genau für die von mir beschriebene Situation da.
Man muss ja nicht nach OSI aufbauen. Aber die Ideen, die sich darin verbergen sind absolut sinnvoll.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »simbad« (20.03.2013, 15:33)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

18

20.03.2013, 15:44

Byte-Order ist ein wichtiger Bestandteil eines Protokolls. Genau deswegen gibt es Network-Byteorder und viele viele Tabellen für Protokolle und wie deren Pakete für TCP, GSM, UMTS etc aufgebaut sind. Dein Protokoll sitzt eben nicht in einer Schicht unterhalb der Anwendung, weil das Protokoll Teil Deiner Anwendung ist, da ist Dein Denkfehler. Welche Order Dein Protokoll verwendet ist auch total egal, solange beide Seiten es verstehen.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

simbad

unregistriert

19

20.03.2013, 16:09

Selbstverständlich reden wir von Anwendungsprotokollen, also des Datenaustausch auf der Anwendungsebene. Das ist genau das was Events sind. Ansonsten könnte man, was auch ne schicke idee wäre, raw sockets aufmachen und gleich ganz unten Datenaustauschen.
Byte-Order ist in jedem Protokoll, völlig unabhängig ob es auf der physikalischen ebene oder der Anwendungsebene vorkommt, ein wichtiger Bestandteil der Definition. Jede Protokollschicht interpretiert dabei aber jeweils nur die für sie relevanten Daten. Es ist dem MAC layer einfach mal völlig egal, was er in einem Ethernet-Frame transportiert und welche Byte-Order darin verwendet wurde. Das interessiert erst den nächsten Layer.
Aufgrund der Spezifikationen denen man bis zu TCP/IP entsprechen muss, damit man überhaupt Daten austauschen kann, vor allem da Ethernet Big-Endian ist, wird ein entsprechendes Re-Ordering bereits in den entsprechenden Stacks erledigt, aber nur für den Eigenbedarf.
Es ändert aber nix daran, dass Probleme der Endianness, sprich Byte-Order den Entwickler einer Anwendung eigentlich nicht zu interessieren hat. Das muss auf welche Art auch immer, die darunter liegende Schicht erledigen. Da der Austausch von Events in einer Client-Server Anwendung typischerweise unterhalb der Anwendung liegt ist dort die byte-order zu berücksichtigen und gegebenenfalls sind auch dort die Mechanismen für ein Re-Ordering unterzubringen.
Die Funktionen die Glocke geschrieben hat liegen genau an der Stelle wo man, wenn man das denn machen wollte, das Re-Order stattfinden müsste.
Denn SDL_net macht die Verwaltung der Verbindung, beim Server eben mehrere Verbindungen und entspricht somit dem Session-Management, auch wenn das vielleicht schon ein bisschen zu groß gewählter Name ist.
Danach werden die Daten für die Anwedung aufbereitet , Presentation Layer, und dann gehts in die Anwendung.
Mehr habe ich nie behauptet. Und meine Ausführungen sind auch nicht fehlerbehaftet. Es mag sein, das hier unterschiedliche Schichten diskutiert wurden. Das wars dann aber auch.
Das was Glocke implementiert hat ist der Presentation-Layer. Und dagehört sowohl die Serialisierung als auch das Re-Ordering hin.

20

20.03.2013, 16:13

Das was Glocke implementiert hat ist der Presentation-Layer. Und dagehört sowohl die Serialisierung als auch das Re-Ordering hin.


Könnt ihr mir ungefähr sagen, wie ich bezüglich des Re-Orderings vorgehen könnte / sollte? Mir fehlt da derzeit noch der konkrete Ansatz.

Werbeanzeige