Websocket PHP:从客户端发送到服务器的立即断开连接和奇怪的消息字符串

Posted

技术标签:

【中文标题】Websocket PHP:从客户端发送到服务器的立即断开连接和奇怪的消息字符串【英文标题】:Websocket PHP: Immediately Disconnected and Weird Message String Sent from Client to Server 【发布时间】:2013-01-17 07:22:39 【问题描述】:

我目前正在关注此tutorial。到现在为止,我遇到了很多错误,也改变了很多班级。现在,问题是,当客户端发送一条消息时,它在服务器中被读取为一个奇怪的字符串,并且服务器发送给客户端的字符串不会触发 onmessage 函数。另一个问题是,当我从客户端发送消息 3 次时,它将立即关闭,并在 chrome 日志Received unexpected continuation frame. 中显示错误。这门课还缺少什么?这是我所做的最后一个更改的课程。谢谢。

<?php

// Usage: $master=new WebSocket("localhost",12345);

class WebSocket
  var $master;
  var $sockets = array();
  var $users   = array();
  var $debug   = false;

  function __construct($address,$port)
    //error_reporting(E_ALL);
    set_time_limit(0);
    ob_implicit_flush();

    $this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
    socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
    socket_bind($this->master, $address, $port)                    or die("socket_bind() failed");
    socket_listen($this->master,20)                                or die("socket_listen() failed");
    $this->sockets[] = $this->master;
    $this->say("Server Started : ".date('Y-m-d H:i:s'));
    $this->say("Listening on   : ".$address." port ".$port);
    $this->say("Master socket  : ".$this->master."\n");

    while(true)
      $changed = $this->sockets;
      socket_select($changed,$write=NULL,$except=NULL,NULL);
      foreach($changed as $socket)
        if($socket==$this->master)
          $client=socket_accept($this->master);
          if($client<0) $this->log("socket_accept() failed"); continue; 
          else $this->connect($client); 
        
        else
          $bytes = @socket_recv($socket,$buffer,2048,0);
          if($bytes==0) $this->disconnect($socket); 
          else
            $user = $this->getuserbysocket($socket);
            if(!$user->handshake) $this->dohandshake($user,$buffer); 
            else $this->process($user,$this->unwrap($buffer)); 
          
        
      
    
  

  function process($user,$msg)
    /* Extend and modify this method to suit your needs */
    /* Basic usage is to echo incoming messages back to client */
    //$this->send($user->socket,$msg);

    foreach($this->users as $usr)
        $this->send($usr->socket,$user->id."&gt; ".$msg);
    
  

  function send($client,$msg) 
    $this->say("> " . $msg);
    $msg = $this->wrap($msg);
    echo $msg;
    socket_write($client, $msg, strlen($msg));
    $this->say("! ".strlen($msg));
   

  function connect($socket)
    $user = new SocketUser();
    $user->id = uniqid();
    $user->socket = $socket;
    array_push($this->users,$user);
    array_push($this->sockets,$socket);
    $this->say($socket." CONNECTED!");
    $this->send($socket, "Welcome");
    $this->log(date("d/n/Y ")."at ".date("H:i:s T"));
  

  function disconnect($socket)
    $found=null;
    $n=count($this->users);
    for($i=0;$i<$n;$i++)
      if($this->users[$i]->socket==$socket) $found=$i; break; 
    
    if(!is_null($found)) array_splice($this->users,$found,1); 
    $index=array_search($socket,$this->sockets);
    socket_close($socket);
    $this->say($socket." DISCONNECTED!");
    if($index>=0) array_splice($this->sockets,$index,1); 
  

  function dohandshake($user,$buffer)
    $this->log("\nRequesting handshake...");
    $this->log($buffer);
    list($resource,$host,$origin,$key,$l8b) = $this->getheaders($buffer);
    $this->log("Handshaking...");
    //$port = explode(":",$host);
    //$port = $port[1];
    //$this->log($origin."\r\n".$host);
    $upgrade  = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" .
                "Upgrade: WebSocket\r\n" .
                "Connection: Upgrade\r\n" .
                                //"WebSocket-Origin: " . $origin . "\r\n" .
                                //"WebSocket-Location: ws://" . $host . $resource . "\r\n" .
                "Sec-WebSocket-Origin: " . $origin . "\r\n" .
                    "Sec-WebSocket-Location: ws://" . $host . $resource . "\r\n" .
                    //"Sec-WebSocket-Protocol: icbmgame\r\n" . //Client doesn't send this
                    "Sec-WebSocket-Accept: " . base64_encode(SHA1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true)) . "\r\n" .
                "\r\n";// .
                    $this->calcKey($key,'',$l8b) . "\r\n";// .
                        //"\r\n";
    socket_write($user->socket,$upgrade.chr(0),strlen($upgrade.chr(0)));
    $user->handshake=true;
    $this->log($upgrade);
    $this->log("Done handshaking...");
    return true;
  

  function calcKey($key1,$key2,$l8b)
        //Get the numbers
        preg_match_all('/([\d]+)/', $key1, $key1_num);
        preg_match_all('/([\d]+)/', $key2, $key2_num);
        //Number crunching [/bad pun]
        $this->log("Key1: " . $key1_num = implode($key1_num[0]) );
        $this->log("Key2: " . $key2_num = implode($key2_num[0]) );
        //Count spaces
        preg_match_all('/([ ]+)/', $key1, $key1_spc);
        preg_match_all('/([ ]+)/', $key2, $key2_spc);
        //How many spaces did it find?
        $this->log("Key1 Spaces: " . $key1_spc = strlen(implode($key1_spc[0])) );
        $this->log("Key2 Spaces: " . $key2_spc = strlen(implode($key2_spc[0])) );
        if($key1_spc==0|$key2_spc==0) $this->log("Invalid key");return; 
        //Some math
        $key1_sec = pack("N",$key1_num / $key1_spc); //Get the 32bit secret key, minus the other thing
        $key2_sec = pack("N",$key2_num / $key2_spc);
        //This needs checking, I'm not completely sure it should be a binary string
        return md5($key1_sec.$key2_sec.$l8b,1); //The result, I think
  

  function getheaders($req)
    $r=$h=$o=$sk1=$sk2=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("/Origin: (.*)\r\n/"            ,$req,$match)) $o=$match[1]; 
    if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)) $this->log("Sec Key: ".$sk1=$match[1]); 
    //if(preg_match("/Sec-WebSocket-Key1: (.*)\r\n/",$req,$match)) $this->log("Sec Key1: ".$sk1=$match[1]); 
    //if(preg_match("/Sec-WebSocket-Key2: (.*)\r\n/",$req,$match)) $this->log("Sec Key2: ".$sk2=$match[1]); 
    if($match=substr($req,-8))  $this->log("Last 8 bytes: ".$l8b=$match); 
    return array($r,$h,$o,$sk1,$sk2,$l8b);
  

  function getuserbysocket($socket)
    $found=null;
    foreach($this->users as $user)
      if($user->socket==$socket) $found=$user; break; 
    
    return $found;
  

  function     say($msg="") echo $msg."\n"; 
  function     log($msg="") if($this->debug)  echo $msg."\n";  
  function    wrap($msg="") return $msg; 
  //function    wrap($msg="") return chr(0).$msg.chr(255); 
  //function  unwrap($msg="") return $msg; 
  function  unwrap($msg="") return substr($msg,1,strlen($msg)-2); 



