[C++] event-based networking framework

Präsentation und Organisation von eigenen Projekten
Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

[C++] event-based networking framework

Beitrag von Glocke » So Mär 03, 2013 1:07 pm

Hi, vor einiger Zeit hatte ich mich mit TCP und UDP unter Verwendung von SDL_net beschäftigt. Basierend auf diesen Experimenten habe ich dann eine Möglichkeit gesucht, Datenpakete bequem über das Netzwerk hin- und herschicken zu können.

Dabei herausgekommen ist mein Projekt "Event-Based Networking Framework" auf GitHub: https://github.com/cgloeckner/networking.

Es verwendet SDL und SDL_net[/b] sowie einige Dinge von C++11. Ich habe - da noch nicht alle Kompiler den Standard unterstützen - versucht C++11-spezifische Dinge zu vermeiden. Das ist mir bisher nur teilweise gelungen: der gesamte Threading-Code stammt aus SDL.

Ein paar kurze Gedanken dazu:
Das Framework erst einmal ein Prototyp und stellt verschiedene Klassen zur Verfügung, um Daten via TCP oder UDP zu versenden bzw. zu empfangen. Dabei werden diese Daten als Event einer Warteschlange angefügt und nacheinander versendet. Ankommende Events werden wieder in einer Warteschlange gesammelt und können dann "rausgepoppt" werden. Da diese von Event abgeleiteten Events intern dennoch mittels Event* behandelt werden, verfügen Events über eine event_id. Sie dient dazu herauszufinden, um welche Art Event es sich hier handelt, so dass das gelesene Event anschließend in seine richtige Form gecastet werden kann.

Auf der GitHub-Seite gibt es zwei Beispiele example1.cpp und example2.cpp, die die Funktionsweise demonstrieren.

Mich würde interessieren was ihr davon haltet? Ich nichts vergleichbares finden, was sich so "einfach" (wie in den Beispielen) verwenden lässt.

LG Glocke

canlot
Beiträge: 393
Registriert: Di Mär 08, 2011 11:01 pm
Wohnort: NRW

Re: [C++] event-based networking framework

Beitrag von canlot » So Mär 03, 2013 1:52 pm

brauche ich SDL immer noch auch wenn ich das kompiliert habe?
Unwissenheit ist ein Segen

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: [C++] event-based networking framework

Beitrag von Glocke » So Mär 03, 2013 2:14 pm

canlot hat geschrieben:brauche ich SDL immer noch auch wenn ich das kompiliert habe?
Wenn du SDL dynamisch linkst (also g++ ... -lSDL -lSDL_net) dann ja. Linkst du dagegen statisch (also g++ -static ... -lSDL -lSDL_net) sollte die Binärdatei unabhängig von DLLs oder SOs und dementsprechend etwas größer sein.

Die Abhängigkeit des Projektes von SDL begründet sich darin, dass ich den Code in meinem eigenen Spiel integrieren und ggf. auch für Clint Bellangers FLARE bereitstellen möchte. In beiden Fällen wir derzeit SDL verwendet.

LG Glocke

Benutzeravatar
darksider3
Beiträge: 347
Registriert: Fr Sep 14, 2012 6:26 pm
Wohnort: /dev/sda1
Kontaktdaten:

Re: [C++] event-based networking framework

Beitrag von darksider3 » So Mär 03, 2013 2:17 pm

Solange die Executable nicht 200mb übersteigt, brauch man sich also keine Sorgen machen^^
effizienz ist, wenn ich ein loch bohre und hinterher mein nachbar auch ein bild aufhängen kann... ^^
Meine Homepage und der Microblog von mir :)
Live Life dont let Life Live You!
Am meisten Aktiv in Webentwicklung und PHP im Wiki

Lirrec
Beiträge: 15
Registriert: Mo Feb 20, 2012 11:55 am

Re: [C++] event-based networking framework

Beitrag von Lirrec » So Mär 03, 2013 2:33 pm

Wenn ich deinen Code soweit richtig gelesen habe überträgst du die Events einfach anhand ihrer Größe:
Was passiert wenn ein Event Pointer enhält oder Datentypen die nicht POD sind, std::string zum Beispiel?
Falls du sowas unterstützten willst ( gerade auch wenn der User eigene Klassen/Datentypen übertragen will), wirst du wohl nicht um irgendeine Art von Serialisierung für verschiedene Klassen herumkommen.
Das ist nochmal ein großes Thema, und kein leichtes dazu.
Bei meinem eigenen Eventsystem drücke ich mich im Moment auch noch davor, gerade weil es nicht besonders einfach ist ein boost::any ( im prinzip ein void* + typid ) zu serialisieren ;)

