2012-03-27 65 views
2

我在PHP中編寫了一個WebSocket服務器,它連接到Firefox很好,但不能連接到Safari。我認爲,握手有問題。 Safari需要較舊的草稿hybi00 ...但我找不到錯誤:/PHP WebSocketServer無法連接到WebKit(Safari)

嘗試連接後直接在Safari中關閉連接。 在此先感謝您的幫助!

<?php 
error_reporting(E_ALL); 
set_time_limit(0); 
date_default_timezone_set("Europe/Berlin"); 
ob_implicit_flush(true); 

function debug($text) 
{ 
    $file = "log.html"; 
    file_put_contents($file, 
    "<pre>".$text."</pre>"."<b>Ende der Information</b><p />", 8); 
} 



//Socket initialisieren 
$master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); 
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1); 
//An diesen Computer binden 
socket_bind($master, "localhost", 10000) or die(); 
//Maximale Verbindungsanforderungen: 5 
socket_listen($master, 5); 

do 
{ 
    $client = socket_accept($master); 
    echo "Neuer Client!\n"; 
    new Client($client); 
} while(true); 

exit(); 





//Clientklasse 
class Client 
{ 
    private $connection; 
    private $header = array(); 
    private $crypt; //BOOL, hybi00 needs no en-/decryption but hybi06+ 

    function __construct(&$socket) 
    { 
     $this->connection = $socket; 
     $this->SetWebSecKey(); 

     while(true) 
     { 
      $q = socket_read($this->connection, 1024); 
      if(isset($q)) 
      { 
       echo $this->unmask($q); 
      } 
      $welcome = "\x81\x8c\xff\xb8\xbd\xbd\xb7\xdd\xd1\xd1\x90\x98\xea\xd2\x8d\xd4\xd9\x9c"; 
     } 
    } 

    /** 
    * Unmask a received payload 
    * @param $payload 
    * 
    * http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/ 
    */ 
    private function unmask($payload) { 
     $length = ord($payload[1]) & 127; 

     if($length == 126) { 
      $masks = substr($payload, 4, 4); 
      $data = substr($payload, 8); 
     } 
     elseif($length == 127) { 
      $masks = substr($payload, 10, 4); 
      $data = substr($payload, 14); 
     } 
     else { 
      $masks = substr($payload, 2, 4); 
      $data = substr($payload, 6); 
     } 

     $text = ''; 
     for ($i = 0; $i < strlen($data); ++$i) { 
      $text .= $data[$i]^$masks[$i%4]; 
     } 
     return $text; 
    } 

    /** 
    * Encode a text for sending to clients via ws:// 
    * @param $text 
    * 
    * http://srchea.com/blog/2011/12/build-a-real-time-application-using-html5-websockets/ 
    */ 
    private function encode($text) 
    { 
     // 0x1 text frame (FIN + opcode) 
     $b1 = 0x80 | (0x1 & 0x0f); 
     $length = strlen($text); 

     if($length <= 125) 
     $header = pack('CC', $b1, $length); 
     elseif($length > 125 && $length < 65536) 
     $header = pack('CCS', $b1, 126, $length); 
     elseif($length >= 65536) 
     $header = pack('CCN', $b1, 127, $length); 

     return $header.$text; 
    } 

    private function SetWebSecKey() 
    { 
     //Headerrequest in Array teilen 
     $request = socket_read($this->connection, 1024); 
     $request = explode("\n", $request); 
     foreach($request as $key=>$head) 
     { 
      $header = explode(":", $head, 2); 
      $header[0] = trim($header[0]); //Parameter 
      $header[1] = trim($header[1]); //Wert 

      $this->header[$header[0]] = $header[1]; 
     } 

     $handshake = new Handshake($this->header); 
     $handshake->Send($this->connection); 
    } 

    private function SendPlain($text) 
    { 
     socket_write($this->connection, $text, strlen($text)); 
    } 
} 

