Comment créer un serveur websockets en PHP

y a-t-il des tutoriels ou des guides qui montrent comment écrire moi-même un serveur websockets simple en PHP? J'ai essayé de le chercher sur google mais je n'en ai pas trouvé beaucoup. J'ai trouvé phpwebsockets mais il est désuet maintenant et ne supporte pas le protocole le plus récent. J'ai essayé de le mettre à jour moi-même, mais ça n'a pas l'air de marcher.

#!/php -q
<?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."nn";
  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) 
  list($resource,$host,$u,$c,$key,$protocol,$version,$origin,$data) = getheaders($buffer);
  console("Handshaking...");

    $acceptkey = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
  $upgrade  = "HTTP/1.1 101 Switching ProtocolsrnUpgrade: websocketrnConnection: UpgradernSec-WebSocket-Accept: $acceptkeyrn";

  socket_write($user->socket,$upgrade,strlen($upgrade));
  $user->handshake=true;
  console($upgrade);
  console("Done handshaking...");
  return true;
}

function getheaders($req){
    $r=$h=$u=$c=$key=$protocol=$version=$o=$data=null;
    if(preg_match("/GET (.*) HTTP/"   ,$req,$match)){ $r=$match[1]; }
    if(preg_match("/Host: (.*)rn/"  ,$req,$match)){ $h=$match[1]; }
    if(preg_match("/Upgrade: (.*)rn/",$req,$match)){ $u=$match[1]; }
    if(preg_match("/Connection: (.*)rn/",$req,$match)){ $c=$match[1]; }
    if(preg_match("/Sec-WebSocket-Key: (.*)rn/",$req,$match)){ $key=$match[1]; }
    if(preg_match("/Sec-WebSocket-Protocol: (.*)rn/",$req,$match)){ $protocol=$match[1]; }
    if(preg_match("/Sec-WebSocket-Version: (.*)rn/",$req,$match)){ $version=$match[1]; }
    if(preg_match("/Origin: (.*)rn/",$req,$match)){ $o=$match[1]; }
    if(preg_match("/rn(.*?)$/",$req,$match)){ $data=$match[1]; }
    return array($r,$h,$u,$c,$key,$protocol,$version,$o,$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;
}

?>

et le client:

var connection = new WebSocket('ws://localhost:12345');
connection.onopen = function () {
  connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
  console.log('Server: ' + e.data);
};

S'il y a un problème dans mon code, pouvez-vous m'aider à le corriger? Concole dans firefox dit Firefox can't establish a connection to the server at ws://localhost:12345/.

MODIFIER

Comme cette question suscite beaucoup d'intérêt, j'ai décidé de vous fournir ce que j'ai finalement trouvé. Voici mon code complet.

63
demandé sur Dharman 2013-01-25 02:42:14

5 réponses

j'étais dans le même bateau que vous récemment, et voici ce que j'ai fait:

1) j'ai utilisé le code phpwebsockets comme référence pour la structure du code côté serveur. (Vous semblez déjà le faire, et comme vous l'avez noté, le code ne fonctionne pas réellement pour une variété de raisons.)

2) j'ai utilisé PHP.net pour lire les détails sur chaque fonction de socket utilisée dans le code phpwebsockets. En faisant cela, j'ai finalement pu comprendre comment l'ensemble le système fonctionne conceptuellement. Ce fut un très grand obstacle.

3) j'ai lu le draft WebSocket actuel (s'il vous plaît faire une recherche sur le web, car je ne peux pas poster plus de deux liens par poste). J'ai dû le lire un tas de fois avant que ça commence à couler. Vous devrez probablement revenir à ce document encore et encore tout au long du processus, car il s'agit de la seule ressource définitive avec des informations correctes et à jour sur L'API WebSocket.

4) J'ai codé la procédure de poignée de main appropriée basée sur les instructions dans le brouillon #3. Ce n'était pas trop mauvais.

5) j'ai continué à recevoir un tas de texte brouillé envoyé par les clients au serveur après la poignée de main et je ne pouvais pas comprendre pourquoi jusqu'à ce que je réalise que les données sont encodées et doivent être démasquées. Le lien suivant m'a beaucoup aidé ici: http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets /

veuillez noter que le code disponible sur ce lien a un certain nombre de problèmes et ne fonctionnera pas correctement sans autre modification.

