Seite 1 von 1

[OOP] Threading & Networking

Verfasst: Mi Dez 12, 2012 9:06 am
von Glocke
Hallo,

ich habe in C++ mit SDL (im Rahmen einer Isometrischen RPG Engine) etwas experimentiert. Allerdings frage ich mich, wie man am OOP-artigsten Netzwerkkommunikation betreibt. Ausgehend vom Server-Client-Konzept verwende ich derzeit folgende Herangehensweise:

Auf dem Server gibt es einen ListenerThread, der ankommende Clients annimmt und für diesen einen HandlerThread startet. Für jeden angenommenen Client gibt es einen HandlerThread, der der Kommunikation zwischen Client und Server dient.
Die Clients senden z.B. die gewünschten Positionsänderungen (Objekt-ID, Richtungs-ID) an den Server. Dort wird dann die Bewegung geprüft (Kollision etc.) und ausgeführt. Anschließend wird die neue Position (Objekt-ID, Position XY) an alle Clients gesendet. Diese (und somit auch der ursprüngliche Absender) aktualisieren die Position des jeweiligen Objekts (in ihrer View).

Dabei erscheinen mir ein paar Dinge nicht so OOP-artig (sehen für mich auch irgendwie unschön aus):

(1) Threading: Beim starten der Threads kann ich nur einen Parameter angeben. Wenn ich als Thread-Funktion die Methode der Server-Instanz (ausgehend davon, dass es eine Klasse Server gibt, die das handlet) verwende, geht der eine Parameter ja schon für das Mitgeben der Server-Instanz drauf. Für weitere Parameter ist dann kein Platz. Derzeit fahre ich eine Alternative: Ein Struct, das die Server-Instanz und weiteren Parameter enthält. So richtig schön ist das aber auch nicht. Zumal ich für unterschiedliche Parameter dann wieder unterschiedliche Struct's brauche :-S

(2) Kommunikation: Für die Kommunikation sende ich über den Socket erst eine Aktions-ID (die Server und Client kennen) und anschließend alle Daten (alle int's, string's usw. einzeln). Der Server "switcht" die Aktions-ID, führt die entsprechende Methode aus und gibt ihr als Parameter den Socket mit). Dann liest die Methode die weiteren Daten vom Socket, verarbeitet sie und startet anschließend die Benachrichtigung aller Clients.

Nun:
  • Bei (2) ist das wahrscheinlich die gängigste Variante. Oder?
  • Bei (1) habe ich Bauchschmerzen: Mit der Zeit entsteht ein schwer überschaubares Gewirr an Struct's, kryptischen Thread-Aufrufen und schlecht überschaubarer Verfolgung der Arbeitsweise. Allerdings finde ich keinen "schöneren" Ansatz für den Umgang mit Threads (btw basierend auf den SDL-Threads) aus objektorientierter Sicht :-S
Ich hoffe ihr könnt mir helfen xD

LG Glocke

Re: [OOP] Threading & Networking

Verfasst: Mi Dez 12, 2012 11:16 am
von fat-lobyte
Hallo,
Glocke hat geschrieben:ich habe in C++ mit SDL (im Rahmen einer Isometrischen RPG Engine) etwas experimentiert. Allerdings frage ich mich, wie man am OOP-artigsten Netzwerkkommunikation betreibt.
Also ganz zu allererst würde ich gerne erwähnen, dass es ein ziemlich schlechts Ziel ist, etwas "möglichst OOP-artig" zu Programmieren :-)

