[Socket] Verbindung mit mehreren Clients, nur an bestimmte Nachrichten senden

  • Hallo zusammen,


    hab endlich mal wieder Zeit gefunden, ein wenig zu programmieren und hab mich dabei wieder an ein schon länger bestehendes Problem für mich gesetzt:
    Socketverbindungen mit mehreren Clients.

    Ich hab aus einem Tutorial folgenden Code für den Server:

    Wenn ich jetzt meine Armee von Pis mit folgenden Client-Code drauf los lasse:

    funktioniert das problemlos, dass beim Server die Nachrichten und Verbindungen angezeigt werden.


    Mir fehlt allerdings für folgende Anforderung gerade das Transferdenken:

    Ich hab verschiedene Typen von Clients: Steuergerät und Aktoren.

    Ich will vom Steuergerät (Webanwendung) an den Server Befehle schicken, die verarbeitet und aufgrund von Konfiguration an bestimmte Aktoren weiter geschickt werden sollen (also nicht an alle Clients!).

    Ich hab mir das ungefähr so vorgestellt, dass die Clients einzeln nach aufgebauter Verbindung in Listen oder Directorys gespeichert werden, woraus ich dann einzelne auswählen und Nachrichten an diese versenden kann. Und das hab ich noch nicht so ganz rausgefunden, wie das mit dem Socket-OOP funktioniert.

    Kann mir da mal bitte jemand auf die Sprünge helfen? Hab mit Google auch nicht wirklich was hilfreiches gefunden.


    Vielen Dank im raus und

    liebe Grüße


    Fipsi

  • Hallo,


    vor weg, ich kann dir bei deiner eigentlichen Frage leider nicht weiter helfen, aber vor kurzem erst wurde hier auch etwas zu Python und 'socket' besprochen.

    Die Hinweise dort, sind vielleicht auch für dich interessant. Schau dir mal im folgenden Thema die Hinweise von noisefloor und __blackjack__ an.




    Grüße und viel Erfolg mit deinem Projekt.

    Dennis

    “If you’re not paying for the product, then you are the product” Daniel Hövermann

  • Hmm, der Code ist extrem simpel. Fuer einen ersten Versuch mag es reichen, aber es braucht noch einiges bis etwas Sinnvolles draus wird...


    Der Server ist threaded. Wie gut kennst du dich mit Python-Threading aus?


    Vereinfacht gesagt braucht es folgendes:

    - Eine THREADSICHERE Verwaltung der Requesthaendler

    - Eine Unterscheidung der verschiedenen RequestHandler

    - Eine THREADSICHERE Art die Daten an bestimmte Threads zu uebergeben


    So auf den ersten Blick wuerde ich vermuten dass ein StreamRequestHandler die passendere Basisklasse fuer den RequestHandler ist

  • Der Witz bei Client-Server-Systemen ist mitunter, dass die Rollen wechseln.

    Deine "Steuergeräte" sind Clients, die Daten an den Server (zentraler Rechner) schicken.

    Der zentrale Rechner schickt Daten an die Aktoren. In dem Fall ist der zentrale Rechner der Client und der Aktor ist der Server.

    Eine mögliche Lösung ist also, auf den Aktoren den Socket-Server zu installieren, auf den Steuerrechnern den Socket-Client und auf den zentralen Rechner beides.

  • Danke für die Verlinkung. Da ich die Daten dann im JSON-Format übertragen werde, würd ich merken, wenn die Nachrichten nicht vollständig sind. Aber dann bau ich da noch ein Error-Handling mit ein.



    Dass ich da noch mehr brauch, weiß ich leider auch. Ich brech das immer runter, dass ich mich um ein Problem nach dem anderen kümmer, vor allem, wenn ich was für mich neues mach.

    Apropos neu: Threading ist für mich auch neu, ergo: kenn mich nicht wirklich damit aus.

    Aber dann werd ich mir heut Nachmittag mal Stream statt Threading anschauen. Das war wie gesagt aus einem kurzen Tutorial raus, da wollte ich einfach anknüpfen.



    Der Witz bei Client-Server-Systemen ist mitunter, dass die Rollen wechseln.

    Deine "Steuergeräte" sind Clients, die Daten an den Server (zentraler Rechner) schicken.

    Der zentrale Rechner schickt Daten an die Aktoren. In dem Fall ist der zentrale Rechner der Client und der Aktor ist der Server.

    Eine mögliche Lösung ist also, auf den Aktoren den Socket-Server zu installieren, auf den Steuerrechnern den Socket-Client und auf den zentralen Rechner beides.

    Dass ich auf den Aktoren auch Server installier, seh ich jetzt nicht als notwendig und ehrlich gesagt auch nicht als sinnvoll?

    Der Server kann doch auch Nachrichten senden, ohne, dass er auf eine Nachricht vom Client antworten muss? Außerdem muss sich ja immer der Client beim Server einwählen? Da mein Aufbau flexibel ist, d. h. immer nur Server ein fester Knotenpunkt ist und Steuergeräte und Aktoren sowohl in unterschiedlicher Anzahl als auch mit unterschiedlichen IP-Adressen, Hostnamen, etc. vorhanden sind, wüsste der Server dann nie, wohin er überhaupt ne Verbindung aufbauen soll, wenn ich ihm nicht immer alles manuell vorher sag, was aber ein in meinen Augen unnötiger Aufwand wäre.

    Wenn du es besser weißt, lass ich mich gern überzeugen, Sockets sind ebenfalls neu für mich.


    Liebe Grüße


    Fipsi

  • Hallo,


    warum willst du denn Nachrichten hier low-level über einen Socket und nimmst nicht ein fertiges und bewährtes Protokoll wie z.B. http(s)? Dann hast du was, was trilliardenfach funktioniert und über Python über div. Webframeworks sehr gut unterstützt wird und auch JSON kann. So sparst du dir die fehleranfällige Selbstimplentierung von Errorhandling, Nachrichtenvalidierung etc.


    Gruß, noisefloor

  • Wie soll denn der Server Nachrichten an den Client senden, wenn dort kein Socket auf Nachrichten wartet? Das geht doch nur, wenn der Client vorher eine Verbindung aufgebaut hat und diese offen gehalten wird bis zum St.-Nimmerleins-Tag. Der Server schickt dann quasi Antworten, die aber eigentlich keine Antworten sind.

    Vielmehr sollte doch der Zentralrechner in diesem Fall die Verbindung aufbauen, wenn er einen Aktor ansprechen will.

    Deine Aktoren sollten sich beim Zentralrechner anmelden, also ihm eine Nachricht schicken, mit der sie sich identifizieren und ihre IP mitteilen. Dann kann der Zentralrechner sie auf dem dafür eingerichteten Socket jederzeit ansprechen.

  • Okay, vllt. sollte ich ein wenig zu meinem Vorhaben und bisherigen Basteleien ausholen:


    Es geht immer noch um meine Ampeln für den Turnierbetrieb bei Bogensportturnieren.

    Auf diesen läuft ein Timer runter, gibt ebenfalls drei Farben (Rot, Gelb, Grün) und die aktuelle Gruppe ("AB" oder "CD") wird angezeigt.

    Es sind bei jeder Veranstaltung mindestens zwei Ampeln (Raspberry Pi) sowie ein Steuergerät (Webanwendung) vorhanden.

    Um das ganze Synchron zu halten und Zentral steuern zu können, gibt es noch einen zentralen Server (Raspberry Pi).


    Ich hab bisher an dem Ansatz gebastelt, dass der Server eine Webseite zu den aktuellen Befehlen (Zeit, Start, Stop, Pause, anzuzeigender Text, etc.) bereit stellt, die die Ampel alle 100 ms abfragt und entsprechend der Befehle dann die Anzeige generiert und selbst den Timer laufen lässt.

    Also doof gesagt: Der Server gibt den Status des Steuergeräts weiter und die Ampel muss dann selber überlegen, was sie jetzt genau anzeigen soll.


    Mir würde es aber besser gefallen, wenn es nur einen Timer gibt (auf dem Server), der dann jede Sekunde den Befehl selbst an die Ampeln schickt, was diese jetzt genau anzeigen sollen.

    Wieder doof gesagt: Der Server bearbeitet die Befehle selbst und gibt an die Ampeln dann nur noch weiter, was diese direkt ausgeben sollen.


    Wenn es noch andere Möglichkeiten zur Verbindung gibt, die ich wahrscheinlich noch gar nicht kenn, bin ich auch gern für bessere Vorschläge offen.

    Wichtig ist auf jeden Fall, dass ich die einzelnen Teilnehmer unterscheiden und ansprechen kann sowie dass ich möglichst wenig Verzögerung/Latenz hab, damit die Ampeln möglichst synchron laufen (150 ms wären jetzt noch kein Beinbruch).


    Vielen Dank für euren Input und

    liebe Grüße


    Fipsi

  • Wie gut ist dein JavaScript?


    Das toent doch sehr nach WebSockets und einem Server mit node.js

    Das passt soweit, hab aber noch nie irgendwas mit none.js gemacht, kenn das nur vom namen her.


    Hast du den Ansatz, auf den Ampeln none.js einzurichten und über Websockets mit dem Server (auf dem läuft Apache mit PHP und MySQL) verbinden? Haut das dann hin, dass die Ampel die Anzeige über Python (rpi-rgb-led-matrix lib) ansteuert?


    Liebe Grüße


    Fipsi

  • Mein Ansatz war ein Server mit node.js und Anzeigen per Browser mit WebSockets.


    Anscheinend hast du aber schon Code in Python auf den Ampeln und das ueber den Browser auzurufen ist nicht einfach, (wenn es ueberhaut geht)


    Beschreib doch mal genauer wie dein System aussieht.

  • Also folgendermaßen:

    Ein zentraler Server, den ich auch ohne Ampeln hab. Momentan ein Raspberry Pi, wird aber später ein Box PC (auch mit Ubuntu Server dann). Auf dem läuft Apache mit PHP und MySQL, sowie noch ein paar kleine Python-Scripte.


    Der Server hängt an einem LTE-Router und an einem LAN, das mit PowerLAN verteilt wird (an die Ampeln).


    Die Ampeln sind variabel in der Anzahl und welche genau ich verwende (Funktion ist immer gleich, aber z. B. unterschiedliche Größen). Wie schon erwähnt ist die Ampel ein Raspberry Pi und LED Matrixen, die ich über Python mit der lib rpi-rgb-led-matrix (GitHub) ansteuer. Wenn die Ampel eingeschaltet wird, verbindet sie sich mit dem Server und registriert sich, bzw. genauer gesagt, bekommt sie eine ID, die in der Datenbank des Servers gespeichert und zur Identifizierung an der Matrix angezeigt wird. Sobald dann der Nutzer die Ampel einem Bereich und einer Konfiguration zuweist, wird nicht mehr die ID angezeigt, sondern entsprechend dem aktuellen Befehl einen Text, den Turnierstatus, usw. Damit die Ampeln immer das richtige anzeigen und Synchron sind, ist entweder eine bestehende Verbindung zum Server nötig, über die der Server der Ampel immer die aktuellen Befehle und Status schickt oder die Ampel stellt permanent anfragen an den Server, ob sich was geändert hat.


    Auf dem Server läuft dann eine Webseite, über die die Ampeln gesteuert werden. Webseite auch deshalb, dass über Smartphone, Tablet, Laptop, usw. darauf zugegriffen und gesteuert werden kann. Da ich auch auf der Steuerseite immer den aktuellen Status anzeigen will, also auch den Timer, weshalb ich auch hier gern eine permanente Verbindung und den Timer auf dem Server hätte.


    Ich hoffe, das ist soweit verständlich. Wenn noch was unklar ist oder ich Details vergessen oder mir noch keine Gedanken drüber gemacht hab, gerne nachfragen.

    Wenn jemand einen besseren Ansatz als meine bisherigen hat - auch immer gern her damit :D


    Liebe Grüße


    Fipsi

  • Laeuft das schon alles? Und wie viele Ampeln gibt es?

    Die Anzeige der Ampeln und die Konfiguration laufen schon. Die Anzahl der Ampeln ist wie gesagt variabel. Mindestens zwei, könnten aber auch 6 oder 8 sein und dann auch 3 oder 4 Steuergeräte.



    > Sobald dann der Nutzer die Ampel einem Bereich und einer Konfiguration zuweist,

    Wie passiert das?


    Wie kritisch ist das Timing?

    Die Ampel registriert sich nach dem Einschalten wie gesagt am Server. Das wird dann am Steuergerät angezeigt.

    Der Nutzer legt im Vorfeld die Schießfelder (Bereich) an (falls es auch mehrere geben sollte) und für jedes Schießfeld die Konfiguration. Sobald sich eine Ampel registriert hat, kann diese vom Nutzer einem Schießfeld und damit auch einer Konfiguration zugeordnet werden (P. S.: Das passiert alles in der MySQL-DB und mit PHP). Jedes Schießfeld hat auch seinen eigenen Timer, da diese unabhängig voneinander gesteuert werden können.


    Das Timing sollte unter den Ampeln innerhalb eines Schießfelds nicht mehr als 150 ms unterschied haben, da es neben den optischen auch ein akustisches Signal gibt und mehr Verzögerung würde auffallen.


    Liebe Grüße


    Fipsi

    Edited once, last by Fipsi ().

  • > Die Anzeige der Ampeln und die Konfiguration laufen schon.

    Und wie weit ist der Server?


    > Die Anzahl der Ampeln ist wie gesagt variabel. Mindestens zwei, könnten aber auch 6 oder 8 sein

    Ok, nicht kritisch


    > und dann auch 3 oder 4 Steuergeräte.

    Was verstehst du unter "Steuergeraet"?


    > Das passiert alles in der MySQL-DB und mit PHP

    Hmm, das PHP im System macht es nicht einfach...


    -> Du koenntest allerdings von den Ampeln her direkt auf die Datenbank zugreifen und die Daten direkt aus der DB holen

    -> Eine andere Variante waere ein Polling des Servers mit Python Requests. Das wuerde eine API in PHP bedeuten.


    -> MQTT koennte eine Option sein, aber damit kenne ich mich nicht aus. Gibt es MQTT fuer PHP?


    -> Sockets sind auch eine Loesung, aber erfordern ziemlich viel Wissen. Und die Frage ist wie die Daten aus dem PHP auf den Socket-Server kommen.


  • Auf dem Server läuft die ganze Software für den Turnierbetrieb, die ist noch in Arbeit, aber eben die Zuordnung und Konfiguration der Ampeln sowie die Darstellung als JSON zum Abruf funktioniert schon.


    Mit Steuergerät mein ich Handy, Tablet, Laptop, etc. die sich zum Steuern der Ampeln verbinden (Weboberfläche). Da brauch ich je Bereich mindestens eins.


    Das PHP nutze ich halt zur Ein- und Ausgabe der Daten aus der DB und zur Darstellung der Konfig für die Ampeln (Ausgabe als JSON). Darüber will ich aber eben nicht die Steuerung selbst der Ampeln laufen lassen, deswegen sollte ich das Python-Script.


    Das ist ja im Grunde das, was ich in meiner ersten Variante gemacht hab? Die Ampel fragt alle 100 ms die Befehle vom Server ab.


    Keine Ahnung, kenn mich mit MQTT auch nicht aus. Eine schnelle Google-Abfrage ergibt, dass es eine lib auf Github gibt.


    Sockets waren eben mein zweiter Ansatz, an dem ich eben häng. Die Daten hätte ich auf dem Server selbst von Python als Request an den Webserver via HTTP mit JSON abgefragt.


    Liebe Grüße


    Fipsi

  • > Sockets waren eben mein zweiter Ansatz, an dem ich eben häng.

    Na gut, dann eben doch Sockets.


    Also:

    - TCP oder UDP?

    -> wie gut ist das Netzwerk?

    -> UDP ist einfacher, aber Meldungen koennen verloren gehen wenn das Netzwerk nicht top ist

    - Threads oder select (oder AsyncIO)?


    - Stell mal alle deine Meldungen zusammen

    -> was schickt der Server, was der Client

    -> wie reagiert der andere


    - Ueberlege wie du sie einpacken willst

    -> Bei TCP braucht es zumindest eine Laenge oder ein Trennzeichen fuer ein vernuenftiges Protokoll


    - Wie kommt der Serve zu seinen Daten?

    -> pollen der DB

    -> Meldungen von PHP

  • Aauf jeden Fall TCP, da verlorene Pakete auffallen müssen, bzw. nicht vorkommen dürfen.

    Das Netzwerk ist mobil und variabel, deswegen lässt sich dazu schlecht eine Aussage treffen. Später sind aber auch mal 80 - 90 Teilnehmer insgesamt möglich, also da geht auch ein bisschen was ab dann.

    Ich hab ja weder von Threads noch Sockets große Ahnung, aber was ich bisher so gelesen hab, wäre select besser für mich (bzw. einfacher).


    Ampel wird eingeschaltet, versucht Verbindung zum Server aufzubauen.

    - Client: "ich bin eine neue Ampel, gib mir eine ID"

    - Server: "WIllkommen, hier deine ID"

    - Client: *Zeigt ID in der LED Matrix an*

    - Server: "du wurdest einem Bereich zugeordnet, zeig das hier an: [folgt später]"


    Steuergerät verbindet sich:

    - Client: "ich bin ein Steuergerät, zeig mir deine Bereiche"

    - Server: "[Bereiche]"

    - Client: "Ich nehm Bereich [XY]"

    - Server: "aktueller Status *[folgt später]*"


    Timer für Ampel läuft:

    - Server: "Zeige [Timer], [Gruppe], [Farbe], [Text]"

    - Client: "ich zeige *befehl bestätigen*"

    - Sekundliche Wiederholung zum Timer


    Timer für Ampel läuft nicht:

    - Client: *minütliche Wiederholung* "ich lebe noch"

    - Server: "danke, bis in einer Minute"


    Steuergerät - wenn Nutzer was macht:

    - Client: "gebe Befehl [XY] an die Ampeln weiter"

    - Server: "gebe Befehl weiter, aktueller Status der Ampeln ist [Timer], [Gruppe], [Farbe], [Text]"


    Steuergerät - wenn der Timer läuft:

    - Server: "Aktueller Status: [Timer], [Gruppe], [Farbe], [Text]"

    - Client: "danke"

    *sekündliche Wiederholung zum Timer*


    Wenn das ganze mit Sockets geht, hätte ich das gern alles in JSON verpackt, da bin ich dann am flexibelsten, wenn ich mal an den Befehlen bastel und noch das Ding noch mehr kann.


    Der Server bekommt seine Daten über zwei Wege:

    Bereichszuordnungen und Konfiguration holt er sich über Request als JSON via PHP von der DB.

    Befehle bekommt er über Websockets von JavaScript (damit hab ich mich noch nicht befasst, wie das dann genau funktioniert).

    Wenn ein Steuergerät was an der Bereichszuordnung oder Konfig ändert, passiert das über Ajax Via PHP in der DB und gibt er gleichzeitig als Websocket an das Pythonscript weiter, dass sich was geändert hat und er die Daten neu requesten soll.



    Ich hoffe, es ist soweit verständlich, wie ich mir das vorstelle.


    Liebe Grüße


    Fipsi

  • Da ist ein Beispiel mit poll das realisitisch ausssieht: https://pymotw.com/2/select/


    Ob die Sockets writeable sind habe ich allerdings noch nie geprueft. Das sind sie normalerweise.


    Die Queues braucht es nicht, die Daten werden direkt im Prozess verarbeitet. Was du aber checken musst: ist der Block vollstaendig?


    Was du zudem noch brauchst: eine Kennung welcher Socket mit welcher Ampel verbunden ist.