Kommunauty
Connexion
Inscription

Fichiers liés au système de web socket

le 20 janvier 2011 • Programmation • par openrpg

Vous souhaitez créer un système de web socket sur votre site et vous ne savez pas comment faire ?

Voici un exemple de tchat humain vs machine qui vous explique le fonctionnement de ce genre de programme.

client.php

Partie du script qui sera visible sur votre site internet (ou en local) via par exemple cette adresse : http://localhost/websocket/client.php

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
var socket;
var host = "ws://localhost:8080/websocket/startDaemon.php";
  
$(document).ready(function() {
  
  try
  {
    var socket = new WebSocket(host);
    
    message('<p class="event">Etat : '+socket.readyState);
    
    socket.onopen = function(){
      message('<p class="event">Etat : '+socket.readyState+' (Ouvert)');
    }
    
    socket.onmessage = function(msg){
      message('<p class="message">Reçu : '+msg.data);
    }
    
    socket.onclose = function(){
      message('<p class="event">Etat : '+socket.readyState+' (Fermer)');
    }      
  }
  catch(exception){ message('<p>Erreur : '+exception); }
  
  function send()
  {
    var text = $('#text').val();
    if(text=="")
    {
      message('<p class="warning">Veuillez indiquer un message');
      return ;  
    }
    
    try
    {
      socket.send(text);
      message('<p class="event">Envois : '+text)
    }
    catch(exception){ message('<p class="warning">'); }
    
    $('#text').val("");
  }
  
  function message(msg)
  {
    $('#chatLog').append(msg+'</p>');
  }
  
  $('#text').keypress(function(event) {
    if (event.keyCode == '13') {
      send();
    }
  });  
  
  $('#send').click(function(){ send(); });
  
  $('#disconnect').click(function(){ socket.close(); });
      
});
</script>
<meta charset=utf-8 />
<style type="text/css">
body {
  font-family:Arial, Helvetica, sans-serif;
}
#chatLog {
  padding:5px;
  border:1px solid black;
}
#chatLog p {
  margin:0;
}
.event {
  color:#999;
}
.warning {
  font-weight:bold;
  color:#CCC;
}
</style>
<title>WebSockets Client</title>
</head>
<body>
<h1>WebSockets</h1>
<div id="chatLog"></div>
<p id="examples">Taper : 'bonjour', 'nom', 'age', 'date'</p>
<input id="text" type="text" />
<button id="send">Envoyer</button>
<button id="disconnect">Deconnexion</button>
</body>
</html>


startDaemon.php

Ce fichier est celui qu'il faut lancer pour activer le serveur. Il doit être lancé via votre terminal avec cette commande : $ php -q /le/chemin/du/fichier/startDaemon.php

Il réunit tout le fichier au bon fonctionnement du programme et initialise l'objet "new socketWebSocket" a qui nous avons passé 2 arguments, le premier est l'adresse du server websocket et le deuxième est le port sur lequel nous interrogeons le programme ici on est en local donc "localhost" et j'ai choisi d'utiliser le port "8080"

<?php
ob_implicit_flush(true);
require_once 'socket.class.php';
require_once 'socketWebSocket.class.php';
require_once 'trigger.class.php';
new socketWebSocket('localhost',8080);
?>

socket.class.php

Ce fichier gère la création du websocket et son initialisation. Il contient la classe parent qui par la suite aura 1 héritier et un sous héritier.

<?php
class socket
{
  protected $master, $allsockets = array();
  
  public function __construct( $host='localhost', $port=8080 )
  {
    $this->createSocket($host,$port);
  }
  