6) j'ai alors rencontré le fil SO suivant, qui explique clairement comment coder et décoder correctement les messages envoyés de part et d'autre: Comment puis-je envoyer et recevoir des messages WebSocket du côté du serveur?

ce lien a été très utile. Je recommande de le consulter tout en regardant au repêchage de WebSocket. Cela aidera à donner plus de sens à ce que le projet dit.

7) j'ai presque fini à ce moment, mais j'ai eu quelques problèmes avec une application WebRTC que je faisais en utilisant WebSocket, donc j'ai fini par poser ma propre question sur SO, que j'ai finalement résolu. Pour faire référence à la question et à la réponse, veuillez faire une recherche sur le web pour "Alors quelles sont ces données à la fin de WebRTC information candidat?"(sans les guillemets).

8) à ce point, je pretty beaucoup de choses ont tout de travail. J'ai juste dû ajouter de la logique supplémentaire pour gérer la fermeture des connexions, et j'ai été fait.

ce processus m'a pris environ deux semaines. La bonne nouvelle est que je comprends vraiment bien WebSocket maintenant et j'ai été en mesure de faire mon propre client et le serveur scripts à partir de zéro qui fonctionnent très bien. Espérons que le point culminant de toutes ces informations vous donnera assez de conseils et d'informations pour coder votre propre script PHP WebSocket. Bonne chance!

Edit : cette édition est quelques années après ma réponse originale, et bien que j'ai encore une solution de travail, il n'est pas vraiment prêt pour le partage. Heureusement, quelqu'un d'autre sur GitHub a presque le même code que le mien (mais beaucoup plus propre), donc je recommande d'utiliser le code suivant pour une solution de WebSocket PHP qui fonctionne:

https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php

Edit #2 : alors que J'aime toujours utiliser PHP pour beaucoup de choses liées côté serveur, je dois admettre que j'ai vraiment réchauffé Jusqu'à Noeud.js beaucoup récemment, et la principale raison est parce que c'est mieux conçu à partir du sol en place pour gérer les WebSocket que PHP (ou tout autre langage côté serveur). En tant que tel, j'ai récemment découvert qu'il est beaucoup plus facile de configurer à la fois Apache/PHP et Node.js sur votre serveur et utiliser le noeud.js pour exécuter le serveur WebSocket et Apache / PHP pour tout le reste. Et dans le cas où vous êtes sur un environnement d'hébergement partagé dans lequel vous ne pouvez pas installer/utiliser le noeud.js pour WebSocket, vous pouvez utiliser un service gratuit comme Heroku pour configurer un Nœud.js WebSocket server et de faire des requêtes inter-domaines à partir de votre serveur. Assurez-vous juste que si vous faites cela pour configurer votre serveur WebSocket pour être en mesure de traiter les requêtes d'origine croisée.

75
répondu HartleySan 2017-05-23 12:10:37

autant que je sache Ratchet est la meilleure solution WebSocket PHP disponible à l'heure actuelle. Et puisque c'est open source vous pouvez voir comment l'auteur a construit cette solution WebSocket en utilisant PHP.

19
répondu leggetter 2013-01-25 11:27:04

pourquoi ne pas utiliser les douilles http://uk1.php.net/manual/en/book.sockets.php ? Il est bien documenté (pas seulement dans le contexte de PHP) et a de bons exemples http://uk1.php.net/manual/en/sockets.examples.php

6
répondu Lukasz Kujawa 2013-01-24 23:09:38

doit convertir la clé de hex en dec avant base64_encoding et ensuite l'Envoyer pour poignée de main.

$hashedKey = sha1($key. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true);

$rawToken = "";
    for ($i = 0; $i < 20; $i++) {
      $rawToken .= chr(hexdec(substr($hashedKey,$i*2, 2)));
    }
$handshakeToken = base64_encode($rawToken) . "\r\n";

$handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken\r\n";

dites-moi si ça peut aider.

0
répondu user2288650 2016-12-11 09:12:01
<?php

// server.php

$server = stream_socket_server("tcp://127.0.0.1:8001", $errno, $errorMessage);

if($server == false) {
    throw new Exception("Could not bind to socket: $errorMessage");

}

for(;;) {
    $client = @stream_socket_accept($server);

    if($client) {
        stream_copy_to_stream($client, $client);
        fclose($client);
    }
}

à partir d'un terminal lancer : php server.php

depuis un autre terminal: echo "hello woerld" / nc 127.0.0.1 8002

-4
répondu techexpander.com 2013-11-30 15:21:10