Mit SDL_net hab ich mich noch nicht beschäftigt, ich nehme an die Endianness dabei automatisch HOST<->NETWORK konvertiert?

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: [C++] event-based networking framework

Beitrag von Glocke » So Mär 03, 2013 3:02 pm

Lirrec hat geschrieben:Wenn ich deinen Code soweit richtig gelesen habe überträgst du die Events einfach anhand ihrer Größe:
Was passiert wenn ein Event Pointer enhält oder Datentypen die nicht POD sind, std::string zum Beispiel?
Using pointers or high-level containers is not allowed to guarantee to serialize events referring to socket communication.
siehe Kommentar über dem Basis-Event. Das ist eine Einschränkung an die Events, um die einfache Serialisierung zu gewährleisten. Bei einem std::string gehe ich von einer maximalen Zeichenlänge aus und kopiere ihn mit memcpy in ein char-Array (siehe example2.cpp).
Lirrec hat geschrieben:gerade auch wenn der User eigene Klassen/Datentypen übertragen will
Da habe ich ein kleines Beispiel: Gehen wir davon aus, auf dem Server und dem Client gibt es eine Spielfigur, die an einer Position steht. Jetzt will der Server dem Client sagen, dass sich diese Spielfigur in eine bestimmte Richtung bewegt. Dann könnte des Event Movement z.B. so aussehen:
  • Objekt-ID (Client und Server müssten dann die gleiche ID für gleiche Objekte verwenden. Anhand der ID würde das Objekt dann am Client identifiziert werden)
  • Bewegungsrichtung (z.B. in Form eines Vektors mit x-, y- und z-Koordinate)
Dann würde auf dem Server auf Basis des Objektes ein Movement-Event erstellt werden, das der Client dann verarbeiten kann.

Bisher bin ich der Einfachheit halber davon ausgegangen, dass sie für alle gängigen Probleme so ein Event mit "Differenz-Daten" erstellen lässt; d.h. (a) ich verwalte meine Objekte die sich ändern dürfen über IDs und (b) ich kann alle Daten (die sich z.B. ändern) durch eine endliche Menge von Primitivdaten ausdrücken.
Davon bin ich erstmal ausgegangen - und dafür scheint es (bisher leider nur in der Theorie) gut zu funktionieren.
Lirrec hat geschrieben:Bei meinem eigenen Eventsystem drücke ich mich im Moment auch noch davor, gerade weil es nicht besonders einfach ist ein boost::any ( im prinzip ein void* + typid ) zu serialisieren ;)
Das wäre eine Sache, die ich für die Zukunft in Betracht ziehen könnte ... irgendwann :D
Lirrec hat geschrieben:Mit SDL_net hab ich mich noch nicht beschäftigt, ich nehme an die Endianness dabei automatisch HOST<->NETWORK konvertiert?
Ich dachte sowas mal gelesen zu haben; ausprobiert habe das aber nicht.

LG Glocke

Lirrec
Beiträge: 15
Registriert: Mo Feb 20, 2012 11:55 am

Re: [C++] event-based networking framework

Beitrag von Lirrec » So Mär 03, 2013 7:28 pm

Glocke hat geschrieben: Da habe ich ein kleines Beispiel: Gehen wir davon aus, auf dem Server und dem Client gibt es eine Spielfigur, die an einer Position steht. Jetzt will der Server dem Client sagen, dass sich diese Spielfigur in eine bestimmte Richtung bewegt. Dann könnte des Event Movement z.B. so aussehen:
  • Objekt-ID (Client und Server müssten dann die gleiche ID für gleiche Objekte verwenden. Anhand der ID würde das Objekt dann am Client identifiziert werden)
  • Bewegungsrichtung (z.B. in Form eines Vektors mit x-, y- und z-Koordinate)
Dann würde auf dem Server auf Basis des Objektes ein Movement-Event erstellt werden, das der Client dann verarbeiten kann.

