Hallo,
ich hab leider ein Problem mit Websocket-Verbindungen, das ich nicht gelöst bekomme…
Der Client baut eigentlich ganz gewöhnlich und erfolgreich eine Socket-Verbindung auf. Danach funktioniert die Kommunikation tadellos, bis der Benutzer seine Seite neu lädt (z.B. F5). Theoretisch bedeutet das doch, dass die Seite kurz verlassen und dann wieder neu aufgebaut wird. Also wird die Verbindung getrennt und auch wieder neu aufgebaut. Aber scheinbar bekommt der Server das nicht richtig mit, denn der client kann zwar noch Nachrichten versenden, aber es kommen keine Nachrichten mehr beim ihm an. Auf dem Server sehe ich, dass beim Wiederverbinden dieselbe Ressource-ID verwendet wird, was wohl schon nicht richtig ist und der Server meldet ein paar Sekunden später, dass der Client disconnected wäre.
Ich habe sogar schon sicherheitshalber ein websocket.close() eingebaut, bevor die Seite beendet wird:
window.onbeforeunload = function() {
websocket.onclose = function () {}; // disable onclose handler first
websocket.close()
};
Aber das scheint den Server nicht zu interessieren.
Client-Script:
var wsUri = "ws://10.10.10.123:9000/scripts/server.php";
websocket = new WebSocket(wsUri);
websocket.onopen = function(ev) { // connection is open
$('#message_box').append("<div class=\"system_msg\">Connected!</div>"); //notify user
var mymessage = "user"; // first hello to give system the name
//prepare json data
var msg = {
message: mymessage,
name: myname,
type: mytype
};
//convert and send data to server
websocket.send(JSON.stringify(msg));
}
//#### Message received from server?
websocket.onmessage = function(ev) {
var msg = JSON.parse(ev.data); //PHP sends Json data
var utype = msg.type; //message type
var umsg = msg.message; //message text
var uname = msg.name; //user name
$('#message_box').append("<div><span class=\"user_name\">"+uname+"</span>: <span class=\"user_message\">"+umsg+" ("+utype+")</span></div>");
var objDiv = document.getElementById("message_box");
objDiv.scrollTop = objDiv.scrollHeight;
};
websocket.onerror = function(ev){$('#message_box').append("<div class=\"system_error\">Error Occurred - "+ev.data+"</div>");};
websocket.onclose = function(ev){$('#message_box').append("<div class=\"system_msg\">Connection Closed</div>");};
Server-Script:
//Create TCP/IP sream socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//reuseable port
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
//bind socket to specified host
socket_bind($socket, 0, $port);
//listen to port
socket_listen($socket);
//create & add listning socket to the list
$clients = array($socket);
$clientsid = array();
//start endless loop, so that our script doesn't stop
while (true) {
//manage multipal connections
$changed = $clients;
//returns the socket resources in $changed array
socket_select($changed, $null, $null, 0, 10);
//check for new socket
if (in_array($socket, $changed)) {
$socket_new = socket_accept($socket); //accpet new socket
$clients[] = $socket_new; //add socket to client array
$header = socket_read($socket_new, 1024); //read data sent by the socket
perform_handshaking($header, $socket_new, $host, $port); //perform websocket handshake
socket_getpeername($socket_new, $ip); //get ip address of connected socket
$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected'))); //prepare json data
send_message($response,""); //notify all users about new connection
//make room for new socket
$found_socket = array_search($socket, $changed);
unset($changed[$found_socket]);
}
//loop through all connected sockets
foreach ($changed as $changed_socket) {
//check for any incomming data
while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)
{
$received_text = unmask($buf); //unmask data
$tst_msg = json_decode($received_text); //json decode
$user_name = $tst_msg->name; //sender name
$user_message = $tst_msg->message; //message text
$user_type = $tst_msg->type; //message type
echo "$user_name sent: $user_message (type: $user_type)\r\n";
$erg = array_multi_search($user_name, $clientsid);
if (empty($erg[0][0])) { // neues Device angemeldet
array_push($clientsid, array($user_name,$changed_socket));
$response = mask(json_encode(array('type'=>'system', 'name'=>'system', 'message'=>$user_name.' added.')));
send_message($response,""); // notify all users about new connection
foreach($clientsid as $subKey => $subArray){
echo "-->".$subArray[0].":".$subArray[1]."\r\n";
if (empty($subArray[0])) {
unset($clientsid[$subKey]);
}
}
}
$x = explode(":",$user_message);
if ($x[0] <> $user_message) { // user sends to specific target
$target = $x[0];
array_shift($x); // erstes Element löschen
$user_message = implode($x);
$response_text = mask(json_encode(array('type'=>'whisper', 'name'=>$user_name." (to ".$target.")", 'message'=>$user_message)));
send_message($response_text,$user_name); //send data to inform sender that message will be sent
} else {
$target = "";
}
//prepare data to be sent to client
$response_text = mask(json_encode(array('type'=>$user_type, 'name'=>$user_name, 'message'=>$user_message)));
send_message($response_text,$target); //send data to target device
break 2; //exits this loop
}
$buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ);
if ($buf === false) { // check disconnected client
// remove client for $clients array
$found_socket = array_search($changed_socket, $clients);
socket_getpeername($changed_socket, $ip);
unset($clients[$found_socket]);
// remove client from $clientsid multi-array
foreach($clientsid as $subKey => $subArray){
echo "-->".$subArray[0].":".$subArray[1]."\r\n";
if (($subArray[1] == $changed_socket) OR (empty($subArray[0]))) {
unset($clientsid[$subKey]);
}
}
//notify all service-pc users about disconnected connection
$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected')));
send_message($response,"");
echo $ip." disconnected\r\n";
}
}
}
// close the listening socket
socket_close($socket);
function send_message($msg,$target) {
global $clients;
global $clientsid;
if (!empty($target)) {
$erg = array_multi_search($target, $clientsid);
}
if ((!empty($target)) AND (!empty($erg[0][0]))) {
echo "sende zu ".$erg[0][0]." mit resource: ".$erg[0][1]."\r\n";
$usertarget = $erg[0][1];
@socket_write($usertarget,$msg,strlen($msg)); // send only to selected user
} else {
foreach($clientsid as $nr => $client) {
if (substr($client[0],0,9) == "user") { // only inform all users
echo "sende zu ".$client[0]." mit resource: ".$client[1]."\r\n";
$changed_socket = $client[1];
@socket_write($changed_socket,$msg,strlen($msg));
}
}
}
return true;
}
//Unmask incoming framed message
function unmask($text) {
$length = ord($text[1]) & 127;
if($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
}
elseif($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
}
else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
//Encode message for transfer to client.
function mask($text) {
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$text;
}
//handshake new client.
function perform_handshaking($receved_header,$client_conn, $host, $port) {
$headers = array();
$lines = preg_split("/\r\n/", $receved_header);
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
//hand shaking header
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $host\r\n" .
"WebSocket-Location: ws://$host:$port/demo/shout.php\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($client_conn,$upgrade,strlen($upgrade));
}
function array_multi_search($mSearch, $aArray, $sKey = "") {
$aResult = array();
foreach( (array) $aArray as $aValues) {
if($sKey === "" && in_array($mSearch, $aValues)) $aResult[] = $aValues;
else
if(isset($aValues[$sKey]) && $aValues[$sKey] == $mSearch) $aResult[] = $aValues;
}
return $aResult;
}
LG Marvin