class Handshake 
{ 
    private $header; 
    private $hybi00; 
    private $secKey; 

    public function __construct($headerArray) 
    { 
     echo "\nHandshake wird ausgeführt: "; 
     //Einfacher oder doppelter SecWebKey? 
     //Einfach: Hybi06+ 
     //Doppelt: Hybi00+ 
     $this->header = $headerArray; 
     if(isset($this->header["Sec-WebSocket-Key"])) 
     { 
      echo "Hybi06+\n"; 
      //Moderner Hybi06+ 
      $this->secKey = $this->InitHybi06(); 
     } elseif(isset($this->header["Sec-WebSocket-Key1"])) { 
      //Älterer Hybi00+ 
      echo "Hybi00+\n"; 
      $this->secKey = $this->InitHybi00(); 
     } else { 
      echo "Fehler - Unbekanntes WebSocket-Protokoll\n"; 
      return; 
     } 
    } 

    private function InitHybi06() 
    { 
     $this->hybi00 = false; 

     $key = $this->header["Sec-WebSocket-Key"]; 

     $GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; 
     $key .= $GUID; 

     return base64_encode(sha1($key, true)); 
    } 

    private function InitHybi00() 
    { 
     $this->hybi00 = true; 

     $key1 = $this->header["Sec-WebSocket-Key1"]; 
     $key2 = $this->header["Sec-WebSocket-Key2"]; 

     $key1 = $this->Hybi00NumSpace($key1); 
     $key2 = $this->Hybi00NumSpace($key2); 

     $data = array_keys($this->header); 
     $data = $data[count($this->header)-1]; 

     $ctx = hash_init('md5'); 
     hash_update($ctx, pack("N", $key1)); 
     hash_update($ctx, pack("N", $key2)); 
     hash_update($ctx, $data); 
     $hash_data = hash_final($ctx,true); 

     return $hash_data; 
    } 

    private function Hybi00NumSpace($key) 
    { 
     $numbrs = preg_replace('/[^\d]*/', '', $key); 
     $spaces = strlen(preg_replace('/[^\s][^\s]*/', '', $key)); 

     return $numbrs/$spaces; 
    } 

    public function Send($socket, $origin="http://localhost", $location="ws://localhost", $return=false) 
    { 
     if(!isset($this->header) || !isset($this->hybi00) || !isset($this->secKey)) 
     { 
      echo "Fehler: Handshake nicht vollständig!\n"; return; 
     } 
     $header = $this->CreateHeader($origin, $location); 
     debug($header); 
     $header = explode("\n", $header); 
     if($return) 
      return $header; 
     foreach($header as $line) 
     { 
      $line .= "\n"; 
      socket_write($socket, $line, strlen($line)); 
     } 
    } 

    private function CreateHeader($origin, $location) 
    { 
     $header = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n". 
     "Upgrade: WebSocket\r\n". 
     "Connection: Upgrade\r\n". 
     "Sec-WebSocket-Origin: $origin\r\n"; 

     if($this->hybi00) 
     { 
      $header .= "Sec-WebSocket-Location: $location\r\n"; 
      $header .= "\r\n".$this->secKey.chr(0); 
     } else { 
      $header .= "Sec-WebSocket-Accept: ".$this->secKey."\r\n\r\n"; 
     } 

     return $header; 
    } 
} 
+0

獲取wireshark並比較您的服務器與工作服務器的行爲。 – 2012-03-27 14:59:58

+0

「服務器」本身正常運行,並從Safari和Firefox接收標題 - 但無法發送到Safari,而FF沒有問題。 – Jazzschmidt 2012-03-27 15:33:17

+0

http://socket.io/ – dqhendricks 2012-03-27 15:57:26

回答

0

我不知道如何在你的代碼做到這一點,你可以看看PHPDaemon - 對PHP的非阻塞服務器寫作,與WebSocket的支持。