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

1

05.03.2013, 09:53

[C++] Networking-Framework für SDL-Projekt

Hallo, ich habe es absichtlich vom anderen Event-basierte-Netzwerkkommunikation-Thread getrennt: Dort ging es um prinzipielle Herangehensweisen. Hier möchte ich gerne Euer Feedback (sowohl positiv als auch negativ) zu meinem kleinen Projekt bekommen :)

Seit einer Weile arbeite ich mit C++ und SDL. Bisher hatte ich nichts vergleichbares gefunden, deswegen habe ich für mein Problem selber einen Lösungsansatz entwickelt: https://github.com/cgloeckner/networking

Dabei handelt es sich um ein Framework, welches in C++ geschrieben ist und SDL sowie SDL_net verwendet. Es bietet die Möglichkeit "Events" über das Netzwerk zu versenden und an der Gegenseite entsprechend zu behandeln. Die Events werden in threadsicheren Warteschlangen angesammelt und sequenziell gesendet, beziehungsweise gelesen und wieder in einer Warteschlange aneinandergereiht. Dort verbleiben sie bis zu ihrer Bearbeitung. Dabei handelt es sich bei den "Events" um Ableitungen einer Struktur Event. Sie enthält eine Event-ID, anhand der die Events auf der Gegenseite bezüglich ihres ursprünglichen Typs erkannt und gecastet werden können. Dabei verwenden beide (oder besser alle) Seiten die gleiche Menge an Events.

Einschränkungen

Meine Events dürfen nur Primitivdaten enthalten, keine Zeiger oder std-Container. Grund dafür ist die fehlende Serialisierung vor dem Versenden über das Netzwerk. Ohne Zeiger und Container können die Events direkt geschrieben werden, ohne übrige Daten vorher serialisieren zu müssen.

Lizenz und Aufbau

Das Projekt habe ich unter der Lizenz CC BY-NC 3.0 veröffentlicht. Im verlinkten GitHub-Repository findet ihr den Quellcode des Frameworks (src-Verzeichnis) sowie zwei Beispiel-Programme example1.cpp (TCP- bzw. UDP-basiertes Beispiel, welches direkt die NetworkingQueue verwendet) und example2.cpp (TCP-basiertes Beispiel einer kleinen Server-Client-Struktur mit Worker-Threads, die jeweils einen Client bedienen).

Einige weitere Gedanken

Das Projekt verwendet bereits Features von C++11. Eigentlich will ich es von C++11 loslösen, weil ja noch nicht alle Compiler C++11 in gleichem Umfang unterstützen. Daher habe ich beispielsweise alle Threading-bezogenen Dinge auf der Threading-Funktionalität von SDL aufgebaut.

Ich bin gespannt auf euer Feedback.

LG Glocke

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Glocke« (16.04.2013, 15:44) aus folgendem Grund: Titel angepasst


simbad

unregistriert

2

17.03.2013, 10:36

Es ist ungünstig, das du dich auf die SDL zurückziehst und keine Möglichkeit eröffnest eine andere Library für das Networking zu verwenden. Dadurch ist man gezwungen die SDL zu benutzen. Im Zuge der weiteren Entwicklung kann es passieren, das die SDL nicht mehr zur Verfügung steht, sie z.B. nicht mehr für eine bestimmte OS-Version gepflegt wird oder man durch neue Compiler Schwierigkeiten bekommt sie zu übersetzen.
Dann sind deine Funktionen auch nutzlos.
Durch den verzicht auf die Serialisierung entgehen dir viele Vorteile die man damit hätte. Deine Events sind darauf angewiesen, das die verwendeten Compiler das gleiche Verständnis über die Anordnung der Attribute in den Klassen haben, dazu zählt auch das Allignment. Du wirst also Schwierigkeiten haben einen Client, der mit Visualstudio erzeugt wurde, mit einem Server unter Linux zu koppeln. Dabei rede ich noch nicht von dem Problem, das du unter Windows vielleicht einen 32-Bit Client hast und unter Linux einen 64-Bit Server, die für einen long unterschiedlich viele Bytes brauchen. Du kannst auch keinen Rechner ankoppeln, der im Big-Endian Format seine Daten ablegt. Das gibt dann auf dem Netzwerk reichliches Chaos.

Serialisierung ist dabei das Mittel, mit dem die Daten in einer Form transportiert werden, die es dem Empfänger ermöglichen, unterschiedlichste Maschinen zu koppeln.

3

17.03.2013, 17:23

Dabei rede ich noch nicht von dem Problem, das du unter Windows vielleicht einen 32-Bit Client hast und unter Linux einen 64-Bit Server, die für einen long unterschiedlich viele Bytes brauchen. Du kannst auch keinen Rechner ankoppeln, der im Big-Endian Format seine Daten ablegt.


Oh ... das habe ich total vernachlässigt!

Serialisierung ist dabei das Mittel, mit dem die Daten in einer Form transportiert werden, die es dem Empfänger ermöglichen, unterschiedlichste Maschinen zu koppeln.


Hmm ich sollte mir vllt. mal überlege, wie ich Serialisierung mit meinem Framework kombinieren kann.. :)

