Mise en place d'un serveur Websocket PHP sur Debian

Discussion dans 'Administration d'un site Web' créé par 7804j, 1 Avril 2012.

  1. 7804j
    7804j WRInaute discret
    Inscrit:
    20 Avril 2011
    Messages:
    167
    J'aime reçus:
    0
    Bonjour,

    Depuis que j'ai appris l'existence des Websockets, je me suis rendu compte que l'un de mes projets majeurs n'allait plus du tout être viable car développé en AJAX, ce qui était bien trop lent. J'ai donc regardé quelques tutoriels de base sur les websockets mais malheureusement sans réel succès...

    La majeure partie de ces derniers conseillent "PhpWebsocket" (http://code.google.com/p/phpwebsocket/), je suis donc allé sur le site et ai tenté d'en suivre les instructions. Là, j'apprends qu'il me faut créer deux fichiers, que je crée alors et place sur mon serveur dans le dossier /var/www/mon_site/websocket

    Le premier, client.php :
    Code:
    <html>
    <head>
    <title>WebSocket</title>
    
    <style>
     html,body{font:normal 0.9em arial,helvetica;}
     #log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}
     #msg {width:330px;}
    </style>
    
    <script>
    var socket;
    
    function init(){
      var host = "ws://localhost:12345/websocket/server.php";
      try{
        socket = new WebSocket(host);
        log('WebSocket - status '+socket.readyState);
        socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
        socket.onmessage = function(msg){ log("Received: "+msg.data); };
        socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
      }
      catch(ex){ log(ex); }
      $("msg").focus();
    }
    
    function send(){
      var txt,msg;
      txt = $("msg");
      msg = txt.value;
      if(!msg){ alert("Message can not be empty"); return; }
      txt.value="";
      txt.focus();
      try{ socket.send(msg); log('Sent: '+msg); } catch(ex){ log(ex); }
    }
    function quit(){
      log("Goodbye!");
      socket.close();
      socket=null;
    }
    
    // Utilities
    function $(id){ return document.getElementById(id); }
    function log(msg){ $("log").innerHTML+="<br>"+msg; }
    function onkey(event){ if(event.keyCode==13){ send(); } }
    </script>
    
    </head>
    <body onload="init()">
     <h3>WebSocket v2.00</h3>
     <div id="log"></div>
     <input id="msg" type="textbox" onkeypress="onkey(event)"/>
     <button onclick="send()">Send</button>
     <button onclick="quit()">Quit</button>
     <div>Commands: hello, hi, name, age, date, time, thanks, bye</div>
    </body>
    </html>
    
    Le second, server.php :
    Code:
    <?php  /*  >php -q server.php  */
    
    error_reporting(E_ALL);
    set_time_limit(0);
    ob_implicit_flush();
    
    $master  = WebSocket("localhost",12345);
    $sockets = array($master);
    $users   = array();
    $debug   = false;
    
    while(true){
      $changed = $sockets;
      socket_select($changed,$write=NULL,$except=NULL,NULL);
      foreach($changed as $socket){
        if($socket==$master){
          $client=socket_accept($master);
          if($client<0){ console("socket_accept() failed"); continue; }
          else{ connect($client); }
        }
        else{
          $bytes = @socket_recv($socket,$buffer,2048,0);
          if($bytes==0){ disconnect($socket); }
          else{
            $user = getuserbysocket($socket);
            if(!$user->handshake){ dohandshake($user,$buffer); }
            else{ process($user,$buffer); }
          }
        }
      }
    }
    
    //---------------------------------------------------------------
    function process($user,$msg){
      $action = unwrap($msg);
      say("< ".$action);
      switch($action){
        case "hello" : send($user->socket,"hello human");                       break;
        case "hi"    : send($user->socket,"zup human");                         break;
        case "name"  : send($user->socket,"my name is Multivac, silly I know"); break;
        case "age"   : send($user->socket,"I am older than time itself");       break;
        case "date"  : send($user->socket,"today is ".date("Y.m.d"));           break;
        case "time"  : send($user->socket,"server time is ".date("H:i:s"));     break;
        case "thanks": send($user->socket,"you're welcome");                    break;
        case "bye"   : send($user->socket,"bye");                               break;
        default      : send($user->socket,$action." not understood");           break;
      }
    }
    
    function send($client,$msg){
      say("> ".$msg);
      $msg = wrap($msg);
      socket_write($client,$msg,strlen($msg));
    }
    
    function WebSocket($address,$port){
      $master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
      socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
      socket_bind($master, $address, $port)                    or die("socket_bind() failed");
      socket_listen($master,20)                                or die("socket_listen() failed");
      echo "Server Started : ".date('Y-m-d H:i:s')."\n";
      echo "Master socket  : ".$master."\n";
      echo "Listening on   : ".$address." port ".$port."\n\n";
      return $master;
    }
    
    function connect($socket){
      global $sockets,$users;
      $user = new User();
      $user->id = uniqid();
      $user->socket = $socket;
      array_push($users,$user);
      array_push($sockets,$socket);
      console($socket." CONNECTED!");
    }
    
    function disconnect($socket){
      global $sockets,$users;
      $found=null;
      $n=count($users);
      for($i=0;$i<$n;$i++){
        if($users[$i]->socket==$socket){ $found=$i; break; }
      }
      if(!is_null($found)){ array_splice($users,$found,1); }
      $index = array_search($socket,$sockets);
      socket_close($socket);
      console($socket." DISCONNECTED!");
      if($index>=0){ array_splice($sockets,$index,1); }
    }
    
    function dohandshake($user,$buffer){
      console("\nRequesting handshake...");
      console($buffer);
      list($resource,$host,$origin,$strkey1,$strkey2,$data) = getheaders($buffer);
      console("Handshaking...");
    
      $pattern = '/[^\d]*/';
      $replacement = '';
      $numkey1 = preg_replace($pattern, $replacement, $strkey1);
      $numkey2 = preg_replace($pattern, $replacement, $strkey2);
    
      $pattern = '/[^ ]*/';
      $replacement = '';
      $spaces1 = strlen(preg_replace($pattern, $replacement, $strkey1));
      $spaces2 = strlen(preg_replace($pattern, $replacement, $strkey2));
    
      if ($spaces1 == 0 || $spaces2 == 0 || $numkey1 % $spaces1 != 0 || $numkey2 % $spaces2 != 0) {
            socket_close($user->socket);
            console('failed');
            return false;
      }
    
      $ctx = hash_init('md5');
      hash_update($ctx, pack("N", $numkey1/$spaces1));
      hash_update($ctx, pack("N", $numkey2/$spaces2));
      hash_update($ctx, $data);
      $hash_data = hash_final($ctx,true);
    
      $upgrade  = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" .
                  "Upgrade: WebSocket\r\n" .
                  "Connection: Upgrade\r\n" .
                  "Sec-WebSocket-Origin: " . $origin . "\r\n" .
                  "Sec-WebSocket-Location: ws://" . $host . $resource . "\r\n" .
                  "\r\n" .
                  $hash_data;
    
      socket_write($user->socket,$upgrade.chr(0),strlen($upgrade.chr(0)));
      $user->handshake=true;
      console($upgrade);
      console("Done handshaking...");
      return true;
    }
    
    function getheaders($req){
      $r=$h=$o=null;
      if(preg_match("/GET (.*) HTTP/"   ,$req,$match)){ $r=$match[1]; }
      if(preg_match("/Host: (.*)\r\n/"  ,$req,$match)){ $h=$match[1]; }
      if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
      if(preg_match("/Sec-WebSocket-Key2: (.*)\r\n/",$req,$match)){ $key2=$match[1]; }
      if(preg_match("/Sec-WebSocket-Key1: (.*)\r\n/",$req,$match)){ $key1=$match[1]; }
      if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; }
      return array($r,$h,$o,$key1,$key2,$data);
    }
    
    function getuserbysocket($socket){
      global $users;
      $found=null;
      foreach($users as $user){
        if($user->socket==$socket){ $found=$user; break; }
      }
      return $found;
    }
    
    function     say($msg=""){ echo $msg."\n"; }
    function    wrap($msg=""){ return chr(0).$msg.chr(255); }
    function  unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }
    function console($msg=""){ global $debug; if($debug){ echo $msg."\n"; } }
    
    class User{
      var $id;
      var $socket;
      var $handshake;
    }
    
    ?>
    
    Je change le port indiqué dans le fichier, ajoute l'exception au Linux Firewall (est-ce vraiment nécessaire, en fait ?), place les deux fichiers en FTP dans le dossier et ouvre chrome. Là, je m'aperçois que dans le fichier client.php j'ai une ligne qui contient ws://localhost:12345/websocket/server.php. Je me dis alors que je devrais sûrement la renommer mais que devrais-je y placer ? Est-ce que je devrais mettre quelque chose du genre ws://monsite.com/websocket/server.php ? Est-ce que je devrais mettre quelque chose du genre ws://88.553.(ip):12345/var/www/mon_site/websocket/server.php ? En fait, je n'y connais rien et je sais pas trop où mène ce port et comment le configurer... Dois-je toucher à qqch de la config Apache ou je ne sais quoi ?

    Sinon, encore une petite question pour l'activation du serveur websocket. J'avais bien sûr préalablement lancé la commande php -f /var/www/mon_site/websocket/server.php mais que se passe-t-il si je modifie le fichier server.php, par exemple par FTP ? Dois-je relancer le serveur ? Et si je souhaite ajouter des fonctionnalités au serveur, suis-je obligé à chaque fois de modifier l'intégralité du fichier et de le relancer ?

    Merci d'avance de votre grande aide :)

    7804j
     
  2. Julia41
    Julia41 WRInaute passionné
    Inscrit:
    31 Août 2007
    Messages:
    1 779
    J'aime reçus:
    0
    Si tu modifies ton fichier server.php, oui tu dois relancer ton socket.
    Aussi, tu dois "bind" ton websocket sur une IP (ou un hostname comme "ton site") publique, surtout pas sur localhost (sauf si tu dev).
    Par contre, attends un peu, car "rien" n'est compatible pour le moment.
    Pour avoir fait quelques tests, ça bourrine quand même bien le serveur :p
     
  3. 7804j
    7804j WRInaute discret
    Inscrit:
    20 Avril 2011
    Messages:
    167
    J'aime reçus:
    0
    "Rien" ? Tu veux dire tout sauf IE ? Enfin c'est ce que j'ai cru comprendre :/
    De toute manière, même avec AJAX mon site (une sorte de jeu par navigateur) ne serait pas compatible IE ^^'

    Qu'entends-tu par bourrinage du serveur ? J'ai un dédié à 70 euros par mois chez OVH qui a quand même des bons caracs, ça risque quand même de le ralentir beaucoup ?

    Pour "binder" mon websocket, je n'ai pas bien compris : si mon serveur se trouve à l'url http://www.example.com/websocket/server.php, suffit-il de remplacer cela par ws://www.example.com/websocket/server.php ? Ou dois-je spécifier le port qqpart dans l'url ou dans ma config debian ? J'ai essayé comme ceci mais cela n'a pas vraiment fonctionné :/

    P.S : Ma page de test est actuellement ici, mais je reçois une erreur dans la console javascript "Unexpected response code : 200" : http://dofus2.org/ajax/client.php
    Qu'est-ce que cela veut dire ?
     
  4. spout
    spout WRInaute accro
    Inscrit:
    14 Mai 2003
    Messages:
    9 153
    J'aime reçus:
    324
    Et pourquoi ne pas faire les websockets avec node.js / expressjs / socket.io ?
     
  5. 7804j
    7804j WRInaute discret
    Inscrit:
    20 Avril 2011
    Messages:
    167
    J'aime reçus:
    0
    Je n'y connais rien, mais cela a l'air intéressant. Sont-ce les trois des technologies utilisant ce protocole ws et permettant d'éviter les requêtes http inutiles ? Quelles sont les différences principales, etc. ?
     
  6. 7804j
    7804j WRInaute discret
    Inscrit:
    20 Avril 2011
    Messages:
    167
    J'aime reçus:
    0
    Donc si j'ai bien compris, tous ces systèmes ne fonctionneraient qu'avec du javascript tournant côté serveur. Le problème, c'est que je n'ai absolument aucune expérience ni aucune idée de comment je devrais m'y prendre, si en plus je dois rendre le tout compatible aux bases de données :/ J'ai passé une heure à essayer de comprendre un peu mais je m'en sors pas très bien avec les tutos qui existent (je suis même pas parvenu à terminer l'installation...)

    Je pense que ce serait bien si j'arrivais à faire fonctionner cela en php en fait, mais merci quand même pour le lien, je connaissais pas tout ça.

    J'en reviens donc au même problème :
     
  7. 7804j
    7804j WRInaute discret
    Inscrit:
    20 Avril 2011
    Messages:
    167
    J'aime reçus:
    0
    Je n'ai toujours pas pu résoudre le problème, mais j'ai remarqué que php me retournait cela quand j'allais sur la page server.php avec mon navigateur :

    Warning: socket_bind(): unable to bind address [98]: Address already in use in /var/www/dofus2_org/ajax/server.php on line 59 socket_bind() failed

    Une idée ?
     
Chargement...
Similar Threads - place serveur Websocket Forum Date
quelqun pour m'expliquer la mise en place de serveurs pour email Gmail, Google Talk, Blogger et Orkut 25 Septembre 2017
Serveur et emplacement du CMS installé Débuter en référencement 24 Septembre 2014
Comment protéger son ebook qui est placé dans le serveur contre les moteurs de recherche Demandes d'avis et de conseils sur vos sites 19 Décembre 2012
Dossier utilisateur déplacé et impossibilité d'accéder à mon serveur OVH Administration d'un site Web 7 Juillet 2012
Besoin de conseils pour mise en place réseau/serveur partage de fichiers en interne Le café de WebRankInfo 4 Août 2011
Mise en place d'un serveur web Administration d'un site Web 14 Mars 2011
Emplacement adsense et clics incorrects AdSense 20 Avril 2021
Déplacement de catégories CMS Prestashop : impact SEO ? e-commerce 16 Mars 2021
Google Images: URL à la place du titre dans les résultats de recherche YouTube, Google Images et Google Maps 4 Mars 2021
Référencement Marketplace Référencement Google 20 Janvier 2021