Existem tutoriais ou guias mostrando como escrever para mim mesmo um servidor websockets simples em PHP? Tentei procurá-lo no google, mas não encontrei muitos. Encontrei phpwebsockets, mas agora está desatualizado e não oferece suporte ao protocolo mais recente. Eu tentei atualizá-lo sozinho, mas não 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;
}
?>
e o 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);
};
Se houver algo errado em meu código, você pode me ajudar a consertar? Concole no firefox dizFirefox can't establish a connection to the server at ws://localhost:12345/.
EDITAR
Como há muito interesse nesta questão, decidi fornecer a vocês o que eu finalmente descobri. Aqui está meu código completo.
php
javascript
websocket
Dharman
fonte
fonte
Respostas:
Eu estava no mesmo barco que você recentemente e aqui está o que fiz:
Usei o código phpwebsockets como uma referência de como estruturar o código do lado do servidor. (Parece que você já está fazendo isso e, como observou, o código na verdade não funciona por vários motivos.)
Usei o PHP.net para ler os detalhes sobre cada função de soquete usada no código do phpwebsockets. Ao fazer isso, finalmente consegui entender como todo o sistema funciona conceitualmente. Este foi um grande obstáculo.
Eu li o rascunho real do WebSocket . Eu tive que ler isso um monte de vezes antes de finalmente começar a entender. Você provavelmente terá que voltar a este documento várias vezes ao longo do processo, pois é o único recurso definitivo com dados corretos e atualizados informações sobre a API WebSocket.
Codifiquei o procedimento de handshake adequado com base nas instruções do rascunho no item 3. Isso não foi tão ruim.
Continuei recebendo um monte de texto distorcido enviado dos clientes para o servidor após o aperto de mão e não consegui descobrir o motivo até perceber que os dados estão codificados e devem ser desmascarados. O link a seguir me ajudou muito aqui: (
link original quebrado) Cópia arquivada .Observe que o código disponível neste link tem vários problemas e não funcionará corretamente sem outras modificações.
Então me deparei com o seguinte segmento de SO, que explica claramente como codificar e decodificar adequadamente as mensagens enviadas e recebidas: Como posso enviar e receber mensagens WebSocket no servidor?
Este link foi muito útil. Recomendo consultá-lo enquanto analisa o rascunho do WebSocket. Isso ajudará a dar mais sentido ao que o rascunho diz.
Eu estava quase terminando neste ponto, mas tive alguns problemas com um aplicativo WebRTC que estava fazendo usando WebSocket, então acabei fazendo minha própria pergunta no SO, que eventualmente resolvi: Quais são esses dados no final das informações do candidato WebRTC?
Nesse ponto, eu tinha praticamente tudo funcionando. Eu só tinha que adicionar alguma lógica adicional para lidar com o fechamento de conexões, e estava feito.
Esse processo levou cerca de duas semanas no total. A boa notícia é que agora entendo muito bem o WebSocket e pude fazer meus próprios scripts de cliente e servidor do zero que funcionam muito bem. Esperançosamente, o culminar de todas essas informações lhe dará orientação e informações suficientes para codificar seu próprio script WebSocket PHP.
Boa sorte!
Edit : Esta edição é alguns anos após minha resposta original e, embora eu ainda tenha uma solução de trabalho, ela não está realmente pronta para ser compartilhada. Felizmente, outra pessoa no GitHub tem código quase idêntico ao meu (mas muito mais limpo), então recomendo usar o seguinte código para uma solução PHP WebSocket funcional:
https://github.com/ghedipunk/PHP-Websockets/blob/master/ websockets.php
Edit # 2 : Embora eu ainda goste de usar PHP para muitas coisas relacionadas ao lado do servidor, tenho que admitir que realmente me familiarizei com o Node.js recentemente, e o principal motivo é porque ele é melhor projetado para lidar com WebSocket em vez de PHP (ou qualquer outra linguagem do lado do servidor). Como tal, descobri recentemente que é muito mais fácil configurar Apache / PHP e Node.js em seu servidor e usar Node.js para executar o servidor WebSocket e Apache / PHP para todo o resto. E no caso em que você está em um ambiente de hospedagem compartilhada em que não pode instalar / usar Node.js para WebSocket, você pode usar um serviço gratuito como Herokupara configurar um servidor Node.js WebSocket e fazer solicitações entre domínios a partir de seu servidor. Apenas certifique-se de fazer isso para configurar seu servidor WebSocket para poder lidar com solicitações de origem cruzada.
fonte
Tanto quanto eu sei, Ratchet é a melhor solução PHP WebSocket disponível no momento. E como é de código aberto, você pode ver como o autor construiu essa solução WebSocket usando PHP.
fonte
Por que não usar sockets http://uk1.php.net/manual/en/book.sockets.php ? Está bem documentado (não apenas no contexto de PHP) e tem bons exemplos http://uk1.php.net/manual/en/sockets.examples.php
fonte
É necessário converter a chave de hex para dec antes de base64_encoding e enviá-la para handshake.
$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";
Avise-me se isso ajudar.
fonte
Eu estava no seu lugar por um tempo e finalmente acabei usando node.js, porque ele pode fazer soluções híbridas como ter um servidor web e socket em um. Assim, o backend php pode enviar solicitações através de http para o servidor web do nó e então transmiti-lo com websocket. Maneira muito eficiente de ir.
fonte
<?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); } }
de um terminal executado: php server.php
de outro terminal executado: echo "hello woerld" | nc 127.0.0.1 8002
fonte