Bisher bin ich der Einfachheit halber davon ausgegangen, dass sie für alle gängigen Probleme so ein Event mit "Differenz-Daten" erstellen lässt; d.h. (a) ich verwalte meine Objekte die sich ändern dürfen über IDs und (b) ich kann alle Daten (die sich z.B. ändern) durch eine endliche Menge von Primitivdaten ausdrücken.
Davon bin ich erstmal ausgegangen - und dafür scheint es (bisher leider nur in der Theorie) gut zu funktionieren
Im Prinzip wird es immer auf etwas derartiges hinauslaufen, ja.

(a) ist auf jeden Fall eine gute Möglichkeit Objekte über Netzwerk etc, zu identifizieren, eine wirkliche Alternative gibts dazu wohl nicht.

(b) wäre im simpelsten Fall das reine Serialisieren ( das Reduzieren auf PODs funktioniert ja im prinzip mit fast allen Objekten, ausgeschlossen sind halt Dinge wie Handles auf Dateien, Sockets etc ).

Der zweite Teil ist dann natürlich das Optimieren der Netzwerkevents generell.
"Delta" Events sind da auf jeden Fall eine gute Idee, für das Beispiel würde ich vielleicht direkt die neue Position senden, das ist weniger Fehleranfällig, wenn zb ein Movement-event verlorengeht.

( Das Erinnert mich an manches Spiel Age of Empires oder Command & Conquer Generäle, wo man nach mehreren Stunden Spielzeit feststellt, das die Mitspieler nicht mehr in Sync sind und schon man eine Weile gegen Gegner spielt die nicht mehr von Menschen gesteuert werden, sondern von der K.I :D )

Andererseits hat der Empfänger mit einem Vektor natürlich die Möglichkeit zu interpolieren und die weitere Bewegung vorherzusagen, um evtl Ruckler bis zum nächsten Event zu verhindern.

Ich denke das konzipieren von solchen Events ( eigentlich ist es damit ja schon ein Netzwerkprotokoll für das jeweilige Programm) muss man auf jeden Fall gut durchdenken, je nach Anforderungen.

EDIT: Dazu hab ich vor einer Weile mal folgenden Artikel gefunden, dort erklärt der Autor das Netzwerk Design von den ersten beiden Teilen von Age of Empires, fand ich recht interessant:
http://www.gamasutra.com/view/feature/3 ... ork%5F.php

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: [C++] event-based networking framework

Beitrag von Glocke » Mo Mär 11, 2013 8:57 pm

Inzwischen habe ich einige Dinge noch verändert:
  • Events werden jetzt von einer Basis-Klasse (nicht mehr einer Struktur) abgeleitet. Für mich einfach eine Stilsache.
  • Die Link-Klasse (für TCP) besitzt nun zum Senden und Empfangen Template-Methoden. Damit kann ich direkt mit new den nötigen Speicher adressieren - und nicht mehr mit malloc, was ein free erfordert anstelle des delete. Im Zuge dieser Änderung habe ich vorerst die UDP-Unterstützung deaktiviert.
  • Wie die Events gesendet werden, wird nun durch die statischen Methoden void toTcp(TcpLink* link, Event* event) und Event* fromTcp(TcpLink* link) definiert. Diese werden an entsprechender Stelle aufgerufen und führen das eigentliche Senden/Empfangen durch. Sie müssen (wenn man das Framework verwenden will) selber implementiert werden. Die beiden Beispiele verwenden dabei das Schema zunächst die Event-ID (statt bisher der Byte-Anzahl) zu senden und an der Gegenseite entsprechend zu switchen und die Receive-Methode mit dem korrekten Typ zu callen.
  • Im Rahmen meiner Server-Client-Implementierung habe ich es geschafft die Probleme (die ich mit ekelhaft vielen Typparametern "gelöst" hatte) komplett ohne Typparameter zu lösen. Damit ist der Code einfacher zu lesen und nicht mehr so exzessiv Template-vergewaltigend :lol:
Weiterhin freue ich mich auf Feedback jeglicher Art :)

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Re: [C++] event-based networking framework

Beitrag von Glocke » Di Apr 16, 2013 2:15 pm

Hallo,

in den letzten Wochen habe ich das Framework ein ganzes Stück umkonzipiert - auch basierend auf einigen der hier bzw. in anderen Foren angesprochenen Probleme:

