我在PHP中编写了一个WebSocket服务器,它可以很好地连接到Firefox,但不能连接到Safari。我想,握手有问题。 Safari需要较旧的草案hybi00 ......但我找不到错误:/
尝试连接后,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;
}
}