Marvin Esse: Programmiertechnik: Websockets über mehrere Seiten?

Hallo,

ich habe folgende Aufgabenstellung: Es soll eine bidirektionale Kommunikation stattfinden können, die zwischen 2 (oder mehr) Clients stattfindet (über einen Server). Auf den Clients soll dafür nur ein aktueller Browser notwendig sein.

Ich hatte daher an Websockets gedacht und auch als erstes Proof-of-Concept einen kleinen funktionierenenden Chat gebaut. Angemeldete Clients lassen sich dabei gezielt ansprechen.

Soweit so gut, mein Problem dabei ist jetzt, dass sich die Kommunikation nicht auf eine Seite beschränkt und im Moment verstehe ich das so, dass bei jedem Laden einer neuen Seite die Verbindung neu aufgebaut wird. Oder könnte ich auf der Server-Seite prüfen, ob ich den Client bereits in der Verbindungsliste (Liste der Sockets) habe und die Verbindung wiederbenutzen, ohne dass bei jedem Seitenwechsel eine neue Verbindung geöffnet werden muss? Dummerweise lassen sich Socket Resourcen wohl auch nicht in einer MySQL-Tabelle speichern.

Ich habe etwas über sogenannte SharedWorker gefunden, aber die scheinen auch mit dem aktuellen Firefox noch nicht fehlerfrei zu laufen? Jedenfalls haben die bisher gefundenen Online-Beispiele nicht funktioniert.

Auch wenn ich mit einem Frameset arbeite, ist mir nicht klar, wie dann eine Kommunikation mit dynamischen Ändern von Inhalten funktionieren soll.

Momentan fällt mir nur ein, dass ich alles auf eine einzige Seite umstelle und die eigentlichen Seiteninhalte z.B. per Ajax lade. Damit bliebe wohl dann immer dieselbe Seite geöffnet.

Bin für Anregungen und Ideen dankbar.

