如何在PHP中创buildwebsockets服务器

是否有任何教程或指南显示如何在PHP中编写简单的websockets服务器? 我试图在谷歌上寻找它,但我没有find很多。 我发现phpwebsockets,但现在已经过时,不支持最新的协议。 我试图自己更新它,但似乎并没有工作。

#!/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("Ymd")); 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('Ymd 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; } ?> 

和客户:

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

如果我的代码有什么问题,你能帮我修复吗? 在firefox concole说Firefox can't establish a connection to the server at ws://localhost:12345/.

编辑
既然对这个问题有很大的兴趣,我决定给你提供我最后想到的东西。 这是我的完整代码。

我和你最近在同一条船上,这就是我所做的:

1)我使用phpwebsockets代码作为如何构build服务器端代码的参考。 (你似乎已经这样做了,正如你所指出的那样,这些代码实际上并不是出于各种原因。)

2)我使用PHP.net读取有关phpwebsockets代码中使用的每个套接字函数的详细信息。 通过这样做,我终于明白了整个系统在概念上是如何工作的。 这是一个很大的障碍。

3)我读了实际的WebSocket草案(请做一个网页search,因为我不能发布超过两个链接每个职位)。 在它最终开始沉入水中之前,我必须多次阅读这个东西。在整个过程中,你可能不得不一遍又一遍地回到这个文档,因为它是一个正确的,最新的资源,具有正确的,最新的有关WebSocket API的信息。

4)我根据草案#3中的说明编写了适当的握手程序。 这不是太糟糕。

5)握手后,客户端发送给服务器的乱码文本不断出现,直到我意识到数据是编码的,而且必须被解除屏蔽。 以下链接对我有所帮助: http : //srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/

请注意,此链接中的代码有一些问题,如果不做进一步的修改将无法正常工作。

6)然后我遇到了以下SO线程,这清楚地解释了如何正确地编码和解码来回发送的消息: 如何在服务器端发送和接收WebSocket消息?

这个链接真的很有帮助。 我build议在查看WebSocket草稿时查阅它。 这将有助于从草案的内容中获得更多的意义。

7)在这一点上我差不多已经完成了,但是在使用WebSocket的WebRTC应用程序中遇到了一些问题,所以我最终在SO上提出了自己的问题,我最终解决了这个问题。 要参考这个问题和答案,请做一个networkingsearch“这是什么数据在WebRTC候选人信息? (不带引号)。

8)在这一点上,我几乎所有的工作。 我只需要添加一些额外的逻辑来处理连接closures,我就完成了。

这个过程花了我大约两个星期的时间。 好消息是,我现在很了解WebSocket,并且能够从头开始制作自己的客户端和服务器脚本,这些脚本非常棒。 希望所有这些信息的高潮将为您提供足够的指导和信息来编写您自己的WebSocket PHP脚本。 祝你好运!

编辑 :这个编辑是我原来的答案后的几年,虽然我仍然有一个工作的解决scheme,它不是真的准备好共享。 幸运的是,GitHub上的其他人对我的几乎完全相同的代码(但更干净),所以我build议使用下面的代码来运行PHP WebSocket解决scheme:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php

编辑#2 :虽然我仍然喜欢使用PHP的许多服务器端相关的东西,我不得不承认,我已经真的热身了Node.js最近很多,主要原因是因为它是更好的devise最基本的处理WebSocket比PHP(或任何其他服务器端语言)。 因此,我最近发现,在服务器上设置Apache / PHP和Node.js,并使用Node.js来运行WebSocket服务器和Apache / PHP的其他任何东西都容易得多。 如果你在一个共享主机环境中,你不能安装/使用Node.js的WebSocket,你可以使用一个免费的服务,如Heroku来build立一个Node.js的WebSocket服务器,并使跨域从您的服务器请求它。 只要确保你这样设置你的WebSocket服务器就能够处理跨域请求。

据我所知, 棘轮是目前可用的最好的PHP WebSocket解决scheme。 而且由于它是开源的,你可以看到作者如何使用PHP构build这个WebSocket解决scheme。

为什么不使用套接字http://uk1.php.net/manual/en/book.sockets.php ? 这是很好的文档(不仅在PHP上下文),并有很好的例子http://uk1.php.net/manual/en/sockets.examples.php

在base64_encoding之前需要将密钥从hex转换为十进制,然后将其发送给握手。

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

让我知道这是否有帮助。

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

从一个terminal运行:php server.php

从另一个terminal运行:echo“hello woerld”| nc 127.0.0.1 8002