  private function createSocket( $host, $port )
  {
    if( ( $this->master = socket_create(AF_INET,SOCK_STREAM,SOL_TCP) ) < 0 )
      die('socket_create() erreur : '.socket_strerror($this->master));
  
    self::console('Socket '.$this->master.' créé.');
  
    socket_set_option($this->master,SOL_SOCKET,SO_REUSEADDR,1);
  
    if( ( $ret = socket_bind($this->master,$host,$port) ) < 0 )
      die('socket_bind() erreur : '.socket_strerror($ret));
  
    self::console('Socket connecté '.$host.':'.$port);
  
    if( ( $ret = socket_listen($this->master,5) ) < 0 )
      die('socket_listen() erreur : '.socket_strerror($ret));
  
    self::console('Debut de l\'écoute.');
  
    $this->allsockets[] = $this->master;
  }
  
  protected function console( $msg, $type = 'System' )
  {
    $msg = explode("\n",$msg);
    foreach( $msg as $line )
      echo date('Y-m-d H:i:s') . ' '.$type.' : '.$line."\n";
  }
  
  protected function send($client,$msg)
  {
    socket_write($client, $msg,strlen($msg));
  }
}
?>


socketWebSocket.class.php

Ce fichier est le coeur de notre websocket. Il gère la boucle qui reçoit/envoie les informations aux diverses connexions qui lui sont liées. Il gère aussi les connexions proprement dites et aussi les déconnexions

<?php
class socketWebSocket extends socket
{
  private $clients = array(), $handshakes = array();
  
  public function __construct()
  {
    parent::__construct();
    self::run();
  }
  
  protected function console( $msg, $type='WebSocket' )
  {
    parent::console( $msg, $type );
  }
  
  protected function send( $client, $msg )
  {
    self::console('> '.$msg);
    parent::send($client,chr(0).$msg.chr(255));
  }
  
  private function run()
  {
    while(true)
    {
      $changed_sockets = $this->allsockets;
  
      $num_sockets = socket_select($changed_sockets,$write=NULL,$exceptions=NULL,NULL);
  
      foreach( $changed_sockets as $socket )
      {
        if( $socket == $this->master )
        {
          if( ($client=socket_accept($this->master)) < 0 )
          {
            console('socket_accept() erreur : '. socket_strerror(socket_last_error($client)));
            continue;
          }
          else
          {
            $this->allsockets[] = $client;
            $socket_index = array_search($client,$this->allsockets);
            $this->clients[$socket_index] = new stdClass;
            $this->clients[$socket_index]->socket_id = $client;
  
            self::console($client . ' connecté!');
          }
        }
        else
        {
          $socket_index = array_search($socket,$this->allsockets);
  
          $bytes = socket_recv($socket,$buffer,2048,0);
          
          if( $bytes === 0 )
            self::disconnected($socket);
  
          else
          {
            if( !isset($this->handshakes[$socket_index]) )
            {
              self::do_handshake($buffer,$socket,$socket_index);
              self::send($socket,'connexion');
            }
            else
            {
              if(!$action = substr($buffer,1,$bytes-2))
                self::disconnected($socket);
  
              else
              {
                self::console('< '.$action);
                
                $action = str_replace(array(' ','\'','-',','),'_',$action);
                if( method_exists('trigger',$action) )
                  self::send($socket,trigger::$action());
              }
            }
          }
        }
      }
    }
  }
  
  private function do_handshake($buffer,$socket,$socket_index)
  {
    self::console('Requete handshake...');
  
    list($resource,$host,$origin,$key1,$key2,$l8b) = self::getheaders($buffer);
  
    self::console('Handshaking...');
    
    $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" . self::calcKey($key1,$key2,$l8b) . "\r\n";
          
    $this->handshakes[$socket_index] = true;
  
    socket_write($socket,$upgrade.chr(0),strlen($upgrade.chr(0)));
  
    self::console('Handshaking effectué...');
  }
  
