Dha*_*man 75 javascript php websocket
是否有任何教程或指南显示如何在PHP中编写一个简单的websockets服务器?我试过在谷歌上寻找它,但我找不到很多.我发现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("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;
}
?>
Run Code Online (Sandbox Code Playgroud)
和客户:
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);
};
Run Code Online (Sandbox Code Playgroud)
如果我的代码有什么问题你可以帮我解决吗?Firefox中的Concole说Firefox can't establish a connection to the server at ws://localhost:12345/.
编辑
由于对这个问题很感兴趣,我决定向你提供我最终提出的建议.这是我的完整代码.
Har*_*San 101
我和你最近在同一条船上,这就是我所做的:
1)我使用phpwebsockets代码作为如何构造服务器端代码的参考.(您似乎已经这样做了,正如您所指出的,代码实际上并不是出于各种原因.)
2)我使用PHP.net来读取有关phpwebsockets代码中使用的每个套接字函数的详细信息.通过这样做,我终于能够理解整个系统在概念上如何运作.这是一个相当大的障碍.
3)我阅读了实际的WebSocket草案(请对其进行网络搜索,因为我不能在每个帖子上发布两个以上的链接).在它最终开始陷入困境之前,我不得不多次阅读这篇文章.你可能不得不在整个过程中一次又一次地回到这个文档,因为它是一个确定的,最新的最终资源有关WebSocket API的信息.
4)我根据#3草案中的说明编写了正确的握手程序.这不是太糟糕.
5)握手后我一直收到一堆从客户端发送到服务器的乱码文本,直到我意识到数据已被编码并且必须被取消屏蔽,我才弄明白为什么.以下链接对我有很大帮助:http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/
请注意,此链接提供的代码存在许多问题,如果不进行进一步修改将无法正常运行.
6)然后我遇到了以下SO线程,它清楚地解释了如何正确编码和解码来回发送的消息:如何在服务器端发送和接收WebSocket消息?
这个链接非常有用.我建议在查看WebSocket草案时查阅它.这将有助于更好地理解草案的内容.
7)我几乎已经完成了这一点,但是我使用WebSocket制作的WebRTC应用程序存在一些问题,所以我最终在SO上提出了自己的问题,我最终解决了这个问题.要参考问题和答案,请在网上搜索"在WebRTC候选人信息结束时这些数据是什么?" (不带引号).
8)在这一点上,我几乎把它全部工作了.我只需要添加一些额外的逻辑来处理连接的关闭,我就完成了.
这个过程总共花了我两个星期的时间.好消息是我现在非常了解WebSocket,而且我能够从头开始制作自己的客户端和服务器脚本,效果很好.希望所有这些信息的高潮将为您提供足够的指导和信息来编写您自己的WebSocket PHP脚本.祝好运!
编辑:这个编辑是在我的原始答案之后的几年,虽然我仍然有一个有效的解决方案,但它还没有真正准备好分享.幸运的是,GitHub上的其他人拥有几乎相同的代码(但更清晰),所以我建议使用以下代码来实现有效的PHP WebSocket解决方案:https:
//github.com/ghedipunk/PHP-Websockets/blob/master/ websockets.php
编辑#2:虽然我仍然喜欢将PHP用于许多与服务器端相关的事情,但我不得不承认我最近对Node.js做了很多热情,主要原因是因为它的设计更好开始处理WebSocket而不是PHP(或任何其他服务器端语言).因此,我最近发现,在服务器上设置Apache/PHP和Node.js并使用Node.js运行WebSocket服务器和Apache/PHP用于其他所有事情要容易得多.如果您在共享托管环境中无法安装/使用Node.js进行WebSocket,您可以使用像Heroku这样的免费服务来设置Node.js WebSocket服务器并进行跨域从您的服务器请求它.只要确保你这样做就可以设置你的WebSocket服务器以便能够处理跨源请求.
为什么不使用套接字http://uk1.php.net/manual/en/book.sockets.php?它有很好的文档记录(不仅在PHP上下文中)并且有很好的例子http://uk1.php.net/manual/en/sockets.examples.php
我花了几个小时搜索了可能执行 PHP + WebSockets 的最小解决方案,直到找到这篇文章:
它不需要任何第三方库。
下面是如何做到这一点:创建一个index.html
包含以下内容的:
<html>
<body>
<div id="root"></div>
<script>
var host = 'ws://<<<IP_OF_YOUR_SERVER>>>:12345/websockets.php';
var socket = new WebSocket(host);
socket.onmessage = function(e) {
document.getElementById('root').innerHTML = e.data;
};
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
并在命令行中启动后在浏览器中打开它php websockets.php
(是的,这将是一个事件循环,不断运行 PHP 脚本),并使用此websockets.php
文件。