it-swarm.com.de

So erstellen Sie einen Websockets-Server in PHP

Gibt es Tutorials oder Anleitungen, wie man einen einfachen Websockets-Server in PHP schreibt? Ich habe es auf Google gesucht, aber ich habe nicht viele gefunden. Ich habe phpwebsockets gefunden, aber es ist jetzt veraltet und unterstützt das neueste Protokoll nicht. Ich habe versucht, es selbst zu aktualisieren, aber es scheint nicht zu funktionieren. 

#!/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."\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) 
  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 Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $acceptkey\r\n";

  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: (.*)\r\n/"  ,$req,$match)){ $h=$match[1]; }
    if(preg_match("/Upgrade: (.*)\r\n/",$req,$match)){ $u=$match[1]; }
    if(preg_match("/Connection: (.*)\r\n/",$req,$match)){ $c=$match[1]; }
    if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key=$match[1]; }
    if(preg_match("/Sec-WebSocket-Protocol: (.*)\r\n/",$req,$match)){ $protocol=$match[1]; }
    if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/",$req,$match)){ $version=$match[1]; }
    if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
    if(preg_match("/\r\n(.*?)\$/",$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;
}

?>

und der Kunde: 

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);
};

Wenn mein Code fehlerhaft ist, können Sie mir helfen, den Fehler zu beheben? Concole in Firefox sagt Firefox can't establish a connection to the server at ws://localhost:12345/.

EDIT
Da es großes Interesse an dieser Frage gibt, habe ich mich entschlossen, Ihnen mitzuteilen, was ich schließlich gefunden habe. Hier ist mein vollständiger Code.

67
Dharman

Ich war in demselben Boot wie Sie vor kurzem und hier habe ich getan:

1) Ich habe den phpwebsockets-Code als Referenz für die Strukturierung des serverseitigen Codes verwendet. (Sie scheinen dies bereits zu tun, und wie Sie bereits festgestellt haben, funktioniert der Code aus verschiedenen Gründen nicht.)

2) Ich habe PHP.net verwendet, um die Details zu jeder im phpwebsockets-Code verwendeten Socketfunktion zu lesen. Dadurch konnte ich endlich verstehen, wie das gesamte System konzeptionell funktioniert. Das war eine ziemlich große Hürde.

3) Ich habe den eigentlichen WebSocket-Entwurf gelesen (bitte führen Sie eine Websuche durch, da ich nicht mehr als zwei Links pro Post veröffentlichen kann). Ich musste dieses Ding ein paar Mal lesen, bevor es endlich einsickerte. Sie werden wahrscheinlich während des gesamten Prozesses immer wieder auf dieses Dokument zurückgreifen müssen, da es die einzige Ressource mit der richtigen Aktualität ist Informationen zur WebSocket-API.

4) Ich habe das richtige Handshake-Verfahren basierend auf den Anweisungen im Entwurf in # 3 codiert. Das war nicht so schlimm.

5) Ich bekam nach dem Handshake immer wieder eine Menge verstümmelten Textes von den Clients an den Server. Ich konnte den Grund dafür nicht herausfinden, bis ich merkte, dass die Daten verschlüsselt sind und demaskiert werden müssen. Der folgende Link hat mir hier sehr geholfen: http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/

Bitte beachten Sie, dass der unter diesem Link verfügbare Code eine Reihe von Problemen aufweist und ohne weitere Änderungen nicht ordnungsgemäß funktioniert.

6) Ich bin dann auf den folgenden SO - Thread gestoßen, der klar erklärt, wie man hin und her gesendete Nachrichten ordnungsgemäß codiert und decodiert: Wie kann ich WebSocket-Nachrichten auf der Serverseite senden und empfangen?

Dieser Link war sehr hilfreich. Ich empfehle, es zu lesen, während Sie den WebSocket-Entwurf betrachten. Dies wird dazu beitragen, das, was der Entwurf sagt, besser zu verstehen.