Du solltest dir das Ziel setzen ein möglichst gut Designtes Programm zu haben. Wenn da OOP hilft (ich glaube das tut es), dann verwende es. Aber man sollte nicht übertreiben und nicht alles auf OOP umstellen, nur weil es ein Modewort ist.
Auf dem Server gibt es einen ListenerThread, der ankommende Clients annimmt und für diesen einen HandlerThread startet. Für jeden angenommenen Client gibt es einen HandlerThread, der der Kommunikation zwischen Client und Server dient.
Die Clients senden z.B. die gewünschten Positionsänderungen (Objekt-ID, Richtungs-ID) an den Server. Dort wird dann die Bewegung geprüft (Kollision etc.) und ausgeführt. Anschließend wird die neue Position (Objekt-ID, Position XY) an alle Clients gesendet. Diese (und somit auch der ursprüngliche Absender) aktualisieren die Position des jeweiligen Objekts (in ihrer View).
Klingt recht straightforward.
Dabei erscheinen mir ein paar Dinge nicht so OOP-artig (sehen für mich auch irgendwie unschön aus):

(1) Threading: Beim starten der Threads kann ich nur einen Parameter angeben. Wenn ich als Thread-Funktion die Methode der Server-Instanz (ausgehend davon, dass es eine Klasse Server gibt, die das handlet) verwende, geht der eine Parameter ja schon für das Mitgeben der Server-Instanz drauf. Für weitere Parameter ist dann kein Platz. Derzeit fahre ich eine Alternative: Ein Struct, das die Server-Instanz und weiteren Parameter enthält.
Ist das nicht normal? Ich denke das müsste von der Threading-API beabsichtigt sein.
So richtig schön ist das aber auch nicht.
Wieso? Da kommts wirklich nur mehr auf die verpackung an. Egal wie du die parameter übergibst (als einzelne Parameter oder als struct), im Endeffekt kommen sie über den Stack. (Außer du übergibst Zeiger, dann kommen sie halt als Zeiger)
Zumal ich für unterschiedliche Parameter dann wieder unterschiedliche Struct's brauche :-S
Sonst würdest du halt für unterschiedliche Parameter unterschiedliche Argumente brauchen. Ich verstehe da das Problem nicht ganz?
(2) Kommunikation: Für die Kommunikation sende ich über den Socket erst eine Aktions-ID (die Server und Client kennen) und anschließend alle Daten (alle int's, string's usw. einzeln). Der Server "switcht" die Aktions-ID, führt die entsprechende Methode aus und gibt ihr als Parameter den Socket mit). Dann liest die Methode die weiteren Daten vom Socket, verarbeitet sie und startet anschließend die Benachrichtigung aller Clients.
Bei (2) ist das wahrscheinlich die gängigste Variante. Oder?
Im endeffekt - Ja.
Überleg dir das Grundproblem. Du bekommst eine lineare Sequenz von Bytes und willst daraus strukturierte Daten aufbauen. Irgendwann muss mal irgendwer multiplexing betreiben (der Sender) und irgendwer muss mal irgendwann demultiplexing betreiben (der Empfänger). Um das Eine oder Andere switch wird man da nicht herumkommen.

Was dann weiter im Programm passiert ist aber noch nicht fix. Du kannst zum Beispiel das Signal/Slot pattern verwenden um die Daten an die richtigen Stellen weiterzuleiten, das mache ich übrigens auch.

Bei (1) habe ich Bauchschmerzen: Mit der Zeit entsteht ein schwer überschaubares Gewirr an Struct's, kryptischen Thread-Aufrufen
#
Dann mach sie weniger Kryptisch, und entwirre deine Structs. C++ gibt dir Vererbung, Virtuelle Methoden, Templates, ... alles was dein Herz begehrt. Überleg dir ne coole Struktur! (kleiner Tip: Vererbung alleine ist *nicht* die Lösung. Ich persönlich bin kein großer Fan von riesigen Klassenhierarchien)
schlecht überschaubarer Verfolgung der Arbeitsweise
Willkommen in der Asynchronen Programmierung. :ugeek: Deal with it.


Und Bitte, Bitte, BITTE tu dir selbst einen Gefallen und versteife dich NICHT auf OOP. Es gibt auch andere Techniken (zum Beispiel generische Programmierung) die zum Ziel führen. Du schießt dir selbst ins Bein, wenn du dir alle "paradigmen" (wohl eher Dogmen) der OOP aufzwingst.