class SocketUser
  var $id;
  var $socket;
  var $handshake;


?>

【问题讨论】:

您在此处使用的 WebSocket 库不支持 IETF 的 WebSockets Web 标准。您将需要支持 RFC6455 版本规范的东西。您需要找到另一个支持当前浏览器实现该规范的库。 是的,你是对的。我已经搬到另一个图书馆,它可以工作。谢谢。 :D 请务必通过指向您使用的库的链接来回答您的问题。我相信其他人会觉得这很有用。 【参考方案1】:

按照 Mark Tomlin 的建议,我搬到了另一个图书馆并找到了另一个 here。对于像我这样的初学者来说,这个库非常容易使用,而且效果很好。

【讨论】:

以上是关于Websocket PHP:从客户端发送到服务器的立即断开连接和奇怪的消息字符串的主要内容,如果未能解决你的问题,请参考以下文章

将图像从 websocket 服务器 .NET 发送到客户端 (HTML5)

php使用WebSocket详细教程之对接收数据解包及发送数据包装

如何在 PHP 服务器上向客户端发送 WebSocket 关闭帧?

Python websocket 客户端 - 从 python 代码向 WebSocketServer 发送消息

是否可以通过 126 个字符发送 websocket 数据?

html5的websocket怎么在服务器端配置php