7) Ich war zu diesem Zeitpunkt fast fertig, hatte aber einige Probleme mit einer WebRTC-App, die ich mit WebSocket erstellte. Daher stellte ich meine eigene Frage zu SO, die ich schließlich gelöst habe. Um auf die Frage und Antwort zu verweisen, führen Sie eine Websuche nach "SO Was sind diese Daten am Ende der WebRTC-Kandidateninformationen?" (ohne Anführungszeichen).

8) Zu diesem Zeitpunkt hatte ich so ziemlich alles am Laufen. Ich musste nur eine zusätzliche Logik hinzufügen, um das Schließen von Verbindungen zu handhaben, und ich war fertig.

Dieser Vorgang hat insgesamt ungefähr zwei Wochen gedauert. Die gute Nachricht ist, dass ich WebSocket jetzt sehr gut verstehe und ich meine eigenen Client- und Server-Skripts von Grund auf erstellen konnte, die gut funktionieren. Hoffentlich gibt Ihnen der Höhepunkt all dieser Informationen ausreichend Anleitung und Informationen, um Ihre eigenen zu kodieren WebSocket PHP Skript . Viel Glück!

Edit: Diese Bearbeitung ist ein paar Jahre nach meiner ursprünglichen Antwort, und obwohl ich immer noch eine funktionierende Lösung habe, ist sie nicht wirklich zum Teilen bereit. Glücklicherweise hat jemand anderes auf GitHub fast identischen Code wie ich (aber viel sauberer). Daher empfehle ich den folgenden Code für eine funktionierende PHP - WebSocket-Lösung:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php

Edit # 2: Obwohl es mir immer noch Spaß macht, PHP für viele serverseitige Dinge zu verwenden, muss ich zugeben, dass ich mich in letzter Zeit sehr viel mit Node.js aufgewärmt habe Grund dafür ist, dass es besser von Grund auf für die Handhabung von WebSocket ausgelegt ist als PHP (oder eine andere serverseitige Sprache). Daher habe ich kürzlich festgestellt, dass es viel einfacher ist, sowohl Apache/PHP als auch Node.js auf Ihrem Server einzurichten und Node.js zum Ausführen des WebSocket-Servers und Apache/PHP für alles andere zu verwenden. Wenn Sie sich in einer gemeinsam genutzten Hosting-Umgebung befinden, in der Sie Node.js für WebSocket nicht installieren/verwenden können, können Sie einen kostenlosen Dienst wie Heroku verwenden, um einen Node.js-WebSocket-Server einzurichten und domänenübergreifend zu erstellen Anfragen von Ihrem Server an. Stellen Sie einfach sicher, dass Sie Ihren WebSocket-Server so einrichten, dass er Cross-Origin-Anforderungen verarbeiten kann.

84
HartleySan

Soweit mir bekannt ist, ist Ratchet die derzeit beste PHP WebSocket-Lösung. Und da es open source ist, können Sie sehen, wie der Autor diese WebSocket-Lösung mit PHP erstellt hat.

19
leggetter

Warum nicht Sockets http://uk1.php.net/manual/de/book.sockets.php verwenden? Es ist gut dokumentiert (nicht nur im PHP -Kontext) und hat gute Beispiele http://uk1.php.net/manual/de/sockets.examples.php

6
Lukasz Kujawa

Ich war eine Weile in Ihren Füßen und habe schließlich mit node.js gearbeitet, weil es hybride Lösungen wie Web- und Socket-Server in einem Server gibt. So kann das php-Backend Anforderungen über http an den Webserver des Knotens senden und dann über Websocket übertragen. Sehr effektiver Weg.

0
M.Z.

Müssen Sie den Schlüssel von hex in dec vor base64_encoding konvertieren und ihn dann zum Handshake senden.

$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";

Lassen Sie mich wissen, ob das hilft. 

0
user2288650