¿Hay tutoriales o guías que muestren cómo escribir yo mismo un servidor websockets simple en PHP? Intenté buscarlo en Google pero no encontré muchos. Encontré phpwebsockets pero ahora está desactualizado y no es compatible con el protocolo más nuevo. Intenté actualizarlo yo mismo pero no parece funcionar.
#!/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;
}
?>
y el cliente:
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);
};
Si hay algo incorrecto en mi código, ¿pueden ayudarme a solucionarlo? Concole en firefox diceFirefox can't establish a connection to the server at ws://localhost:12345/.
EDITAR
Dado que hay mucho interés en esta pregunta, decidí brindarles lo que finalmente se me ocurrió. Aquí está mi código completo.
php
javascript
websocket
Dharman
fuente
fuente
Respuestas:
Estuve en el mismo barco que tú recientemente, y esto es lo que hice:
He utilizado el phpwebsockets código como una referencia para la forma de estructurar el código del lado del servidor. (Parece que ya está haciendo esto y, como señaló, el código en realidad no funciona por una variedad de razones).
Usé PHP.net para leer los detalles sobre cada función de socket utilizada en el código phpwebsockets. Al hacer esto, finalmente pude entender cómo funciona todo el sistema conceptualmente. Este fue un gran obstáculo.
Leí el borrador real de WebSocket . Tuve que leer esto varias veces antes de que finalmente comenzara a asimilarlo. Es probable que tenga que volver a este documento una y otra vez durante todo el proceso, ya que es el único recurso definitivo con información correcta y actualizada. información sobre la API de WebSocket.
Codifiqué el procedimiento de apretón de manos adecuado según las instrucciones del borrador en el n. ° 3. Esto no estuvo tan mal.
Seguí recibiendo un montón de texto confuso enviado de los clientes al servidor después del apretón de manos y no podía entender por qué hasta que me di cuenta de que los datos están codificados y deben desenmascararse. El siguiente enlace me ayudó mucho aquí: (
enlace original roto) Copia archivada .Tenga en cuenta que el código disponible en este enlace tiene varios problemas y no funcionará correctamente sin más modificaciones.
Luego me encontré con el siguiente hilo SO, que explica claramente cómo codificar y decodificar correctamente los mensajes que se envían de un lado a otro: ¿Cómo puedo enviar y recibir mensajes de WebSocket en el lado del servidor?
Este enlace fue realmente útil. Recomiendo consultarlo mientras mira el borrador de WebSocket. Ayudará a darle más sentido a lo que dice el borrador.
Casi había terminado en este punto, pero tuve algunos problemas con una aplicación WebRTC que estaba creando usando WebSocket, así que terminé haciendo mi propia pregunta sobre SO, que finalmente resolví: ¿Qué son estos datos al final de la información del candidato de WebRTC?
En este punto, prácticamente lo tenía todo funcionando. Solo tuve que agregar algo de lógica adicional para manejar el cierre de conexiones, y estaba listo.
Ese proceso me llevó unas dos semanas en total. La buena noticia es que ahora entiendo muy bien WebSocket y pude crear mis propios scripts de cliente y servidor desde cero que funcionan muy bien. Es de esperar que la culminación de toda esa información le proporcione suficiente orientación e información para codificar su propio script PHP de WebSocket.
¡Buena suerte!
Editar : esta edición es un par de años después de mi respuesta original, y aunque todavía tengo una solución funcional, no está realmente lista para compartir. Afortunadamente, alguien más en GitHub tiene un código casi idéntico al mío (pero mucho más limpio), por lo que recomiendo usar el siguiente código para una solución PHP WebSocket que funcione:
https://github.com/ghedipunk/PHP-Websockets/blob/master/ websockets.php
Edite # 2 : si bien todavía disfruto usando PHP para muchas cosas relacionadas con el lado del servidor, debo admitir que realmente me he acostumbrado mucho a Node.js recientemente, y la razón principal es porque está mejor diseñado desde el para manejar WebSocket que PHP (o cualquier otro lenguaje del lado del servidor). Como tal, descubrí recientemente que es mucho más fácil configurar Apache / PHP y Node.js en su servidor y usar Node.js para ejecutar el servidor WebSocket y Apache / PHP para todo lo demás. Y en el caso de que se encuentre en un entorno de alojamiento compartido en el que no pueda instalar / usar Node.js para WebSocket, puede usar un servicio gratuito como Herokupara configurar un servidor WebSocket Node.js y realizar solicitudes entre dominios desde su servidor. Solo asegúrese de hacer eso para configurar su servidor WebSocket para poder manejar solicitudes de origen cruzado.
fuente
Hasta donde yo sé, Ratchet es la mejor solución PHP WebSocket disponible en este momento. Y dado que es de código abierto , puede ver cómo el autor ha construido esta solución WebSocket usando PHP.
fuente
¿Por qué no utilizar sockets http://uk1.php.net/manual/en/book.sockets.php ? Está bien documentado (no solo en contexto PHP) y tiene buenos ejemplos http://uk1.php.net/manual/en/sockets.examples.php
fuente
Necesita convertir la clave de hexadecimal a dec antes de codificación base64 y luego enviarla para el protocolo de enlace.
$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";
Déjeme saber si esto ayuda.
fuente
Estuve en tu lugar por un tiempo y finalmente terminé usando node.js, porque puede hacer soluciones híbridas como tener un servidor web y un servidor de socket en uno. Entonces, el backend php puede enviar solicitudes a través de http al servidor web del nodo y luego transmitirlas con websocket. Una forma muy eficaz de hacerlo.
fuente
<?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); } }
desde una terminal ejecute: php server.php
desde otra terminal ejecute: echo "hello woerld" | nc 127.0.0.1 8002
fuente