LG Glocke

simbad

unregistriert

4

18.03.2013, 12:52

Du brauchst eigentlich drei Funktionen.
GetSizeof()
Pack()
Unpack()

Mit GetSizeof() bekommst du die Größe des Speicherbereichs, der beim Einpacken belegt werden wird.
Pack() packt das ding ein.
Unpack() aus.

Thats all.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

5

18.03.2013, 13:02

Kannst auch mal einen Blick auf boost Serialization oder die google protobuf Library werfen, die soll dafür relativ beliebt sein...

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »dot« (18.03.2013, 13:14)


6

19.03.2013, 11:49

Hi,

wegen der Sache mit Kommunikation-zwischen-32-und-64-Bit-System: wenn ich in den Events diese Typen http://en.cppreference.com/w/cpp/types/integer verwende, sollte ich das Problem umgehen können, richtig?

Wegen der Byte-Reihenfolge bin ich gerade am recherchieren, ob SDL_net das für mich übernimmt oder nicht.

LG Glocke

simbad

unregistriert

7

19.03.2013, 17:44

Ich bin noch von der alten Garde. Da waren die Datentypen uint16_t etc. nicht Bestandteil der Sprache. Es gab aber normalerweise das Header-File stdint.h wo die alle passend für den Compiler defniiert waren/sind.
Mit diesen Datentypen erreicht man eine entsprechende Sicherheit bezüglich der Anzahl der Bytes.

SDL_net wird dir mit an Sicherheit grenzender Wahrscheinlichkeit da nicht direkt helfen können. Da SDL_net nur das Connection-Management macht, aber sich nicht für den Inhalt der über das Netz transportiert wird interessiert.

Die Frage ist eben immer, was man mit der Serialisierung der Daten erreichen will. Wenn man nur ein relativ einfaches Event transportieren will, dann reicht vielleicht in weiten Bereichen tatsächlich sizeof() und memcpy().

Man kann aber auch ganze Objekte transportieren. Ich habe mal in einem Projekt Nachrichten über das Netz bewegt, die nicht nur aus den Kardinaltypen sondern auch aus den Containern der STL und ganzen Objekten bestehen konnten. Dabei haben wir auch verschachtelte maps mit pointern auf die Objekte serialisiert übertragen und auf der anderen Seite wieder zusammengesetzt. Wir haben dabei aber keine Endianess berücksichtigt. Das war einfach nicht nötig.

Es ist also nicht so leicht ein für alle Fälle funktionierendes Konzept zu entwickeln.

simbad

unregistriert

8

19.03.2013, 18:11

Ich kann das vielleicht auch ein wenig erklären, wie wir das damals gemacht haben.

Wenn daran allgemeines Interesse besteht.

9

19.03.2013, 20:44

SDL_net wird dir mit an Sicherheit grenzender Wahrscheinlichkeit da nicht direkt helfen können. Da SDL_net nur das Connection-Management macht, aber sich nicht für den Inhalt der über das Netz transportiert wird interessiert.

Naja ich meinte auch eher, ob SDL_net mir bei der Reihenfolge der Bytes hilft - bisher habe ich keine eindeutige Quelle gefunden die das belegt oder widerlegt.

Die Frage ist eben immer, was man mit der Serialisierung der Daten erreichen will. Wenn man nur ein relativ einfaches Event transportieren will, dann reicht vielleicht in weiten Bereichen tatsächlich sizeof() und memcpy().

Mir persönlich reichen "einfache" Events, d.h. primitiv-Zahlen und ein paar char-Arrays als Zeichenketten. Von daher halte ich mich noch von der Serialisierung fern. In meinem RPG will ich dann meine komplexeren Daten in die Events packen und brauche selber keine Serialisierung im üblichen Sinne. D.h. wenn meine Figur eine Liste von Wegpunkten bekommen hat (und ich eine Vector-Klasse für Wegpunkte habe), reicht es mir dass das Event

C-/C++-Quelltext

1
2
3
4
5
6
struct AddWaypoints: Event {
    uint16_t object_id;
    Vector waypoints[MAX_WAYPOINTS];

    // und dazu mein Konstruktor-Zeug
};

hat - dabei stört mich das MAX_WAYPOINTS dann auch nicht wirklich.

Es ist also nicht so leicht ein für alle Fälle funktionierendes Konzept zu entwickeln.


:) Deswegen versuche ich nicht eine eierlegende Wollmilchsau mit Serialisierungs-Funktionalität zu implementieren :D Ein ein-für-alles sprengt den Rahmen, in dem ich das Framework implementiere: ich arbeite in meiner Freizeit gänzlich alleine daran um es für mein RPG (an dem ich parallel arbeite) zu verwenden - und um es Leuten zur Verfügung zu stellen, die ähnliche Ziele verfolgen.

Btw habe ich eben meinen Code noch etwas aufgeräumt (Multithreading Server, doxygen-artige Doku in den Headerfiles usw.) :P

LG Glocke

simbad

unregistriert

10

20.03.2013, 07:44

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,

Werbeanzeige