Czy są jakieś samouczki lub przewodniki pokazujące, jak samodzielnie napisać prosty serwer WebSockets w PHP? Szukałem go w Google, ale nie znalazłem wielu. Znalazłem phpwebsockets, ale jest już nieaktualne i nie obsługuje najnowszego protokołu. Próbowałem zaktualizować go samodzielnie, ale nie działa.
#!/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;
}
?>
a klient:
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);
};
Jeśli coś jest nie tak w moim kodzie, czy możesz mi pomóc to naprawić? Mówi Concole w FirefoksieFirefox can't establish a connection to the server at ws://localhost:12345/.
EDYCJA
Ponieważ zainteresowanie tym pytaniem jest duże, zdecydowałem się przedstawić to, co w końcu wymyśliłem. Oto mój pełny kod.
php
javascript
websocket
Dharman
źródło
źródło
Odpowiedzi:
Byłem na tej samej łodzi co ty ostatnio i oto co zrobiłem:
Użyłem phpwebsockets kod jako punkt odniesienia dla jak struktura kodu po stronie serwera. (Wygląda na to, że już to robisz i jak zauważyłeś, kod w rzeczywistości nie działa z różnych powodów).
Użyłem PHP.net, aby przeczytać szczegółowe informacje o każdej funkcji gniazda używanej w kodzie phpwebsockets. Robiąc to, w końcu byłem w stanie zrozumieć, jak cały system działa koncepcyjnie. To była dość duża przeszkoda.
Przeczytałem aktualną wersję roboczą WebSocket . Musiałem to przeczytać kilka razy, zanim w końcu zaczęło się docierać. Prawdopodobnie będziesz musiał wracać do tego dokumentu wielokrotnie w trakcie całego procesu, ponieważ jest to jedyny ostateczny zasób z poprawnymi, aktualnymi informacje o interfejsie API WebSocket.
Zakodowałem poprawną procedurę uzgadniania w oparciu o instrukcje w szkicu w # 3. To nie było takie złe.
Po uzgodnieniu nadal otrzymywałem pakiet zniekształconych tekstów wysyłanych od klientów na serwer i nie mogłem zrozumieć, dlaczego, dopóki nie zdałem sobie sprawy, że dane są zakodowane i muszą zostać zdemaskowane. Poniższy link bardzo mi tutaj pomógł: (
oryginalny link uszkodzony) Kopia zarchiwizowana .Pamiętaj, że kod dostępny pod tym linkiem ma wiele problemów i nie będzie działał poprawnie bez dalszych modyfikacji.
Następnie natrafiłem na następujący wątek SO, który jasno wyjaśnia, jak prawidłowo kodować i dekodować wiadomości przesyłane tam iz powrotem: Jak mogę wysyłać i odbierać wiadomości WebSocket po stronie serwera?
Ten link był naprawdę pomocny. Zalecam skonsultowanie się z nim podczas przeglądania wersji roboczej WebSocket. Pomoże to nadać sens temu, co mówi szkic.
Na tym etapie prawie skończyłem, ale miałem pewne problemy z aplikacją WebRTC, którą tworzyłem za pomocą WebSocket, więc w końcu zadałem własne pytanie na temat SO, które ostatecznie rozwiązałem: Jakie są te dane na końcu informacji o kandydatach WebRTC?
W tym momencie prawie wszystko działało. Musiałem tylko dodać dodatkową logikę do obsługi zamykania połączeń i gotowe.
Cały proces zajął mi około dwóch tygodni. Dobra wiadomość jest taka, że teraz naprawdę dobrze rozumiem WebSocket i mogłem stworzyć od podstaw własne skrypty klienta i serwera, które działają świetnie. Mamy nadzieję, że zwieńczenie wszystkich tych informacji da ci wystarczające wskazówki i informacje do zakodowania własnego skryptu PHP WebSocket.
Powodzenia!
Edycja : ta zmiana nastąpiła kilka lat po mojej oryginalnej odpowiedzi i chociaż nadal mam działające rozwiązanie, nie jest ono naprawdę gotowe do udostępnienia. Na szczęście ktoś inny na GitHubie ma prawie identyczny kod jak mój (ale dużo czystszy), więc polecam użycie następującego kodu dla działającego rozwiązania PHP WebSocket:
https://github.com/ghedipunk/PHP-Websockets/blob/master/ websockets.php
Edycja nr 2 : Chociaż nadal lubię używać PHP do wielu rzeczy związanych z serwerem, muszę przyznać, że ostatnio bardzo rozgrzałem się do Node.js, a głównym powodem jest to, że jest lepiej zaprojektowany od gotowe do obsługi WebSocket niż PHP (lub jakikolwiek inny język po stronie serwera). W związku z tym niedawno odkryłem, że o wiele łatwiej jest skonfigurować zarówno Apache / PHP, jak i Node.js na serwerze i używać Node.js do uruchamiania serwera WebSocket i Apache / PHP do wszystkiego innego. A w przypadku, gdy korzystasz ze współdzielonego środowiska hostingowego, w którym nie możesz zainstalować / używać Node.js dla WebSocket, możesz skorzystać z bezpłatnej usługi, takiej jak Herokuaby skonfigurować serwer Node.js WebSocket i wysyłać do niego zapytania międzydomenowe z serwera. Po prostu upewnij się, że robisz to, aby ustawić serwer WebSocket tak, aby mógł obsługiwać żądania między źródłami.
źródło
O ile mi wiadomo, Ratchet to najlepsze dostępne obecnie rozwiązanie PHP WebSocket. A ponieważ jest to oprogramowanie typu open source , możesz zobaczyć, jak autor zbudował to rozwiązanie WebSocket przy użyciu PHP.
źródło
Dlaczego nie użyć gniazd http://uk1.php.net/manual/en/book.sockets.php ? Jest dobrze udokumentowany (nie tylko w kontekście PHP) i ma dobre przykłady http://uk1.php.net/manual/en/sockets.examples.php
źródło
Musisz przekonwertować klucz z hex na dec przed kodowaniem base64_encoding, a następnie wysłać go do uzgadniania.
$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";
Daj mi znać, jeśli to pomoże.
źródło
Byłem w twoich butach przez jakiś czas i ostatecznie skończyłem na node.js, ponieważ może on tworzyć rozwiązania hybrydowe, takie jak posiadanie serwera WWW i gniazda w jednym. Tak więc backend php może wysyłać żądania przez http do serwera WWW węzła, a następnie rozgłaszać je za pomocą websocket. Bardzo skuteczny sposób.
źródło
<?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); } }
z jednego terminala uruchom: php server.php
z innego terminala uruchom: echo "hello woerld" | nc 127.0.0.1 8002
źródło