LG Marvin

  1. Hello Marvin,

    Soweit so gut, mein Problem dabei ist jetzt, dass sich die Kommunikation nicht auf eine Seite beschränkt und im Moment verstehe ich das so, dass bei jedem Laden einer neuen Seite die Verbindung neu aufgebaut wird. Oder könnte ich auf der Server-Seite prüfen, ob ich den Client bereits in der Verbindungsliste (Liste der Sockets) habe und die Verbindung wiederbenutzen, ohne dass bei jedem Seitenwechsel eine neue Verbindung geöffnet werden muss?

    Die Aussxage ist mir so zu vieldeutig. Kannst Du bitte genauer beschreiben, wie Du das meinst?

    Definiere "Seite".
    Definiere "Laden einer neuen Seite"

    Hast Du den XH-Requester in jedes Dokument deines Webauftrittes eingebaut?

    Liebe Grüße
    Tom S.

    --
    Es gibt nichts Gutes, außer man tut es
    Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
    1. Hallo Tom,

      Die Aussxage ist mir so zu vieldeutig. Kannst Du bitte genauer beschreiben, wie Du das meinst?

      Definiere "Seite".
      Definiere "Laden einer neuen Seite"

      Hmm, wie soll ich das anders beschreiben? Mit "Seite" ist eine Datei gemeint, die vom Browser aufgerufen wird. Z.B. index.html Mit "Laden einer neuen Seite" ist gemeint, dass man auf einen Link klickt und eine andere Datei aufgerufen wird. z.B. kontakt.html

      Hast Du den XH-Requester in jedes Dokument deines Webauftrittes eingebaut?

      Was meinst Du mit XH-Requester? Ajax?

      Im Grunde werden auf jeder Seite zahlreiche Informationen angezeigt. Quasi jede Kategorie ist eine eigene Seite/Datei. Über die Websocket-Verbindung sollen jetzt Informationen ausgetauscht werden, die dann natürlich auch sofort auf der Seite angezeigt werden sollen. Nimm als Beispiel eine Seite auf der eine Mess-Grafik angezeigt wird. Client 1, Client 2 und Client 3 schicken ihre Werte automatisch, Client 4 zeigt automatisch eine aktualisierte Grafik.

      LG Marvin

      1. Hello,

        Die Aussxage ist mir so zu vieldeutig. Kannst Du bitte genauer beschreiben, wie Du das meinst?

        Definiere "Seite".
        Definiere "Laden einer neuen Seite"

        Hmm, wie soll ich das anders beschreiben? Mit "Seite" ist eine Datei gemeint, die vom Browser aufgerufen wird. Z.B. index.html Mit "Laden einer neuen Seite" ist gemeint, dass man auf einen Link klickt und eine andere Datei aufgerufen wird. z.B. kontakt.html

        Ok, Du meinst also tatsächlich "Seite" im Sinne von Dokument innerhalb deines eigenen Webauftrittes und nicht "Site" im Sinne von "anderer Webauftritt, andere Domain".

        Hast Du den XH-Requester in jedes Dokument deines Webauftrittes eingebaut?

        Was meinst Du mit XH-Requester? Ajax?

        Wie upgradest Du denn den HTTP/s-Request auf WSP (Websocket Protokoll)? Das mache ich mit einem "AJAX"-Modul als Requester. Das muss in jedem aufgerufenen Dokument vorhanden sein, das Kontakt zum Websocket-Responder (Websocket-Hub, Websocket-Server) aufnehmen will. Anders wäre es, wenn dasjenige Dokument geöffnet bliebe, das die Websocket-Verbindung hält. Dann könnte man, Zustimmung durch die Same Origin Policy vorausgesetzt, per JavaScript den Content des Dialoges von einem Domument an ein anderes weitergeben und bräuchte in den weiteren keinen Requester mehr.

        Was mich aber viel mehr interessiert: welches Websocket-Paket benutzt Du? Wie realisierst Du darin, dass der WS-Server nur von Dokumenten deiner Domain benutzt werden kann?

        Liebe Grüße
        Tom S.

        --
        Es gibt nichts Gutes, außer man tut es
        Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
        1. Wie upgradest Du denn den HTTP/s-Request auf WSP (Websocket Protokoll)? Das mache ich mit einem "AJAX"-Modul als Requester. Das muss in jedem aufgerufenen Dokument vorhanden sein, das Kontakt zum Websocket-Responder (Websocket-Hub, Websocket-Server) aufnehmen will. Anders wäre es, wenn dasjenige Dokument geöffnet bliebe, das die Websocket-Verbindung hält. Dann könnte man, Zustimmung durch die Same Origin Policy vorausgesetzt, per JavaScript den Content des Dialoges von einem Domument an ein anderes weitergeben und bräuchte in den weiteren keinen Requester mehr.

          Im Moment sind das noch threotische Gedanken, da ich mir vorher gerne schon im Klaren sein möchte, wohin mich die Reise führt ohne womöglich mehrmals von vorne zu starten. Aber im Grunde läuft es wohl darauf hinaus, dass ich entweder alles auf eine Single-Page-Lösung baue oder die Verbindung bei jedem Aufruf einer Seite neu aufbaue.

          Was mich aber viel mehr interessiert: welches Websocket-Paket benutzt Du? Wie realisierst Du darin, dass der WS-Server nur von Dokumenten deiner Domain benutzt werden kann?

          Ich benutzer kein Paket (sowas wie socket.io z.B.). Ich habe mich am Beispiel von hier orientiert: Chat using Websocket

          Da ich die Socket-Resource und die Identifikation des Clients in einem Multi-Array speichere, wieviele Einträge (und damit wieviele gleichzeitige Verbindungen) verträgt das Array? Oder ist das letztlich nur vom memory_limit abhängig?

          Über die Sicherheit habe ich mir sicherlich auch noch keine ausreichenden Gedanken gemacht. Gibt es hierfür entsprechende Informationen, wie ich den Zugriff schützen kann?

          LG Marvin

          1. Tach!

            Da ich die Socket-Resource und die Identifikation des Clients in einem Multi-Array speichere, wieviele Einträge (und damit wieviele gleichzeitige Verbindungen) verträgt das Array? Oder ist das letztlich nur vom memory_limit abhängig?

            Fragen, die das PHP-Handbuch beantworten kann ... Wenn es ein Limit bei Arrays gibt, wird es wohl eher nicht nur bei "Handvoll" liegen, sondern bei MaxInt, wenn überhaupt. Wenn du übermäßig viele Besucher bekommst, platzt das System eher bereits an anderer Stelle.

            Über die Sicherheit habe ich mir sicherlich auch noch keine ausreichenden Gedanken gemacht. Gibt es hierfür entsprechende Informationen, wie ich den Zugriff schützen kann?

            Solange du nicht per Firewall einen Ausschluss erreichen kannst, kannst du nicht vorher wissen, wer eine Verbindung herstellt, bleiben dir nur die üblichen Authentifizierungsverfahren, nach dem Verbindungsaufbau, zum Beispiel Nutzername und Passwort.

            dedlfix.

            1. Hello,

              Da ich die Socket-Resource und die Identifikation des Clients in einem Multi-Array speichere, wieviele Einträge (und damit wieviele gleichzeitige Verbindungen) verträgt das Array? Oder ist das letztlich nur vom memory_limit abhängig?

              Fragen, die das PHP-Handbuch beantworten kann ... Wenn es ein Limit bei Arrays gibt, wird es wohl eher nicht nur bei "Handvoll" liegen, sondern bei MaxInt, wenn überhaupt. Wenn du übermäßig viele Besucher bekommst, platzt das System eher bereits an anderer Stelle.

              Das haben wir hier im Forum schon mal ausprobiert. Müsste man noch im Archiv finden.

              @Marvin Esse:

              Das hängt in erster Linie vom Memory_Limit ab. Üblicherweise bekommen normale PHP-Scripte ja nur 8MB zugewiesen (zumindest bei mir *g*). PHP-Arrays sind in Wirklichkeit aber Listen bzw. Hashtables mit angeschlossenen Datenbereichen, bzw. Unterlisten. Also ergibt sich im Prinzip eine Baumstruktur. Die benötigt für die Verwaltung einiges an Overhead.

              Wenn Du nun einen PHP-CLI-Prozess mit dem "Server" in den Hintergrund stellst, solltest Du dem also genügend Speicher (128 - 512MB?) zuordnen, wenn Du nicht gleich am Ende sein willst. Die Kommunikation selbst benötigt ja auch noch Speicher. Der Prozess wird ja nur einmal benötigt und nicht pro Request, so wie beim HTTP-Responder.

              Hättest Du Interesse daran, die Erkenntnisse zum Thema hier im Wiki auf einer "Experimantal"-Seite gemeinsam zu sammeln? Die Diskussionen dazu würden weiterhin im Forum laufen, aber würden mit den Fragen im Wiki kreuzverlinkt werden. Das Thema ist zu umfangreich, um es in einem einzigen Thread abzuhandeln.

              Liebe Grüße
              Tom S.

              --
              Es gibt nichts Gutes, außer man tut es
              Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
              1. Hättest Du Interesse daran, die Erkenntnisse zum Thema hier im Wiki auf einer "Experimantal"-Seite gemeinsam zu sammeln? Die Diskussionen dazu würden weiterhin im Forum laufen, aber würden mit den Fragen im Wiki kreuzverlinkt werden. Das Thema ist zu umfangreich, um es in einem einzigen Thread abzuhandeln.

                Dafür müsste ich wohl erstmal einen eigenen Webserver aufsetzen, damit nicht die Besucher der bisherigen Seiten das Ergebnis verfälschen. Und dort dann eine Testseite, z.B. so ein einfacher Chat und ein paar Clients, damit sich das gegebenenfalls auf viele hochrechnen lässt. Grundsätzlich bin ich aber sehr gerne bereit dann meine bescheidenen Erkenntnisse zu teilen.

                LG Marvin

              2. Tach!

                Wenn es ein Limit bei Arrays gibt, wird es wohl eher nicht nur bei "Handvoll" liegen, sondern bei MaxInt, wenn überhaupt.

                Das hängt in erster Linie vom Memory_Limit ab. Üblicherweise bekommen normale PHP-Scripte ja nur 8MB zugewiesen (zumindest bei mir *g*). PHP-Arrays sind in Wirklichkeit aber Listen bzw. Hashtables mit angeschlossenen Datenbereichen, bzw. Unterlisten. Also ergibt sich im Prinzip eine Baumstruktur. Die benötigt für die Verwaltung einiges an Overhead.

                Das war jetzt nicht direkt die Frage, sondern ob die Anzahl der Verbindungen ein Array sprengt oder nicht. Die Antwort ist: nein, unrealistisch. Dass weitere zu speichernde Daten weiteren Speicher brauchen, und ob die mit dem Connections-Array direkt verbunden sind, steht auf einem anderen Blatt.

                Wenn Du nun einen PHP-CLI-Prozess mit dem "Server" in den Hintergrund stellst, solltest Du dem also genügend Speicher (128 - 512MB?) zuordnen, wenn Du nicht gleich am Ende sein willst. Die Kommunikation selbst benötigt ja auch noch Speicher. Der Prozess wird ja nur einmal benötigt und nicht pro Request, so wie beim HTTP-Responder.

                Hättest Du Interesse daran, die Erkenntnisse zum Thema hier im Wiki auf einer "Experimantal"-Seite gemeinsam zu sammeln?

                Ähm, was genau für einen Erkenntnisgewinn versprichst du dir da? Und inwieweit wird der Speicherverbrauch einer individuellen Programmierung vergleichbar sein mit anderen individuellen Programmierungen? Hast du sowas in der Art im Kopf: "5 Verbindungen brauchen 1 MB, wenn du also 10 Verbindungen planst, brauchst du 2 MB."?

                Wenn ja, kann ich das Ergebnis vorwegnehmen: Wenn ein Lasttest in für den Anwendungsfall realistischer Größenordnung mit einem Speicherlimit aussteigt, musst du mehr Speicher freigeben und gegebenenfalls mehr in die Maschine stecken. Aussagen zu konkreten Größen lassen sich wegen der Individualität der Anwendungsfälle nicht machen.

                dedlfix.

                1. Hello,

                  STOP * REWIND * START

                  Du bringst da jetzt meine Aussagen durcheinander. Und Du unterschlägst wichtigte Sätze:

                  "Das haben wir hier im Forum schon mal ausprobiert. Müsste man noch im Archiv finden."

                  Das bezog sich auf den Speicherverbrauch von Arrays und wieviele Elemente diese aufnehmen könnten.

                  Und die Erkenntnisse, die ich zu gewinnen hoffe, beziehen sich nicht auf Arrays, sondern auf Websocket und ihre Techniken auf Client- und Server-Seite - sofern es die bei einem installierten Websocket überhaupt noch gibt. Nach der Institution der beiderseitigen Websockets sollten die Partner doch (nahezu) gleichberechtigt sein :-)

                  Liebe Grüße
                  Tom S.

                  --
                  Es gibt nichts Gutes, außer man tut es
                  Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
                  1. Tach!

                    Du bringst da jetzt meine Aussagen durcheinander. Und Du unterschlägst wichtigte Sätze:

                    Durchaus möglich.

                    "Das haben wir hier im Forum schon mal ausprobiert. Müsste man noch im Archiv finden."

                    Das bezog sich auf den Speicherverbrauch von Arrays und wieviele Elemente diese aufnehmen könnten.

                    Ja, aber das kann man auf ein Wort abkürzen: ausreichend. Die Grenze ist praktisch nicht erreichbar. Da schlägt eher das Speicherlimit zu, und das nicht nach Anzahl der Elemente, sondern nach Größe der Daten und der Daten in anderen Strukturen des Scripts.

                    Und die Erkenntnisse, die ich zu gewinnen hoffe, beziehen sich nicht auf Arrays, sondern auf Websocket und ihre Techniken auf Client- und Server-Seite - sofern es die bei einem installierten Websocket überhaupt noch gibt. Nach der Institution der beiderseitigen Websockets sollten die Partner doch (nahezu) gleichberechtigt sein :-)

                    Es gibt auch abseits von Websockets bidirektionale Datenverbindungen. Und trotzdem bleibt das eine Ende meist ein Server und das andere ein Client.

                    dedlfix.

                    1. Hello,

                      Ja, aber das kann man auf ein Wort abkürzen: ausreichend. Die Grenze ist praktisch nicht erreichbar. Da schlägt eher das Speicherlimit zu, und das nicht nach Anzahl der Elemente, sondern nach Größe der Daten und der Daten in anderen Strukturen des Scripts.

                      Dankeschöne. Genauso war's gemeint.

                      Und die Erkenntnisse, die ich zu gewinnen hoffe, beziehen sich nicht auf Arrays, sondern auf Websocket und ihre Techniken auf Client- und Server-Seite - sofern es die bei einem installierten Websocket überhaupt noch gibt. Nach der Institution der beiderseitigen Websockets sollten die Partner doch (nahezu) gleichberechtigt sein :-)

                      Es gibt auch abseits von Websockets bidirektionale Datenverbindungen. Und trotzdem bleibt das eine Ende meist ein Server und das andere ein Client.

                      Na schaun wir mal, wie wir das gemeinsam erkunden und in verständliche Erklärungen verpacken können. Ich bleibe jedenfalls bei diesem Thema dabei. Schade, dass wir noch keine allgemein interessierenden Entwicklungsthemen zuordnen und abonnieren können. Die Zuweisung zu diesen Themenbreichen müsste dann sicherlich durch mindestens halbwissende Moderatoren erfolgen :-)

                      Liebe Grüße
                      Tom S.

                      --
                      Es gibt nichts Gutes, außer man tut es
                      Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
  2. Tach!

    Soweit so gut, mein Problem dabei ist jetzt, dass sich die Kommunikation nicht auf eine Seite beschränkt und im Moment verstehe ich das so, dass bei jedem Laden einer neuen Seite die Verbindung neu aufgebaut wird.

    Ja, jeder Browsertab hat seinen eigenen Endpunkt bei der Verbindung zum Webserver. Der Server schickt ein Datenpaket entweder an den einen oder den anderen. Die Browsertabs laufen getrennt, vielleicht sogar in eigenen Prozessen. Ich kenne nichts, das man unabhängig von einem konkreten Browsertab laufen lassen kann, das dann als gemeinsamer Endpunkt dienen könnte. Aber das würde das Problem auch nur vom Server in den Browser verlagern, ohne es grundsätzlich zu lösen.

    Oder könnte ich auf der Server-Seite prüfen, ob ich den Client bereits in der Verbindungsliste (Liste der Sockets) habe und die Verbindung wiederbenutzen, ohne dass bei jedem Seitenwechsel eine neue Verbindung geöffnet werden muss?

    Nein. Auf dieser Ebene kannst du nicht eingreifen.

    Dummerweise lassen sich Socket Resourcen wohl auch nicht in einer MySQL-Tabelle speichern.

    Eine solche Verbindung muss am Leben erhalten werden, die bricht sonst ab. Du hättest dann nur die Daten einer toten Verbindung gespeichert. Sockets können nur in aktiven Prozessen laufen.

    Ich habe etwas über sogenannte SharedWorker gefunden, aber die scheinen auch mit dem aktuellen Firefox noch nicht fehlerfrei zu laufen? Jedenfalls haben die bisher gefundenen Online-Beispiele nicht funktioniert.

    Ah, sowas gibt es doch? Das ist ja schon sehr lange im Forefox und Chrome, so dass ich eher nicht denke, dass die Beispiele grundsätzlich nicht funktionieren.

    Momentan fällt mir nur ein, dass ich alles auf eine einzige Seite umstelle und die eigentlichen Seiteninhalte z.B. per Ajax lade. Damit bliebe wohl dann immer dieselbe Seite geöffnet.

    Was ist denn das Problem, dass du den Verteiler nicht im Server aufsetzen kannst, sondern ihn dir in den Browser holen möchtest? Du brauchst doch sowieso auf dem Server einen Verteilmechanismus für mehrere Clients. Da spielt es doch keine Rolle, ob die Endpunkte unterschiedliche Clients oder unterschiedliche Tabs in einem Client sind.

    dedlfix.

  3. Ich denke, du meinst mit "mehreren Seiten", dass dein Web aus mehreren HTML Seiten besteht und Du die WS Verbindung über Seitennavigation hinweg behalten willst.

    Das wird nicht gehen, eine Navigation verwirft alle Ressourcen der bestehenden Seite.

    Aber was spricht dagegen, wenn Du nach der Navigation von der neuen Seite aus eine neue WS Verbindung aufbaust? Du musst nur sicherstellen, dass sie als "gleicher Client" wiedererkannt wird, z.B. über den Wert eines Cookies. Wenn Du darin eine GUID ablegst, oder einen anderen nicht erratbaren und pro Session kryptographisch sicher erzeugten Wert, sollte das doch nicht wirklich ein Problem sein, oder? Wenn Du auf der neuen Seite Informationen haben musst, die die alte Seite über den WS Kanal bekommen hat, dann musst Du sie entweder neu vom Server holen oder auf dem Client zwischenspeichern (local storage).

    Du läufst natürlich Gefahr, dass der Server etwas schickt und der Client gerade navigiert, so dass das Serverdatenpaket verloren geht. Dafür musst Du Dir ggf. ein Protokoll zur Absicherung der Kommunikation ausdenken, das würde ich aber auf den Moment verschieben wo sich das wirklich als Problem herausstellt.

    Eine Single Page Application ist aber sicherlich die elegantere Lösung. Nicht unbedingt die einfachste.

    Rolf

  4. Hallo Marvin,

    du hast völlig recht: Websocket-Verbindungen werden bei jedem Seitenwechsel geschlossen. Du hast keine Chance das zu verhindern. Punkt.

    Es gibt verschiedene Ansätze, um dieses Problem zu umgehen. Eine übliche Variante wäre es, Tools wie Redis zu verwenden, um die Nachrichten, die du deinen Usern schicken möchtest, zu speichern und bei einer Neuverbindung zu schicken. Zusammen mit einem Timeout, versteht sich: wenn der User erst drei Tage später wiederkehrt, will man ja nicht, dass er drei Tage alte Events bekommt.

    Eine weitere Möglichkeit ist es, eine SPA zu erstellen. Der User lädt die Seite nicht neu, die Verbindung bleibt weitestgehend bestehen (abgesehen von Verbindungsabbrüchen).

    Man kann auch einfach damit leben, dass in dem Augenblick der User halt keine Nachricht bekommt; ob dieser Weg sinnvoll ist, musst du entscheiden, das können wir nicht für dich.

    Ein dritter Weg wäre es, Service Workers einzusetzen; hier müsstest du allerdings auf die Push API umsteigen.

    Dieses ganze Thema steckt noch ein wenig in den Kinderschuhen (auch wenn es bereits tausendmal besser ist als noch 2011: da habe ich mal einen Websocket-Server in Erlang selber implementiert) und es ist häufig noch Handarbeit notwendig, um zu erreichen, was du möchtest.

    Ich hoffe, ich konnte ein paar mögliche Lösungswege aufzeigen; falls nicht, frag nochmal nach.

    LG,
    CK