Kommunikation auf Basis von JSON
  • Daten werden in JSON-Objekten gesammelt und können serialisiert / deserialisiert werden. Die gängigen Container können in JSON-Objekte abgebildet werden.
  • Einige Speicherlecks beim Werfen von Exceptions wurden behoben. (aber sichtlich noch nicht alle!)
  • Beim Lesen der JSON-Objekte wird nicht mehr mit malloc und void-Pointern gearbeitet. Statt dessen wird auf das Mittel der Deserialisierung zurückgegriffen und ein neues JSON-Objekt erstellt, das mittels delete gelöscht werden kann. Dadurch wurden einige Speicherlecks behoben.
  • Probleme aufgrund verschiedener Größen von Datentypen wurden durch (die von C++11 gegebenen) Datentypen mit fester Länge (z.B. std::uint32_t - siehe http://en.cppreference.com/w/cpp/types/integer) eingedämmt. Soweit ich es überblicke, habe ich bereits das ganze Framework auf diese Typen umgebaut - oder fast das ganze ^^
  • Weitere plattformspezifische Probleme wurden durch die Serialisierung / Deserialisierung verringert. Mir ist derzeit erstmal kein weiteres Problem bewusst - ich bin aber offen für neue Probleme :D
  • Komprimierung ist bisher noch nicht eingebaut. Da aktuell aber serialisierte JSON-Objekte als Zeichenkette versandt werden, erscheint ein nachträgliches Implementieren einer Komprimierung bzw. Dekomprimierung recht einfach.
  • Bislang wird ausschließlich die Client-Server-Architektur unterstützt. Wenn jemand Interesse an einer Peer-to-Peer-Implementierung hat, kann ich da ggf. etwas bauen. Hat jemand eine Peer-to-Peer-Implementierung auf Basis des Frameworks, bin ich gerne bereit mir die anzuschauen und ggf. hinzuzufügen.
  • Es gibt aktuell genau eine Beispiel-Anwendung: Ein Konsolen-Chatroom. Siehe https://github.com/cgloeckner/networking
Es würde mich freuen, wenn der ein oder andere das Framework mal etwas auf den Zahl fühlen möchte. Für Fehler/Probleme/Anregungen/Kritik und Lob stehe ich gern zur Verfügung :)

Falls ich Eure Fragen noch nicht (vollständig) beantwortet habe, dann fragt bitte ruhig nochmal :)

LG Glocke

PS: Kann jemand das "event-based" aus dem Titel nehmen? :D

Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

Komplettes Redesign

Beitrag von Glocke » Di Jan 28, 2014 10:49 am

Hi, nach einiger Pause habe ich das Projekt nochmal aufgegriffen und etwas umstrukturiert, um es flexibler zu machen. Das Ergebnis ist eine Server-Client-Implementierung, die unabhängig von der verwendeten Socket-Library ist.

GitHub-Link: http://github.com/cgloeckner/netLib

Nach wie vor basiert die Implementierung auf C++11, ist jedoch nicht mehr von SDL abhängig (die Beispiele allerdings schon). Für den Datenaustausch zwischen den Peers gibt es eine BinaryStream-Klasse. Sie kann verschiedene Daten enthalten und kümmert sich um Endianess (noch nicht 100%ig, siehe README.md).

Im Repository befindet sich ebenfalls eine Beispiel-Implementierung auf Basis von SDL_net und TCPsocket. Prinzipiell können analoge Implementierungen auch für UDPsocket oder sogar andere Socket-Frameworks erstellt werden, ohne den eigentlichen Code der Server- und Client-Implementierung zu beeinflussen. Der Code besteht im Kern aus zwei Abschnitten:
  1. Abstraktion der Sockets und Implementierung eines abstrakten Servers und Clients.
  2. Abstraktion von Server und Client und Implementierung einer Event-Manager-Struktur.
Zu beiden Varianten gibt es entsprechend einen Beispiel-Code:

Direkte Nutzung der Server- und Client-Klasse: https://github.com/cgloeckner/netLib/bl ... e/main.cpp
Nutzung der Event-Struktur zum Aufrufen von Methoden bei bestimmten Events: https://github.com/cgloeckner/netLib/bl ... /main2.cpp

Ich würde mich über euer Feedback freuen! Das ganze ist noch relativ "frisch" und beeinhaltet sicher einige Designfehler... ^^

LG Glocke

Antworten