Ankündigung

Einklappen
Keine Ankündigung bisher.

Websocket Server Connections parallel handlen

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Websocket Server Connections parallel handlen

    Hallo Leute,

    ich komme gerade nicht so wirklich weiter. Ich möchte mehrere Clients verbinden könnten auf einen ServerSocket. Das funktioniert soweit prinzipiell auch. Nur weiß ich ja, dass es in PHP Schwierigkeiten mit Threads gibt (sollte man nicht unter einem Webserver betreiben, warum auch immer).
    Normalerweise, so kenne ich das aus C# / Java, würde ich an der Stelle einen Thread pro Connection erstellen. Das Accept kann dann ruhig weiterhin blockieren, solange bis neue Connection kommen.

    Bisher habe ich folgendes:
    Klasse SimpleServer:
    PHP-Code:
    namespace Server;

    class 
    SimpleServer
    {
        protected 
    $serverSocket;
        protected 
    $serverSocketsChange;
        protected 
    $host;
        protected 
    $port;
        protected 
    $clients;
        protected 
    $run;

        const 
    WS_VERSION 13;

        public function 
    __construct($host$port)
        {
            
    $this->host $host;
            
    $this->port $port;

            
    // Socket erstellen
            
    $this->serverSocket socket_create(AF_INETSOCK_STREAMSOL_TCP);
            
    socket_set_option($this->serverSocketSOL_SOCKETSO_REUSEADDR1);

            
    $this->clients = array();
        }

        public function 
    bind()
        {
            
    // Socket an Adresse und Port binden
            
    socket_bind($this->serverSocket$this->host$this->port);
        }

        public function 
    listen()
        {
            
    // An Port lauschen
            
    socket_listen($this->serverSocket);
        }

        public function 
    accept()
        {
            
    $this->run true;
            while (
    $this->run) {
                echo 
    'Warte auf Verbindung...' . \PHP_EOL;

                
    $client socket_accept($this->serverSocket);
                echo 
    'Verbindung angenommen.' . \PHP_EOL;

                
    $this->sendHeaders($client);

                for (
    $i 0$i 3$i++) {
                    
    $this->multicast('hi from server'$client);
                    
    sleep(2);
                }

                
    $this->clients[] = $client;
            }

            
    $this->disconect();
        }

        public function 
    multicast($msg$client null)
        {
            if (
    $client !== null) {
                
    $response chr(129).chr(\mb_strlen($msg)) . $msg;
                
    socket_write($client$response);
            } else {
                foreach (
    $this->clients as $client) {
                    
    $response chr(129).chr(\mb_strlen($msg)) . $msg;
                    
    socket_write($client$response);
                }
            }
        }

        public function 
    stop()
        {
            
    $this->run false;
        }

        public function 
    disconect()
        {
            foreach (
    $this->clients as $client) {
                
    socket_close($client);
            }
        }

        private function 
    sendHeaders($client)
        {
            
    $request socket_read($client5000);
            
    preg_match('#Sec-WebSocket-Key: (.*)\r\n#'$request$matches);
            
    $key base64_encode(pack(
                
    'H*',
                
    sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
            ));
            
    $headers "HTTP/1.1 101 Switching Protocols\r\n";
            
    $headers .= "Upgrade: websocket\r\n";
            
    $headers .= "Connection: Upgrade\r\n";
            
    $headers .= 'Sec-WebSocket-Version: ' self::WS_VERSION "\r\n";
            
    $headers .= 'Sec-WebSocket-Accept: ' $key "\r\n\r\n";
            
    socket_write($client$headers, \mb_strlen($headers));
        }

    Aufruf der Klasse (Konsole Skript bisher):
    PHP-Code:
    require 'vendor/autoload.php';

    use 
    Server\SimpleServer;

    $server = new SimpleServer('127.0.0.1'8090);
    $server->bind();
    $server->listen();
    $server->accept();
    $server->stop(); 
    Der Client:
    HTML-Code:
    <!DOCTYPE html>
    <html lang="de">
    <head>
        <title>Websocket Test</title>
    </head>
    <body>
    <h1>Chatbox</h1>
    
    <div id="chat"></div>
    
    <script type="text/javascript" src="//code.jquery.com/jquery-3.3.1.min.js"></script>
    <script type="text/javascript">
        var con = new WebSocket('ws://127.0.0.1:8090');
        var $chat = $('#chat');
    
        con.onmessage = function(e) {
            $chat.append('<p>' + e.data + '</p>');
        };
    
        con.onopen = function(e) {
            con.send('Hello Me!');
        };
    
        con.onclose = function (e) {
            console.log('connection closed.', arguments);
        }
    </script>
    </body>
    </html>
    Mir fehlt die Parallelität im SimpleServer::accept(). Außerdem kann ich so aktuell den Server nicht stoppen (es kommt nie zu den boolschen Flag). Wenn ich also Threads vermeiden will, dann bräuchte ich etwas nicht blockierendes. Aber auf der anderen Seite soll es doch wieder blockieren, bis eine neue Connection kommt. Also irgendwie zweischneidig. Am Rande sei noch erwähnt, dass das ganze auf Linux laufen wird. Also die pctnl Zeug müsste auch gehen.



    MFG


    derwunner

  • #2
    Formatierung!

    Du kannst mit socket_select() mehrere Sockets überwachen. Parallel ist das zwar nicht, aber zumindestens kannst du meherere Clients bedienen. Ein Blick ist vielleicht auch https://reactphp.org/ wert. Das Prinzip ist das gleiche, aber mit netten Interface und ein paar Extras.

    Kommentar


    • #3
      erc Ja sorry, ich wollte den Beitrag lieber nicht nochmal anfassen, sonst wäre es noch hässlicher geworden.

      Das hatte ich bereits, war mir zu kompliziert, bzw. für meinen Anwendungsfall nicht geeignet. Ich hatte bereits das hier: https://packagist.org/packages/cboden/ratchet
      Eben davon wollte ich weg zu einer einfachen Lösung. Vorallem konnte man dort nicht aus dem Event driven ausbrechen, was ich aber in meinen Fall brauche.

      Kommentar


      • #4
        php kann out of the box kein Multi-Threading, egal, wie die diversen Bibliotheken das einem vorgaukeln wollen - Es läuft immer pro Prozess alles sequentiell ab. Warum willst Du das denn überhaupt unbedingt in php umsetzen? Wenn Du schon eine Shell zur Verfügung hast, warum benutzt Du nicht z. B. Go?

        Kommentar


        • #5
          Hab deinen Code mal leserlich formatiert:
          PHP-Code:
          <?php

          namespace Server;

          class 
          SimpleServer
          {
              protected 
          $serverSocket;
              protected 
          $serverSocketsChange;
              protected 
          $host;
              protected 
          $port;
              protected 
          $clients;
              protected 
          $run;
              const 
          WS_VERSION 13;

              public function 
          __construct($host$port)
              {
                  
          $this->host $host;
                  
          $this->port $port;          // Socket erstellen
                  
          $this->serverSocket socket_create(AF_INETSOCK_STREAMSOL_TCP);
                  
          socket_set_option($this->serverSocketSOL_SOCKETSO_REUSEADDR1);
                  
          $this->clients = array();
              }

              public function 
          bind()
              {         
          // Socket an Adresse und Port binden
                  
          socket_bind($this->serverSocket$this->host$this->port);
              }

              public function 
          listen()
              {         
          // An Port lauschen
                  
          socket_listen($this->serverSocket);
              }

              public function 
          accept()
              {
                  
          $this->run true;
                  while (
          $this->run) {
                      echo 
          'Warte auf Verbindung...' . \PHP_EOL;
                      
          $client socket_accept($this->serverSocket);
                      echo 
          'Verbindung angenommen.' . \PHP_EOL;
                      
          $this->sendHeaders($client);
                      for (
          $i 0$i 3$i++) {
                          
          $this->multicast('hi from server'$client);
                          
          sleep(2);
                      }
                      
          $this->clients[] = $client;
                  }
                  
          $this->disconect();
              }

              public function 
          multicast($msg$client null)
              {
                  if (
          $client !== null) {
                      
          $response chr(129) . chr(\mb_strlen($msg)) . $msg;
                      
          socket_write($client$response);
                  } else {
                      foreach (
          $this->clients as $client) {
                          
          $response chr(129) . chr(\mb_strlen($msg)) . $msg;
                          
          socket_write($client$response);
                      }
                  }
              }

              public function 
          stop()
              {
                  
          $this->run false;
              }

              public function 
          disconect()
              {
                  foreach (
          $this->clients as $client) {
                      
          socket_close($client);
                  }
              }

              private function 
          sendHeaders($client)
              {
                  
          $request socket_read($client5000);
                  
          preg_match('#Sec-WebSocket-Key: (.*)\r\n#'$request$matches);
                  
          $key base64_encode(pack('H*'sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
                  
          $headers "HTTP/1.1 101 Switching Protocols\r\n";
                  
          $headers .= "Upgrade: websocket\r\n";
                  
          $headers .= "Connection: Upgrade\r\n";
                  
          $headers .= 'Sec-WebSocket-Version: ' self::WS_VERSION "\r\n";
                  
          $headers .= 'Sec-WebSocket-Accept: ' $key "\r\n\r\n";
                  
          socket_write($client$headers, \mb_strlen($headers));
              }
          }
          Für so nen einfachen Chat brauchst du keine Threads oder mehrere Prozesse.
          Das kannst du mit socket_select und einer Schleife machen. Damit kannst du locker paar tausend Request "auf einmal" handhaben.

          Ich persönlich würde dafür aber eher sowas wie socket.io verwenden.
          Da ist dann z.B. auch noch gleich Fallback für Browser ohne WebSocket-Support mit drin.

          ​​​​​​​Grüße.

          Kommentar


          • #6
            Danke php1704 !
            Du willst einen Server von einer Webseite starten.
            du willst von der gleichen webseite (client) mehre Requests parallel laufen lassen.
            wie viele webseiten willst Du paralel darstellen?


            ist doch error by desing oder nicht ?
            nebenbei, da ist nichts forgeschritten und deine weigerung den Code leserlich zu formatieren unterstreicht dies.
            Kommentare mit begrenzern wäre mal ein Anfang gewsen, so ist das alles aber eine sauerei.

            Kommentar


            • #7
              Zitat von tomBuilder Beitrag anzeigen
              Danke php1704 !
              Du willst einen Server von einer Webseite starten.
              du willst von der gleichen webseite (client) mehre Requests parallel laufen lassen.
              wie viele webseiten willst Du paralel darstellen?


              ist doch error by desing oder nicht ?
              nebenbei, da ist nichts forgeschritten und deine weigerung den Code leserlich zu formatieren unterstreicht dies.
              Kommentare mit begrenzern wäre mal ein Anfang gewsen, so ist das alles aber eine sauerei.
              Er möchte einen WebSocket Server. Warum sollte das "Error by Design" sein (abgesehen davon dass PHP nicht optimal für einen WebSocket Server ist) und was soll das mit Websites zu tun haben?
              "Software is like Sex, it's best if it's free." - Linus Torvalds

              Kommentar


              • #8
                Zitat von JaMa Beitrag anzeigen

                Er möchte einen WebSocket Server. Warum sollte das "Error by Design" sein (abgesehen davon dass PHP nicht optimal für einen WebSocket Server ist) und was soll das mit Websites zu tun haben?
                weiß ich ja, dass es in PHP Schwierigkeiten mit Threads gibt (sollte man nicht unter einem Webserver betreiben
                Ich weiss nicht wie Du das liesst, aber in meinen Auge n soll der das PHP(apache/fpm/whatever) einen server starten.
                bei wie vielen parralellen zugriffen auf seine Seite will er wohl aufhören, wann sind seine open ports voll?

                zudem will er pro server mehrere requests parrallel starten.
                ich habe eine angezeigte seite, wieviel connection will ich vom client (webclient) zum server(socketserver) parralell betreiben?

                Kommentar


                • #9
                  Pro WebSocket Server braucht er genau einen Port.

                  Was für Requests? Bei TCP-Verbindungen gibt es keine Requests sondern einfach nur Nachrichten.
                  Und die Anzahl der möglichen Connections hängt von der Bandbreite als auch von der Performance des Servers ab. Das ganze ist aber kein prinzipielles Problem von PHP.
                  "Software is like Sex, it's best if it's free." - Linus Torvalds

                  Kommentar


                  • #10
                    Zitat von derwunner Beitrag anzeigen
                    Das hatte ich bereits, war mir zu kompliziert, bzw. für meinen Anwendungsfall nicht geeignet.[...]Vorallem konnte man dort nicht aus dem Event driven ausbrechen, was ich aber in meinen Fall brauche.
                    Was ist dein Anwendungsfall und was bereitet dir dabei Probleme? Sowas wie sleep() (blockierende Funktionen) darfst du natürlich nicht verwenden. Willst du "warten", dann musst du in der "event loop" mitzählen und beim erreichen deine Aktion ausführen (nennt sich dann Timer). Oder du schaust dir https://reactphp.org/ an da gibts die Timer auch fertig und mit schönen Interface. Ratchet hat auch ein Timer mit dabei. (den von reactphp)

                    Kommentar


                    • #11
                      Zitat von JaMa Beitrag anzeigen
                      Pro WebSocket Server braucht er genau einen Port.

                      Was für Requests? Bei TCP-Verbindungen gibt es keine Requests sondern einfach nur Nachrichten.
                      Und die Anzahl der möglichen Connections hängt von der Bandbreite als auch von der Performance des Servers ab. Das ganze ist aber kein prinzipielles Problem von PHP.
                      Ah JaMa, Du siehst wie Du eineProblem lösen würdest; daher kommen dann Äusserungen wie:
                      Er möchte einen WebSocket Server. Warum sollte das "Error by Design" sein
                      Ich habe mir die Beschreibung angeschaut, den wenigen Code und bleibe immernoch der Meinung, wie er das möchte/umsetzt ist es error by desing..

                      Ja, ein Server braucht ein Port, mehre Server mehrere.
                      Ein TCP SYN übrigens wird mit "Request for Connection" beschrieben, auch wenn Du Requests bei TCP nicht kennst.

                      Es macht wenig Sinn, wenn wir uns hier weiter ereifern, mein Hinweisen auf Verständnissprobleme scheint nur Dich zu interessieren, wobei Du mir diese unterstellst.
                      Der gute TE, naja Du kannst ja andere Threads von der Wunner anschauen.


                      Kommentar


                      • #12
                        Zitat von tomBuilder Beitrag anzeigen

                        Ah JaMa, Du siehst wie Du eineProblem lösen würdest; daher kommen dann Äusserungen wie:

                        Ich habe mir die Beschreibung angeschaut, den wenigen Code und bleibe immernoch der Meinung, wie er das möchte/umsetzt ist es error by desing..

                        Ja, ein Server braucht ein Port, mehre Server mehrere.
                        Ein TCP SYN übrigens wird mit "Request for Connection" beschrieben, auch wenn Du Requests bei TCP nicht kennst.

                        Es macht wenig Sinn, wenn wir uns hier weiter ereifern, mein Hinweisen auf Verständnissprobleme scheint nur Dich zu interessieren, wobei Du mir diese unterstellst.
                        Der gute TE, naja Du kannst ja andere Threads von der Wunner anschauen.

                        Der Code zeigt eine einfache Implementierung eines Standard WebSocket Servers.
                        Warum das Error-By-Design sein soll kann ich nicht nachvollziehen?
                        Was würdest du denn Ändern damit es kein Error-By-Design mehr ist?

                        Bei der TCP-Socket Programmierung wirst du aber nie die Möglichkeit haben ein SYN Package zu senden, dazu müsstest du schon mit Raw Sockets arbeiten
                        "Software is like Sex, it's best if it's free." - Linus Torvalds

                        Kommentar


                        • #13
                          Zitat von JaMa Beitrag anzeigen

                          Der Code zeigt eine einfache Implementierung eines Standard WebSocket Servers.
                          Warum das Error-By-Design sein soll kann ich nicht nachvollziehen?
                          Was würdest du denn Ändern damit es kein Error-By-Design mehr ist?
                          Weder der TE noch ich noch sonst jemand bestreitet, daß der Code so nicht lauffähig ist.
                          Allerdings will TE ja was ändern, möglicherweise hast Du dasauch gelesen, sthet auch in #1, was sich so wie der Code aufgerufen werden soll (zukünftig) nicht realisieren lässt, sagt der TE, natürlich auch in .... #1.
                          Zitat von JaMa Beitrag anzeigen
                          Bei der TCP-Socket Programmierung wirst du aber nie die Möglichkeit haben ein SYN Package zu senden, dazu müsstest du schon mit Raw Sockets arbeiten
                          Du machst aus "request for connection" "ausschliesslich ein sync" senden. Verfremdest Du neuerdings alles, um es Deiner Argumantation anzupassen ?
                          Natürlich sende ich sync pakets, sonst könnte ich kein handshake machen, ich sende diese -- transparent wäherend verbindungsaufbaus.


                          Ich finde es extrem nervig, mit jemdmanden zu diskutieren, der werder den eingangsthread präsent hat, noch meine posts und dann auch teufel komm raus haare in der suppe sucht.

                          Kommentar


                          • #14
                            So, also ich habe ich jetzt mal den Code formatiert (Umweg über Notepad++). Danke an alle anderen für Ihre Zeit, die das auch versucht hatten.

                            Mein Use Case ist ein Ladebalken. Serverseitig sollen eine großes ZIP Archiv erstellt werden, welches aus tausenden Bildern besteht. Das Problem das es zu lösen gilt ist, dass PHP die Ausgabe erst ganz zum Schluss, oder besser gesagt obliegt das dem Webserver, wann er das macht.
                            Demnach fand ich das per XHR nicht so sinnvoll, jedes mal einen neuen PHP zu erzeugen, wenn das doch alles in einem laufen kann. Deswegen Websockets. Bei uns wird es keinem Browser geben, der keine Websockts kann, weil das nur von einem kleinen Nutzerkreis aufgerufen wird.

                            Kommentar


                            • #15
                              Zitat von derwunner Beitrag anzeigen
                              So, also ich habe ich jetzt mal den Code formatiert (Umweg über Notepad++). Danke an alle anderen für Ihre Zeit, die das auch versucht hatten.

                              Mein Use Case ist ein Ladebalken. Serverseitig sollen eine großes ZIP Archiv erstellt werden, welches aus tausenden Bildern besteht. Das Problem das es zu lösen gilt ist, dass PHP die Ausgabe erst ganz zum Schluss, oder besser gesagt obliegt das dem Webserver, wann er das macht.
                              Demnach fand ich das per XHR nicht so sinnvoll, jedes mal einen neuen PHP zu erzeugen, wenn das doch alles in einem laufen kann. Deswegen Websockets. Bei uns wird es keinem Browser geben, der keine Websockts kann, weil das nur von einem kleinen Nutzerkreis aufgerufen wird.
                              ​​​​​​Dafür könntest du auch Long Polling einsetzen.
                              Je nachdem wie viele Benutzer das System nutzen und wie lange die Generierung des ZIP Files dauert, kann das aber durchaus einiges an Ressourcen benötigen (da pro Request ein Thread).
                              "Software is like Sex, it's best if it's free." - Linus Torvalds

                              Kommentar

                              Lädt...
                              X