如何在PHP中创建websockets服务器 [英] How to create websockets server in PHP

查看:108
本文介绍了如何在PHP中创建websockets服务器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Are there any tutorials or guides showing how to write myself a simple websockets server in PHP? I have tried looking for it on google but I didn't find many. I found phpwebsockets but it is outdated now and doesn't support newest protocol. I tried updating it myself but it doesn't seem to work.

#!/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;
}

?>

和客户:

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无法在ws:// localhost:12345 /建立与服务器的连接。

If there is anything wrong in my code can you help me fix it? Concole in firefox says Firefox can't establish a connection to the server at ws://localhost:12345/.

编辑

由于对此问题很感兴趣,我决定向您提供我最终提出的建议。 这是我的完整代码。

推荐答案

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

I was in the same boat as you recently, and here is what I did:

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

1) I used the phpwebsockets code as a reference for how to structure the server-side code. (You seem to already be doing this, and as you noted, the code doesn't actually work for a variety of reasons.)

2)我使用的是PHP.net阅读有关phpwebsockets代码中使用的每个套接字函数的详细信息。通过这样做,我终于能够理解整个系统在概念上如何运作。这是一个相当大的障碍。

2) I used PHP.net to read the details about every socket function used in the phpwebsockets code. By doing this, I was finally able to understand how the whole system works conceptually. This was a pretty big hurdle.

3)我读了实际的WebSocket草案(请进行网络搜索,因为我不能发布超过两个链接后)。在它最终开始陷入困境之前,我不得不多次阅读这篇文章。你可能不得不在整个过程中一次又一次地回到这个文档,因为它是一个确定的,最新的最终资源有关WebSocket API的信息。

3) I read the actual WebSocket draft (please do a web search for it, as I can't post more than two links per post). I had to read this thing a bunch of times before it finally started to sink in. You will likely have to go back to this document again and again throughout the process, as it is the one definitive resource with correct, up-to-date information about the WebSocket API.

4)我根据#3草案中的说明编写了正确的握手程序。这还不错。

4) I coded the proper handshake procedure based on the instructions in the draft in #3. This wasn't too bad.

5)握手后我不断收到客户端发送到服务器的一堆乱码文本,我无法弄清楚为什么直到我意识到数据已编码并且必须取消屏蔽。以下链接帮助了我很多: http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/

5) I kept getting a bunch of garbled text sent from the clients to the server after the handshake and I couldn't figure out why until I realized that the data is encoded and must be unmasked. The following link helped me a lot here: http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/

请注意,此链接提供的代码存在许多问题,如果没有进一步修改将无法正常工作。

Please note that the code available at this link has a number of problems and won't work properly without further modification.

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

6) I then came across the following SO thread, which clearly explains how to properly encode and decode messages being sent back and forth: How can I send and receive WebSocket messages on the server side?

此链接非常有用。我建议在查看WebSocket草案时查阅它。这将有助于更好地理解草案的内容。

This link was really helpful. I recommend consulting it while looking at the WebSocket draft. It'll help make more sense out of what the draft is saying.

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

7) I was almost done at this point, but had some issues with a WebRTC app I was making using WebSocket, so I ended up asking my own question on SO, which I eventually solved. To reference the question and answer, please do a web search for "SO What is this data at the end of WebRTC candidate info?" (without the quotation marks).

8)此时,我几乎把它全部都用了。我只需添加一些额外的逻辑来处理连接的关闭,我就完成了。

8) At this point, I pretty much had it all working. I just had to add some additional logic for handling the closing of connections, and I was done.

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

That process took me about two weeks total. The good news is that I understand WebSocket really well now and I was able to make my own client and server scripts from scratch that work great. Hopefully the culmination of all that information will give you enough guidance and information to code your own WebSocket PHP script. Good luck!

编辑:这个编辑是我原来答案后的几年,虽然我还有一个工作解决方案,它还没有真正准备好分享。幸运的是,GitHub上的其他人的代码几乎完全相同(但更清晰),所以我建议使用以下代码来实现有效的PHP WebSocket解决方案:

https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php

Edit: This edit is a couple of years after my original answer, and while I do still have a working solution, it's not really ready for sharing. Luckily, someone else on GitHub has almost identical code to mine (but much cleaner), so I recommend using the following code for a working PHP WebSocket solution:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php

编辑#2 :虽然我仍然喜欢使用PHP来处理很多与服务器端相关的事情,但我不得不承认我已经热身了最近很多都是Node.js,主要原因是因为它比PHP(或任何其他服务器端语言)从头开始设计更好地处理WebSocket。因此,我最近发现,在服务器上设置Apache / PHP和Node.js并使用Node.js运行WebSocket服务器和Apache / PHP用于其他所有事情要容易得多。如果您在共享托管环境中无法安装/使用Node.js进行WebSocket,您可以使用像Heroku这样的免费服务来设置Node.js WebSocket服务器并进行跨域从您的服务器请求它。只要确保你这样做就可以设置你的WebSocket服务器以便能够处理跨源请求。

Edit #2: While I still enjoy using PHP for a lot of server-side related things, I have to admit that I've really warmed up to Node.js a lot recently, and the main reason is because it's better designed from the ground up to handle WebSocket than PHP (or any other server-side language). As such, I've found recently that it's a lot easier to set up both Apache/PHP and Node.js on your server and use Node.js for running the WebSocket server and Apache/PHP for everything else. And in the case where you're on a shared hosting environment in which you can't install/use Node.js for WebSocket, you can use a free service like Heroku to set up a Node.js WebSocket server and make cross-domain requests to it from your server. Just make sure if you do that to set your WebSocket server up to be able to handle cross-origin requests.

这篇关于如何在PHP中创建websockets服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