Re: [OOP] Threading & Networking

Verfasst: Mi Dez 12, 2012 11:32 am
von Glocke
fat-lobyte hat geschrieben:Du solltest dir das Ziel setzen ein möglichst gut Designtes Programm zu haben. Wenn da OOP hilft (ich glaube das tut es), dann verwende es. Aber man sollte nicht übertreiben und nicht alles auf OOP umstellen, nur weil es ein Modewort ist.
Das Ziel habe ich mir gestellt, weil ich OOP-Programm vom Design her gut finde. :)
fat-lobyte hat geschrieben:Und Bitte, Bitte, BITTE tu dir selbst einen Gefallen und versteife dich NICHT auf OOP. Es gibt auch andere Techniken (zum Beispiel generische Programmierung) die zum Ziel führen. Du schießt dir selbst ins Bein, wenn du dir alle "paradigmen" (wohl eher Dogmen) der OOP aufzwingst.
Ich weiß: das Feld ist weit ^^ Einige Teile der Engine sind bereits generisch.
Die Engine an sich habe ich als Finite State Machine konzipiert und über eine "has"-Beziehung (Finite State Machine as a State) realisiert. z.B. gibt es für das Hauptmenü einen "MainMenu"-State. Analog verwende ich einen "GameServer"- und "GameClient"-State, die via Socket kommunizieren.
fat-lobyte hat geschrieben:Wieso? Da kommts wirklich nur mehr auf die verpackung an. Egal wie du die parameter übergibst (als einzelne Parameter oder als struct), im Endeffekt kommen sie über den Stack. (Außer du übergibst Zeiger, dann kommen sie halt als Zeiger)

Sonst würdest du halt für unterschiedliche Parameter unterschiedliche Argumente brauchen. Ich verstehe da das Problem nicht ganz?
Ich mache mir das Leben zu schwer merke ich gerade. Mir kam folgende Idee (hoffe der Pseudocode ist verständlich):

Code: Alles auswählen

Klasse ServerController:

    Methode Listener():
        Solange Server läuft:
            Nimm neuen Client an
            Erstelle Struct mit: ClientSocket, ServerModel und ServerController
            Starte Handler-Funktion als Thread mit Parameter (erstelltes Struct)

    Methode Handler(Parameter):
        Nimm ClientSocket, ServerModel und ServerController aus Parameter
        SpielerID = ServerModel.login(ClientSocket)
        Endlosschleife:
            Warte auf Aktion vom Client
            Falls Aktion:
                Logout:
                    ServerModel.logout(ID)
                StartBewegung:
                    Lies ID von Socket
                    Lies Richtung von Socket
                    Wenn (ID == SpielerID):
                        ServerModel.startBewegung(ID, Richtung)
                        ServerController.updateBewegung(ID, ServerModel.Position(ID))
                StopBewegung:
                    Lies ID von Socket
                    Wenn (ID == SpielerID):
                        ServerModel.stopBewegung(ID)

    Methode updateBewegung(ID, Position):
        Für jeden Client auf Server:
            Sende ID und Position an Client

Und zwar gehe ich einfach davon aus, dass ein Client nicht parallel zwei Dinge machen kann/muss - und ich daher pro Client nur genau einen Thread brauche. Dann "blockt" ein eventuell langsamerer Programmteil eine andere Aktion halt... :)
fat-lobyte hat geschrieben:Was dann weiter im Programm passiert ist aber noch nicht fix. Du kannst zum Beispiel das Signal/Slot pattern verwenden um die Daten an die richtigen Stellen weiterzuleiten, das mache ich übrigens auch.
Darin werde ich mich mal vertiefen.
fat-lobyte hat geschrieben:Willkommen in der Asynchronen Programmierung. :ugeek: Deal with it.
Hurraaaay :?

In nem anderen Forum wurde mir empfohlen z.B. CORBA oder HTTP + SOAP zu verwenden.... :?:

LG Glocke :lol: