Hallo zusammen,
ich befasse mich testweise gerade mit WebSockets und habe im Netz einige fertige Scripts gefunden.
Nach hin und her Schreiberei funktioniert es nun auch einigermaßen.
Doch ich habe ein merkwürdiges Problem. Mehrere Clients connecten problemlos und können miteinander schreiben.
Doch: Öffne ich einen Client im Safari, einen im FireFox und einen in Chrome, passiert nichts wenn ich den Safari-Client einfach schließe.
Schließe ich allerdings den FireFox oder Chrome-Browser disconnectet er alle anderen Clients. (FireFox sendet beim verlassen wohl noch ein "?" - Chrome ein Zeichen, welches nicht angezeigt wird (vielleicht ein leeren String..?).
Der Server stürzt nicht ab, und gibt auch keine Fehler aus. Man kann danach einfach wieder zum Server verbinden.
Hier nun das Script: (Ja, es ist unsauberes PHP, PHP4&5 gemischt....) Wie gesagt, ist ausm Netz zusammengebaut und nicht die finale Version..
Das Problem SCHEINT mir hier zu liegen:
Diese Funktion wird wohl für alle Sockets aufgerufen, sobald dieses "END"-Zeichen von FF oder Chrome rein kommt.
Hoffe jemand kann mir helfen.
Beste Grüße & Danke im Voraus.
paD
ich befasse mich testweise gerade mit WebSockets und habe im Netz einige fertige Scripts gefunden.
Nach hin und her Schreiberei funktioniert es nun auch einigermaßen.
Doch ich habe ein merkwürdiges Problem. Mehrere Clients connecten problemlos und können miteinander schreiben.
Doch: Öffne ich einen Client im Safari, einen im FireFox und einen in Chrome, passiert nichts wenn ich den Safari-Client einfach schließe.
Schließe ich allerdings den FireFox oder Chrome-Browser disconnectet er alle anderen Clients. (FireFox sendet beim verlassen wohl noch ein "?" - Chrome ein Zeichen, welches nicht angezeigt wird (vielleicht ein leeren String..?).
Der Server stürzt nicht ab, und gibt auch keine Fehler aus. Man kann danach einfach wieder zum Server verbinden.
Hier nun das Script: (Ja, es ist unsauberes PHP, PHP4&5 gemischt....) Wie gesagt, ist ausm Netz zusammengebaut und nicht die finale Version..
Das Problem SCHEINT mir hier zu liegen:
PHP-Code:
elseif($bytes == 0){
$this->disconnect($socket);
}
PHP-Code:
<?php
// Usage: $master=new WebSocket("localhost",12345);
class WebSocket{
private $master;
private $sockets = array();
private $users = array();
private $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) 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;
$wirte = array();
$except = array();
socket_select($changed,$write,$except,NULL);
foreach($changed as $socket){
# Wenn Mastersocket, dann neuer Client
if($socket==$this->master){
$client=socket_accept($this->master);
if($client<0){ $this->log("socket_accept() failed"); continue; }
else{ $this->connect($client); }
}
# Daten von einem Client
else{
$user = $this->getuserbysocket($socket);
$bytes = '';
if($user) {
# daten lesen
$bytes = @socket_recv($socket, $buffer, 2048, 0);
#$data=null;
#while($bytes = @socket_recv($socket, $r_data, 2048, MSG_DONTWAIT)){
# $data.=$r_data;
#}
# User schon gehandshaked?
if(!$user->getHandshake()){
$this->dohandshake($user,$buffer);
}
elseif($bytes == 0){
$this->disconnect($socket);
}
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->getSocket(),$msg);
}
function send($client,$msg){
$this->say("> ".$msg);
$msg = $this->wrap($msg);
socket_write($client,$msg,strlen($msg));
#$this->say("! ".strlen($msg));
}
function sendToAll($msg){
$this->say("> ".$msg);
$msg = $this->wrap($msg);
foreach($this->users as $user){
socket_write($user->getSocket(),$msg,strlen($msg));
}
#$this->say("! ".strlen($msg));
}
function connect($socket){
$user = new User();
$user->setID(uniqid());
$user->setSocket($socket);
array_push($this->users,$user);
array_push($this->sockets,$socket);
$this->log($socket." CONNECTED!");
$this->log(date("d/n/Y ")."at ".date("H:i:s T"));
}
function disconnect($socket){
# User nach socket Suchen
$found=null;
$n=count($this->users);
for($i=0;$i<$n;$i++){
if($this->users[$i]->getSocket()==$socket){ $found=$i; break; }
}
if(!is_null($found)){
array_splice($this->users,$found,1);
}
# Socket aus Liste suchen und schließen
$index=array_search($socket,$this->sockets);
if($index>=0){
array_splice($this->sockets,$index,1);
}
@socket_shutdown($socket, 2);
socket_close($socket);
$this->say($socket." DISCONNECTED!");
}
function dohandshake($user,$buffer){
$this->log("\nRequesting handshake...");
$this->log($buffer);
$this->log("Handshaking...");
// Determine which version of the WebSocket protocol the client is using
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/ ", $buffer, $match))
$version = $match[1];
else {
$this->log("Client doens't support WebSocket");
return false;
}
$this->log("Client WebSocket version {$version} from 13");
if($version == 13)
{
if(preg_match("/GET (.*) HTTP/", $buffer, $match))
$root = $match[1];
if(preg_match("/Host: (.*)\r\n/", $buffer, $match))
$host = $match[1];
if(preg_match("/Origin: (.*)\r\n/", $buffer, $match))
$origin = $match[1];
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $buffer, $match))
$key = $match[1];
// Generate our Socket-Accept key based on the IETF specifications
$acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$acceptKey = base64_encode(sha1($acceptKey, true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
"Upgrade: websocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Accept: $acceptKey".
"\r\n\r\n";
socket_write($user->getSocket(), $upgrade, strlen($upgrade));
$user->setHandshake(true);
$this->say("Handshake successfully done!");
return true;
}
else
{
$this->log("Client is trying to use an unsupported WebSocket protocol ({$version})");
return false;
}
$this->log($upgrade);
$this->log("Done handshaking...");
$this->say("New User");
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 getuserbysocket($socket){
$found=null;
foreach($this->users as $user){
if($user->getSocket()==$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 chr(0).$msg.chr(255); }
#function unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }
## UNMASK
private function unwrap($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;
}
private function wrap($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;
}
}
class User{
private $id;
private $socket;
private $handshake;
private $name = 'none';
public function setID($id) {
$this->id = $id;
}
public function getID() {
return $this->id;
}
public function setSocket($socket) {
$this->socket = $socket;
}
public function getSocket() {
return $this->socket;
}
public function setHandshake($hs) {
$this->handshake = $hs;
}
public function getHandshake() {
return $this->handshake;
}
public function setName($name) {
$this->name = $name;
}
}
?>
Beste Grüße & Danke im Voraus.
paD
Kommentar