  private static function calcKey($key1,$key2,$l8b)
  {
    preg_match_all('/([\d]+)/', $key1, $key1_num);
    preg_match_all('/([\d]+)/', $key2, $key2_num);
    
    $key1_num = implode($key1_num[0]);
    $key2_num = implode($key2_num[0]);
    
    preg_match_all('/([ ]+)/', $key1, $key1_spc);
    preg_match_all('/([ ]+)/', $key2, $key2_spc);
    
    $key1_spc = strlen(implode($key1_spc[0]));
    $key2_spc = strlen(implode($key2_spc[0]));
    
    if( $key1_spc==0|$key2_spc==0 )
    {
      self::console('Key invalide');
      return false;
    }
    
    $key1_sec = pack("N",$key1_num / $key1_spc);
    $key2_sec = pack("N",$key2_num / $key2_spc);
    
    return md5($key1_sec.$key2_sec.$l8b,1);
  }
  
  private function disconnected($socket)
  {
    if( $index = array_search($socket, $this->allsockets) )
      unset($this->allsockets[$index], $this->clients[$index], $this->handshakes[$index]);
  
    socket_close($socket);
    self::console($socket.' deconnexion');
  }
  
  private static 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-Key1: (.*)\r\n/",$req,$match))
      $sk1 = $match[1];
    if(preg_match("/Sec-WebSocket-Key2: (.*)\r\n/",$req,$match))
      $sk2 = $match[1];
    if($match=substr($req,-8))
      $l8b = $match;
      
    return array($r, $h, $o, $sk1, $sk2, $l8b );
  }
}
?>


trigger.class.php

Ce fichier quant à lui est présent pour la démonstration, il exécutera des méthodes selon les appels effectué via le fichier client.php. Si dans le input on tape bonjour, c'est la méthode bonjour() qui sera exécutée. Par contre si on tape salut vu qu'on a aucune méthode de ce nom, aucun retour ne sera fait.

Vous pouvez en créer autant que vous le souhaitez.

<?php
class trigger extends socketWebSocket
{
    protected static function bonjour()
    {
      return 'Bonjour, comment allez vous ?';
    }
    
    protected static function nom()
    {
      return 'Mon nom est alban';
    }  
    
    protected static function date()
    {
      return date('d-m-Y');
    }  
    
    protected static function age()
    {
      return 'J\'ai 28 ans';
    }  
    
    protected static function as_tu_l_heure()
    {
      return date('H:i:s');
    }      
}
?>


Téléchargement

Pour télécharger la totalité du script , rendez vous à cette adresse :

Script entier



  
4 commentaires

Super tuto !

le 20 janvier 2011

Une introduction serait pas un plus.

Et puis franchement j'aime pas cette technologie ! bien trop fragile à mon gout, et qu'on est pas encore sur des futurs navigateurs qui devrait le supporter.

- Chrome (si la faille et corrigé sinon bye)

- firefox et opéra (continue a ce que j'ai entendu)

D'ailleurs niveau sécurité l'ont ils corrigés ?

ps: il se peux que je ne suis plus à jour .

le 20 janvier 2011

On est a la version 0.75 je crois et effectivement une faille de sécu est présente sur des injections via le cache ou un truc du genre. La version 0.76 sera je pense débuggué par contre il est sur tous les navigateur sauf IE mais il est actuellement desactivé par défaut. cette technologie est l'avenir du web qu'on le veuille ou non puisqu'on s'oriente vers de l'application.

le 20 janvier 2011

D'accord, merci pour l'info.

Hum... Personnellement ça se discute vu l'arrivé du html5 (c'est encore à l'état de brouillon + le temps que les webmaster se mette à jour ...). Il est encore bien trop tot pour dire si celui-ci est l'avenir. Vu comment est l'évolution en générale ... Tout à le temps de changer même si je suis d'accord qu'un jour ce n'est plus d'un site web mais d'une appli qu'on parlera

Pour cette version peut être mais google a affirmé (faut que je retrouve ça) qu'il fallait que dans la prochaine version cela soit corrigé !

le 20 janvier 2011



Ajoute un commentaire !

Ajouter une image... Trouvée sur internet » De mon PC »
Adresse URL :
Adresse de la page de la vidéo :
Taille du texte :
Couleur du texte :

Article lu 5438 fois.