php websocket looses user's data only sometimes - javascript

I'm new to websockets and I'm facing a problem that I can't manage to fix. I've downloaded server and client from the web and modified it to have it working my way. The problem is I want to store users' data(e.g. name,color, whatever) on the serverside. When I want to spread the message from one user to all other users it sometimes works and sometimes it doesn't. Funny thing is that it reads user's id perfectly every time but sometimes it looses name or color, which all are, in fact, the fields of the same class and I don't see the difference... Please help.
Server:
<?php
/**
* Simple server class which manage WebSocket protocols
* #author Sann-Remy Chea <http://srchea.com>
* #license This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
* #version 0.1
*/
class Server {
/**
* The address of the server
* #var String
*/
private $address;
/**
* The port for the master socket
* #var int
*/
private $port;
/**
* The master socket
* #var Resource
*/
private $master;
/**
* The array of sockets (1 socket = 1 client)
* #var Array of resource
*/
private $sockets;
/**
* The array of connected clients
* #var Array of clients
*/
private $clients;
/**
* If true, the server will print messages to the terminal
* #var Boolean
*/
private $verboseMode;
private $maxConnections = 3;
/**
* Server constructor
* #param $address The address IP or hostname of the server (default: 127.0.0.1).
* #param $port The port for the master socket (default: 5001)
*/
function Server($address = '127.0.0.1', $port = 5001, $verboseMode = false) {
$this->console("Server starting...");
$this->address = $address;
$this->port = $port;
$this->verboseMode = $verboseMode;
// socket creation
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
if (!is_resource($socket))
$this->console("socket_create() failed: ".socket_strerror(socket_last_error()), true);
if (!socket_bind($socket, $this->address, $this->port))
$this->console("socket_bind() failed: ".socket_strerror(socket_last_error()), true);
if(!socket_listen($socket, 20))
$this->console("socket_listen() failed: ".socket_strerror(socket_last_error()), true);
$this->master = $socket;
$this->sockets = array($socket);
$this->console("Server started on {$this->address}:{$this->port}");
}
/**
* Create a client object with its associated socket
* #param $socket
*/
private function connect($socket) {
$this->console("Creating client...");
$client = new Client(uniqid(), $socket);
$this->clients[] = $client;
$this->sockets[] = $socket;
$this->console("Client #{$client->getId()} is successfully created!");
}
/**
* Do the handshaking between client and server
* #param $client
* #param $headers
*/
private function handshake($client, $headers) {
$this->console("Getting client WebSocket version...");
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
$version = $match[1];
else {
$this->console("The client doesn't support WebSocket");
return false;
}
$this->console("Client WebSocket version is {$version}, (required: 13)");
if($version == 13) {
// Extract header variables
$this->console("Getting headers...");
if(preg_match("/GET (.*) HTTP/", $headers, $match))
$root = $match[1];
if(preg_match("/Host: (.*)\r\n/", $headers, $match))
$host = $match[1];
if(preg_match("/Origin: (.*)\r\n/", $headers, $match))
$origin = $match[1];
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
$key = $match[1];
$this->console("Client headers are:");
$this->console("\t- Root: ".$root);
$this->console("\t- Host: ".$host);
$this->console("\t- Origin: ".$origin);
$this->console("\t- Sec-WebSocket-Key: ".$key);
$this->console("Generating Sec-WebSocket-Accept key...");
$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";
$this->console("Sending this response to the client #{$client->getId()}:\r\n".$upgrade);
socket_write($client->getSocket(), $upgrade);
$client->setHandshake(true);
$this->console("Handshake is successfully done!");
return true;
}
else {
$this->console("WebSocket version 13 required (the client supports version {$version})");
return false;
}
}
/**
* Disconnect a client and close the connection
* #param $socket
*/
private function disconnect($client)
{
$this->console("Disconnecting client #{$client->getId()}");
$i = array_search($client, $this->clients);
$j = array_search($client->getSocket(), $this->sockets);
if($j >= 0)
{
array_splice($this->sockets, $j, 1);
socket_shutdown($client->getSocket(), 2);
socket_close($client->getSocket());
$this->console("Socket closed");
}
if($i >= 0)
array_splice($this->clients, $i, 1);
$this->console("Client #{$client->getId()} disconnected");
/*foreach ($this->clients as $cnt)
$this->send($cnt,$this->usernames[$client->getId()] ." left!");*/
}
/**
* Get the client associated with the socket
* #param $socket
* #return A client object if found, if not false
*/
private function getClientBySocket($socket) {
foreach($this->clients as $client)
if($client->getSocket() == $socket) {
$this->console("Client found");
return $client;
}
return false;
}
/**
* Do an action
* #param $client
* #param $action
*/
private function action($client, $action)
{
$action = $this->unmask($action);
$this->console("Performing action: ".$action);
if($action == "exit" || $action == "quit")
{
$this->console("Killing a child process");
posix_kill($client->getPid(), SIGTERM);
$this->console("Process {$client->getPid()} is killed!");
}
}
/**
* Run the server
*/
public function run()
{
$this->console("Start running...");
while(true)
{
$changed_sockets = $this->sockets;
#socket_select($changed_sockets, $write = NULL, $except = NULL, 1);
foreach($changed_sockets as $socket)
{
if($socket == $this->master)
{
if(($acceptedSocket = socket_accept($this->master)) < 0)
{
$this->console("Socket error: ".socket_strerror(socket_last_error($acceptedSocket)));
}
else
{
$this->connect($acceptedSocket);
}
}
else
{
$this->console("Finding the socket that associated to the client...");
$client = $this->getClientBySocket($socket);
if($client)
{
$this->console("Receiving data from the client");
$data=null;
while($bytes = #socket_recv($socket, $r_data, 2048, MSG_DONTWAIT))
{
$data.=$r_data;
}
if(!$client->getHandshake())
{
$this->console("Doing the handshake");
if($this->handshake($client, $data))
$this->startProcess($client);
}
elseif($bytes === 0)
{
$this->console("disconnecting...");
$this->disconnect($client);
}
else
{
$data = (string)$this->unmask($data);
$data = json_decode($data);
if( $data != "" )$this->console("Got data from user: |" . implode(',',$data) . "|");
// When received data from client
if( $data[0] == "setName" )//set name, color for user with given id
{
$id = $client->getId();
$colors = array( '990099','660099','66ccff','339933','99ff00','ccff00','663300','cc0000' );
//$col = '#'.$colors[ rand(0, sizeof($colors)-1) ];
$col = '#'.$colors[ sizeof( $this->clients ) ];
$this->console( "Setting attributes for user $id | ". $data[1] ." | $col |" );
$client->setName( $data[1] );
$client->setColor( $col );
}
else if( $data != "" )
{
$counter = 0 ;
$id = $client->getId();
$name = null ;
$color = null ;
//while($name == null)
if(1)
{
foreach ($this->clients as $c)
{
if( $c->getId() == $id )
{
$name = $c->getName();
$color = $c->getColor();
break;
}
}
++$counter;
}
foreach ($this->clients as $c)
{
$this->send( $c, json_encode( array(
$name . "[$counter]",
$color,
$data[1] )
) );
}
}
}
}
}
}
}
}
/**
* Start a child process for pushing data
* #param unknown_type $client
*/
private function startProcess($client)
{
$this->console("Start a client process");
$pid = pcntl_fork();
if($pid == -1)
{
die('could not fork');
}
elseif($pid)
{ // process
$client->setPid($pid);
}
else
{
if( sizeof( $this->clients ) > $this->maxConnections )
{
$this->send( $client, json_encode(array("No free slots!")) );
$this->disconnect($client);
}
else
$this->send($client, json_encode($client->getId()));
// we are the child
while(false)
{
//if the client is broken, exit the child process
if($client->exists==false)
{
break;
}
// push something to the client
$seconds = rand(2, 5);
$this->send($client, "I am waiting {$seconds} seconds");
sleep($seconds);
}
}
}
/**
* Send a text to client
* #param $client
* #param $text
*/
private function send($client, $text) {
$this->console("Send '".$text."' to client #{$client->getId()}");
$text = $this->encode($text);
if( socket_write($client->getSocket(), $text, strlen($text)) === FALSE )
{
$client->exists=false; //flag the client as broken
$this->console("Unable to write to client #{$client->getId()}'s socket");
$this->disconnect($client);
}
else
$this->console ("data sent!");
}
/**
* Encode a text for sending to clients via ws://
* #param $text
* #param $messageType
*/
function encode($message, $messageType='text') {
switch ($messageType) {
case 'continuous':
$b1 = 0;
break;
case 'text':
$b1 = 1;
break;
case 'binary':
$b1 = 2;
break;
case 'close':
$b1 = 8;
break;
case 'ping':
$b1 = 9;
break;
case 'pong':
$b1 = 10;
break;
}
$b1 += 128;
$length = strlen($message);
$lengthField = "";
if ($length < 126) {
$b2 = $length;
} elseif ($length <= 65536) {
$b2 = 126;
$hexLength = dechex($length);
//$this->stdout("Hex Length: $hexLength");
if (strlen($hexLength)%2 == 1) {
$hexLength = '0' . $hexLength;
}
$n = strlen($hexLength) - 2;
for ($i = $n; $i >= 0; $i=$i-2) {
$lengthField = chr(hexdec(substr($hexLength, $i, 2))) . $lengthField;
}
while (strlen($lengthField) < 2) {
$lengthField = chr(0) . $lengthField;
}
} else {
$b2 = 127;
$hexLength = dechex($length);
if (strlen($hexLength)%2 == 1) {
$hexLength = '0' . $hexLength;
}
$n = strlen($hexLength) - 2;
for ($i = $n; $i >= 0; $i=$i-2) {
$lengthField = chr(hexdec(substr($hexLength, $i, 2))) . $lengthField;
}
while (strlen($lengthField) < 8) {
$lengthField = chr(0) . $lengthField;
}
}
return chr($b1) . chr($b2) . $lengthField . $message;
}
/**
* Unmask a received payload
* #param $buffer
*/
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;
}
/**
* Print a text to the terminal
* #param $text the text to display
* #param $exit if true, the process will exit
*/
private function console($text, $exit = false) {
$text = date('[Y-m-d H:i:s] ').$text."\r\n";
if($exit)
die($text);
if($this->verboseMode)
echo $text;
}
}
error_reporting(E_ALL);
require_once 'client.php';
set_time_limit(0);
// variables
$address = '127.0.0.1';
$port = 5001;
$verboseMode = true;
$server = new Server($address, $port, $verboseMode);
$server->run();
?>
Client:
<?php
/**
* Define a Client object
* #author Sann-Remy Chea <http://srchea.com>
* #license This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
* #version 0.1
*/
class Client {
private $id;
private $socket;
private $handshake;
private $pid;
private $name;
private $color;
public $exists=true; //to check if client is broken
function Client($id, $socket) {
$this->id = $id;
$this->socket = $socket;
$this->handshake = false;
$this->pid = null;
}
public function setName( $n )
{
$this->name = $n;
}
public function getName()
{
return $this->name;
}
public function setColor( $c )
{
$this->color = $c;
}
public function getColor()
{
return $this->color;
}
public function getId() {
return $this->id;
}
public function getSocket() {
return $this->socket;
}
public function getHandshake() {
return $this->handshake;
}
public function getPid() {
return $this->pid;
}
public function setId($id) {
$this->id = $id;
}
public function setSocket($socket) {
$this->socket = $socket;
}
public function setHandshake($handshake) {
$this->handshake = $handshake;
}
public function setPid($pid) {
$this->pid = $pid;
}
}
?>
Index:
<!DOCTYPE html>
<html lang="pl">
<head>
<title>Websockets</title>
<meta name="BB" content="" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta charset="UTF-8" />
<style>
html, body
{
padding: 0;
margin: 0;
}
#out
{
width:840px;
height:500px;
mragin-right: -20px;
color:#FFF;
font-family: Courier New;
overflow-x: hidden;
overflow-y: auto;
}
#outwrap
{
width:800px;
height:500px;
margin:40px auto 0 auto;
display: block;
border: 1px solid #F00;
background-color: #000;
padding: 10px;
overflow:hidden;
}
#field
{
width:820px;
margin:10px auto;
display: block;
background-color: #000;
color: #FFF;
height: 50px;
}
#btn
{
background-color: #000;
margin:0 auto;
display:block;
width:100px;
height:50px;
font-size:20px;
border-radius: 10px;
}
#btn:hover
{
background-color: #666;
}
</style>
<script>
"use strict";
function $(id){return document.getElementById(id);}
document.addEventListener("DOMContentLoaded",function(){
var name = "";
while( name === "" || name === null )name = window.prompt( "Name" );
var myId = '';
$("field").value = "";
$("field").focus();
//var colors = ['990099','660099','66ccff','339933','99ff00','ccff00','663300','cc0000'];
//var myColor = '#' + colors[Math.floor( Math.random()*colors.length )] ;
//$("btn").style.color = myColor ;
var ws = new WebSocket('ws://127.0.0.1:5001');
ws.onopen = function(msg) {
write( "connected as "+ name +"!" );
ws.send( JSON.stringify( new Array( "setName",name ) ) );
$("field").focus();
//ws.send( name+": connected" );
//write('Connection successfully opened (readyState ' + this.readyState+')');
};
ws.onclose = function(msg) {
if(this.readyState === 2)write( "disconnected!" );
/*write(
'Closing... The connection is going throught'
+ 'the closing handshake (readyState '+this.readyState+')'
);*/
else if(this.readyState === 3)write( "disconnected!" );
/*write(
'Connection closed... The connection has been closed'
+ 'or could not be opened (readyState '+this.readyState+')'
);*/
else write( "disconnected!" );
//write('Connection closed... (unhandled readyState '+this.readyState+')');
};
ws.onerror = function(event) {
if(typeof (event.data) != 'undefined')write( "ERROR:" + event.data );
};
ws.onmessage = function(e){
var msg = JSON.parse(e.data);
msg = msg.toString().split(",");
if( msg.length == 1 )myId = msg.toString();
else
{
write( "<p style='color:"+ msg[1] +";'>"+ msg[0] +": "+ msg[2] +"</p>" );
}
console.log( "Server -> " + msg );
//write( "inc: " + msg );
};
$("field").addEventListener("keydown",function(e){
if( e.keyCode == 13 )
{
$("btn").click();
}
});
$("btn").addEventListener("click",function(){
if( ws.readyState === 1 )
ws.send( JSON.stringify( new Array( myId, $("field").value ) ) );
else write( "Couldn't send the message..." );
$("field").value = "";
$("field").focus();
});
window.onbeforeunload = function()
{
//ws.send( name+": disconnected" );
ws.close();
}
function write( info )
{
//$("out").innerHTML = info + "<br />" + $("out").innerHTML;
var p = document.createElement( "p" );
p.innerHTML = info ;
$("out").appendChild( p );
$("out").scrollTop = $("out").scrollHeight;
}
});
</script>
</head>
<body>
<div id="outwrap">
<div id="out"></div>
</div>
<input type="text" id="field" />
<input type="button" value="SEND" id="btn" />
</body>
</html>
If you uncomment while on line 278(server.php) and comment if a line below: In case about which I'm talking about the loop goes infinite...

You are making the common (beginners?) mistake to assume that writing/reading to/from a TCP socket does send/receive the amount of bytes the calls were told to send/receive. This is not necessarily the case.
It is essential to inspect the value those functions (socket_recv()/socket_send()) return and loop around them until all data had been sent/received.

Related

Why does not working my websocket php+javascript code?

I found some script on the internet. I've tried to configure it, but it doesn't work for me. I added it, I'm not too knowledgeable with the websocket functions, so please help me, if you can! When I tried to send a message to the server, I always get the same error code:
"InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state."
The script doesn't product the else what includes this comment:
// clients who are connected with server will enter into this case
// first client will handshake with server and then exchange data with server
I don't know why, but I really would like to understand this stuff. Can help me someone? (The handshake is OK. If it was not be OK, I would get a javascript error. )
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$address = "127.0.0.1";
$port = "1234";
GLOBAL $clients;
GLOBAL $client_list;
// socket creation
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
if (!is_resource($socket))
console("socket_create() failed: ".socket_strerror(socket_last_error()), true);
if (!socket_bind($socket, $address, $port))
console("socket_bind() failed: ".socket_strerror(socket_last_error()), true);
if(!socket_listen($socket, 20))
console("socket_listen() failed: ".socket_strerror(socket_last_error()), true);
console("Server started on $address : $port\n\n");
$master = $socket;
$sockets = array($socket);
$counert = 0;
while(true) {
$changed = $sockets;
foreach($changed as $socket) {
if($socket == $master) {
$log = "Egyenlők!";
} else {
$log = "Nem egyenlők!";
}
//console($log);
if($socket == $master) {
// new client will enter in this case and connect with server
socket_select($changed,$write=NULL,$except=NULL,NULL);
console("Master Socket Changed.\n\n");
$client = socket_accept($master);
if($client < 0) {
console("socket_accept() failed\n\n");
continue;
} else {
console("Connecting socket.\n\n");
fnConnectacceptedSocket($socket,$client); $master=null;
}
} else {
// clients who are connected with server will enter into this case
// first client will handshake with server and then exchange data with server
$client = getClientBySocket($socket);
if($client) {
if ($clients[$socket]["handshake"] == false) {
$bytes = #socket_recv($client, $data, 2048, MSG_DONTWAIT);
if ((int)$bytes == 0)
continue;
console("Handshaking headers from client:".$data);
if (handshake($client, $data, $socket))
$clients[$socket]["handshake"] = true;
} else if ($clients[$socket]["handshake"] == true) {
$bytes = #socket_recv($client, $data, 2048, MSG_DONTWAIT);
if ($data != "") {
$decoded_data = unmask($data);
socket_write($client, encode("You have entered: ".$decoded_data));
console("Data from client:".$decoded_data);
socket_close($socket);
}
}
}
}
}
}
# Close the master sockets
socket_close($socket);
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;
}
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;
}
function fnConnectacceptedSocket($socket, $client) {
GLOBAL $clients;
GLOBAL $client_list;
$clients[$socket]["id"] = uniqid();
$clients[$socket]["socket"] = $socket;
$clients[$socket]["handshake"] = false;
console("Accepted client \n\n");
$client_list[$socket] = $client;
}
function getClientBySocket($socket) {
GLOBAL $client_list;
return $client_list[$socket];
}
function handshake($client, $headers, $socket) {
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
$version = $match[1];
else {
console("The client doesn't support WebSocket");
return false;
}
if($version == 13) {
// Extract header variables
if(preg_match("/GET (.*) HTTP/", $headers, $match))
$root = $match[1];
if(preg_match("/Host: (.*)\r\n/", $headers, $match))
$host = $match[1];
if(preg_match("/Origin: (.*)\r\n/", $headers, $match))
$origin = $match[1];
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
$key = $match[1];
$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($client, $upgrade);
return true;
}
else {
console("WebSocket version 13 required (the client supports version {$version})");
return false;
}
}
function console($text) {
$text = $text . "\r\n";
$File = "log.txt";
$Handle = fopen($File, 'a');
fwrite($Handle, $text);
fclose($Handle);
}
?>
And this is the javascript code:
<script>
var socket;
var host = "ws://localhost:1234/chat/server.php";
function init() {
try {
socket = new WebSocket(host);
//log('WebSocket - status '+socket.readyState);
socket.onopen = function(msg){ socket.send("asdf"); log("Welcome - status "+this.readyState); };
socket.onmessage = function(msg){ log("Received: "+msg.data); };
socket.onclose = function(msg){ log("Disconnected - status "+this.readyState); };
}
catch(ex){ log(ex); }
$("msg").focus();
}
function send() {
var txt,msg;
txt = $("msg");
msg = txt.value;
if(!msg){ alert("Message can not be empty"); return; }
txt.value="";
txt.focus();
try{ socket.send(msg); log('Sent: '+msg); } catch(ex){ log(ex); }
}
function quit(){
log("Goodbye!");
socket.close();
socket=null;
}
// Utilities
function $(id){ return document.getElementById(id); }
function log(msg){ $("log").innerHTML+="\n"+msg; }
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>
How are you running the PHP code exactly? I tested it and at least something is happening here :). I'm not sure using PHP to implement a websocket server is the way to go though!

PHP sockets and streams, client data is unreadable in server-side after handshake

I'm trying to make simple sockets application with php. Okay so the principle is to handle n amount of chat rooms and multiple functions inside these rooms which involve client-to-client communication through sockets. So here's what I have so far:
Client connects, handshake with headers goes through ok
Sockets script stores clients in array, handles new connections and disconnections, keeps track of which client has which rooms(n:n) etc
Client side I'm using my own syntax how to control the sockets server for example join a room -> "delimiteraction:joindelimiterid:2delimiter", but the problem rises here. I can't get any human readable data from the socket after the handshake. I'm using putty to monitor the socket script and it shows mostly white bars where the data should be also the data seems to be different each time(same client message but different socket data), but length is mostly same(not a non-blocking issue already tried blocking)
I've tried to decode, encode, binary conversions, tried using both streams and sockets but nothing seems to work.
I'm new to sockets, but not to php.
Here's client code:
var delimiter = "maybeencodesomeofit";
var connection = new WebSocket('ws://myip:myport');
connection.onopen = function(e){//define handshake
alert("connection established");
var message = delimiter+"action:join"+delimiter+"userid:1"+"EOD_MARK";
connection.send("somegood10lobbyid:1somegood10body:"+message+"\r");
}
connection.onerror = function (error){
alert("connection errored");
console.log(error);
}
connection.onmessage = function(e){
alert("connection message");
}
connection.onclose = function(e){
alert("closed connection");
}
Here's server code:
<?php
set_time_limit(0);
// create a streaming socket, of type TCP/IP
$sock = stream_socket_server("tcp://myip:myport", $errno, $errstr);
stream_set_blocking($sock,TRUE);
// start listen for connections
// create a list of all the clients that will be connected to us..
// add the listening socket to this list
$clients = array($sock);
$incomplete_data = array();
$end_of_data = "EOD_MARK";
$clients_lobbies = array();
$received_header = array();
$handshakes = array();
echo phpversion();
//define all the lobbies to direct messages to,
$lobbies = array();
echo "Sockets initialized";
while (true) {
// create a copy, so $clients doesn't get modified by socket_select()
$read = $clients;
foreach($received_header as $key => $header){
if($header['time']+1 < microtime(true)){
$headers = [];
echo $header['data'];
$lines = preg_split("/\r\n/", $header['data']);
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')));
// create handshake header
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Accept: $secAccept\r\n\r\n";
// send handshake packet
fwrite($clients[$key], $upgrade);
$handshakes[$key] = true;
$unset_key = $key;
}
}
if(isset($unset_key)){
unset($received_header[$key]);
unset($unset_key);
}
//check incomplete data and delete the data after 5 seconds
$unset_keys = array();
foreach($incomplete_data as $key => $array){
if($array['last_update']+5 < microtime(true)){
$unset_keys[$key] = true;
}
}
foreach($unset_keys as $key => $arr){
unset($incomplete_data[$key]);
}
unset($unset_keys);
// get a list of all the clients that have data to be read from
// if there are no clients with data, go to next iteration
if (stream_select($read, $write = NULL, $except = NULL, 0) === FALSE)
continue;
// check if there is a client trying to connect
if (in_array($sock, $read)) {
// accept the client, and add him to the $clients array
$clients[] = $newsock = stream_socket_accept($sock);
$key = array_search($sock, $read);
// remove the listening socket from the clients-with-data array
unset($read[$key]);
}
// loop through all the clients that have data to read from
foreach ($read as $read_sock) {
// read until newline or 1024 bytes
$key = array_search($read_sock, $clients);
//echo "Data incoming";
// socket_read while show errors when the client is disconnected, so silence the error messages
$data = fread($read_sock, 4096);
//get the headers for the first handshake
if(!isset($handshakes[$key])){
//we need to handshake it first before we continue with normal operation
$received_header[$key]['data'] .= $data;
$received_header[$key]['time'] = microtime(true);
continue;
}
// check if the client is disconnected
if ($data === false) {
echo "Client disconnected";
// remove client from $clients array
$key = array_search($read_sock, $clients);
fclose($read_sock);
unset($clients[$key]);
//remove client from lobbies
if(isset($clients_lobbies[$key])){
foreach($clients_lobbies[$key] as $lobbyid => $boolean){
unset($lobbies[$lobbyid]['clients'][$key]);
}
}
unset($received_header[$key]);
unset($handshakes[$key]);
//remove clients indexed lobbies
unset($clients_lobbies[$key]);
unset($incomplete_data[$key]);
// continue to the next client to read from, if any
continue;
}
// trim off the trailing/beginning white spaces
//$data = trim($data);
// check if there is any data after trimming off the spaces
if (!empty($data)) {
$parsing_data = array();
echo $data;
//PARSE DATA HERE, Direct the messages into the lobbies according to headers, headers will tell if the user wants to join another lobby, what lobby the message is from etc,
if($key !== false && isset($handshakes[$key])){
if(isset($incomplete_data[$key])){
echo "incomplete data";
$data = $incomplete_data[$key]['data'].$data;
$incomplete_data[$key]['data'] = $data;
$incomplete_data[$key]['last_update'] = microtime(true);
}else{
$incomplete_data[$key]['data'] = $data;
$incomplete_data[$key]['last_update'] = microtime(true);
}
if(substr_compare($data,$end_of_data,-strlen($end_of_data) !== 0)){//check if data has arrived completely
$incomplete_data[$key]['data'] = $data;
$incomplete_data[$key]['last_update'] = microtime(true);
continue;
}else{
echo "Data is here.".$data;
//whole data is here
unset($incomplete_data[$key]);
$delimiter = substr($data,0,10);//get first 10 characters as delimiter;
$parsing_data = explode($delimiter,$data);
if(count($parsing_data) === 1){//something went wrong and there's no headers
}
//we can start doing magic here
$headers = array();
$body = "";
foreach($parsing_data as $header){ //parse headers last one will be with key "body" and contains the message to be written to participant sockets
$element = explode(":",$header);
if(count($element) === 2 && $element[0] !== 'body'){
$headers[strval($element[0])] = strval($element[1]);
}elseif($element[0] === 'body'){
$body = strval($element[1]);
}
}
if(isset($headers['lobbyid'])){//there's defined lobbyid to send message to
//forward the message
if(!isset($lobbies[$headers['lobbyid']])){
//create the lobby
$lobbies[$headers['lobbyid']] = array();
//join the lobby
$lobbies[$headers['lobbyid']]['clients'][$key] = $read_sock;
}
$clients_lobbies[$key][$headers['lobbyid']] = true;
// lobby exists -> forward the message to all, including the sender
foreach($lobbies[$headers['lobbyid']]['clients'] as $client_keys => $receivers){
if(isset($clients[$client_keys])){
fwrite($clients[$client_keys], $body);
}
}
}
}
}
}
} // end of reading foreach
}
// close the listening socket
socket_close($sock);
?>
I found the solution, you need to unmask the client-to-server data then re-encode, the unmask function needs to be able to handle multiple frames(nagle's algorithm). Code below will unmask multiple frames accordingly
Here's the code:
function unmask($payload){
$decMessages = Array();
do { // This should be running until all frames are unmasked and added to
$decMessages Array
$length = ord($payload[1]) & 127;
if($length == 126) {
$payload_len = 8;
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
$len = (ord($payload[2]) << 8) + ord($payload[3]);
}elseif($length == 127) {
$payload_len = 14;
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
$len = (ord($payload[2]) << 56) + (ord($payload[3]) << 48) +
(ord($payload[4]) << 40) + (ord($payload[5]) << 32) +
(ord($payload[6]) << 24) +(ord($payload[7]) << 16) +
(ord($payload[8]) << 8) + ord($payload[9]);
}else{
$payload_len = 6;
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
$len = $length;
}
$text = '';
for ($i = 0; $i < $len; ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
$decMessages[] = $text;
$payload = substr($payload, $payload_len+$len, strlen($payload));
}while (($len < strlen($data)) and $countert < 10);
return $decMessages;
}
function encode($message){
$length = strlen($message);
$bytesHeader = [];
$bytesHeader[0] = 129; // 0x1 text frame (FIN + opcode)
if ($length <= 125) {
$bytesHeader[1] = $length;
}else if($length >= 126 && $length <= 65535) {
$bytesHeader[1] = 126;
$bytesHeader[2] = ( $length >> 8 ) & 255;
$bytesHeader[3] = ( $length ) & 255;
}else{
$bytesHeader[1] = 127;
$bytesHeader[2] = ( $length >> 56 ) & 255;
$bytesHeader[3] = ( $length >> 48 ) & 255;
$bytesHeader[4] = ( $length >> 40 ) & 255;
$bytesHeader[5] = ( $length >> 32 ) & 255;
$bytesHeader[6] = ( $length >> 24 ) & 255;
$bytesHeader[7] = ( $length >> 16 ) & 255;
$bytesHeader[8] = ( $length >> 8 ) & 255;
$bytesHeader[9] = ( $length ) & 255;
}
$str = implode(array_map("chr", $bytesHeader)) . $message;
return $str;
}

fwrite() returns int(0) when called

I'm trying to use fcimg and wkhtmltoimage in order to export a chart to a PDF report. I keep getting an error from fcimg saying "Fatal error: Uncaught exception 'FCImageException' with message 'There was an error with wkhtmltopdf'[...]".
By debbuging the function inside de fcimg library I've noticed that a couple of 'fwrite()' functions in charge of writing the minified JS scripts of FusionCharts into the file are returning a value of '0' (no writing, but no error), even do the string to write is properly saved into a variable (as the code below). Is there some kind of limit or restrinction to the 'fwrite' function that I'm not aware of? Previous calls to the function work just fine. Any help or advice would be really appreciated.
[...]
function fusioncharts_to_image($outputFilePath, $swfName, $inputString, $height, $width, $options = array())
{
$jsonFlag = false;
if($inputString[0] === '<') // Check for the first character
{
// do nothing. jsonFlag is set to false anyways
}
else if($inputString[0] === "{")
{
$jsonFlag = true;
}
else
{
throw new FCImageException("The input string doesn't seem to be either JSON or XML");
}
$fileType = "png";
if(isset($options['imageType']))
$fileType = $options['imageType'];
$renderDelay = 1000;
if (isset($options['render_delay']) && is_numeric($options['render_delay']))
$renderDelay = $options['render_delay'];
/*
* Note: sys_get_temp_dir returns /tmp on Linux (don't know about osx)
* but on the other hand, it returns C:\user\foo\Local\Temp\ on Windows
*
* so we need to add a directory separator on Linux but not on windows
*/
$separator = "";
if(DIRECTORY_SEPARATOR === "/") // check if unix system. TODO: There will be better ways to do this
$separator = DIRECTORY_SEPARATOR;
$imageFileName = sys_get_temp_dir().$separator."\FCImage".rand(0, 1000).'.'.$fileType;
// $imageFileName = sys_get_temp_dir().”\FCImage”.rand(0, 1000).’.’.$fileType;
// $imageFileName = 'C:\PRUEBA\FCImage'.rand(0, 1000).'.'.$fileType;
$cwd = __DIR__; // change working directory to the current script's directory
$env = array(); // set any environment variables here
// configure the arguments
$args = "--format $fileType";
$args = $args." --crop-w ".($width - 1);
$args = $args." --crop-h ".($height - 1);
if(isset($options['nodelay']))
{
if($options['nodelay'] === true)
$args = $args." --javascript-delay 0";
}
else
{
$args = $args." --javascript-delay {$renderDelay}";
}
if(isset($options['quality']))
{
$args = $args." --quality {$options['quality']}";
}
$debugFlag = false;
if(isset($options['debug']))
{
$debugFlag = true;
$debugFile = fopen("debug.html", "w");
echo "\n\nCall to:\n fusioncharts_to_image ($outputFilePath, $swfName, [removing input string], $height, $width)";
}
// now, determine the platform this is running on
$os = php_uname("s");
$arch = php_uname("m");
if($os==="Windows NT"){
$platform = _FC_IMG_PLATFORM_WINDOWS;
// var_dump($platform);
}
else if($os === "Linux")
{
if($arch === "i386")
$platform = _FC_IMG_PLATFORM_LINUX;
else if($arch === "i686")
$platform = _FC_IMG_PLATFORM_LINUX;
else if ($arch === "x86_64")
$platform = _FC_IMG_PLATFORM_LINUX_64;
else
throw new FCImageException ("This Linux architecture is not supported");
}
else if($os === "Darwin") {
$platform = _FC_IMG_PLATFORM_OSX;
}
else
throw new FCImageException("Your server OS is currently not supported");
$fcRoot = dirname(__FILE__);
$wkCommand = $platform;
if(isset($options['wkhtmltoimage_path'])) {
$wkCommand = $options['wkhtmltoimage_path'];
}
$command = "$wkCommand $args - $imageFileName";
// var_dump($command); echo "<br>";
if($debugFlag)
{
echo "\n Command: $command";
}
$wkstdin = popen($command, "w");
// var_dump($wkstdin);
if(!is_resource($wkstdin))
{
throw new FCImageException("An error took place while trying to open wkhtmltopdf");
}
$templateHead = <<<TOP
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<style>
body{
padding:0 0 0 0;
margin:0 0 0 0;
}
</style>
<script>
TOP;
// ok. write template.txt into the process stdin
fwrite($wkstdin, $templateHead);
if($debugFlag)
{
fwrite($debugFile, $templateHead);
}
$contFC = file_get_contents($options['licensed_fusioncharts_js']);
$contFCC = file_get_contents($options['licensed_fusioncharts_charts_js']);
// echo $contFC;
// echo $contFCC;
if(isset($options['licensed_fusioncharts_charts_js']) && isset($options['licensed_fusioncharts_js'])) {
$temp1 = fwrite($wkstdin, $contFC);
var_dump($temp1);
$temp2 = fwrite($wkstdin, $contFCC);
var_dump($temp2);
if($debugFlag)
{
fwrite($debugFile, file_get_contents($options['licensed_fusioncharts_js']));
}
if($debugFlag)
{
fwrite($debugFile, file_get_contents($options['licensed_fusioncharts_charts_js']));
}
}
else {
throw new FCImageException("Need to provide fusioncharts licensed version here");
}
$functionToCall = "setXMLData";
if($jsonFlag === true)
$functionToCall = "setJSONData";
// replace all EOL with ""
$escapedData = str_replace("\n", "", $inputString);
$escapedData = addslashes($escapedData);
$templateCode = "</script>
</head>
<body>
<div id='chartContainer'><small>Loading chart...</small></div>
</body>
<script>
FusionCharts.setCurrentRenderer('javascript');
var chart = new FusionCharts('$swfName', 'chart0', '$width', '$height', '0', '1');
chart.$functionToCall('$escapedData');
chart.render('chartContainer');
</script>
</html>";
$temp = fwrite($wkstdin, $templateCode);
var_dump($temp);
if($debugFlag)
{
fwrite($debugFile, $templateCode);
}
$returnCode = pclose($wkstdin);
// echo(var_dump($returnCode)." returnCode");
if($returnCode !== 0)
{
// var_dump($imageFileName);
if(file_exists($imageFileName)){
unlink($imageFileName);
}
throw new FCImageException("There was an error with wkhtmltopdf ");
}
// success!
rename($imageFileName, $outputFilePath);
return true;
}

Why is my code not passing to my div?

I'm trying to create a gameserver query for my website, and I want it to load the content, save it, and echo it later. However, it doesn't seem to be echoing. It selects the element by ID and is supposed to echo the content of the VAR.
Here's my HTML code:
<center><div id="cstrike-map"><i class="fa fa-refresh fa-spin"></i> <b>Please wait ...</b><br /></div>
JavaScript:
<script type="text/javascript">
var map = "";
var hostname = "";
var game = "";
var players = "";
$.post( "serverstats-cstrike/cstrike.php", { func: "getStats" }, function( data ) {
map = ( data.map );
hostname = ( data.hostname );
game = ( data.game );
players = ( data.players );
}, "json");
function echoMap(){
document.getElementByID("cstrike-map");
document.write("<h5>Map: " + map + "</h5>");
}
</script>
PHP files:
query.php
/* SOURCE ENGINE QUERY FUNCTION, requires the server ip:port */
function source_query($ip)
{
$cut = explode(":", $ip);
$HL2_address = $cut[0];
$HL2_port = $cut[1];
$HL2_command = "\377\377\377\377TSource Engine Query\0";
$HL2_socket = fsockopen("udp://".$HL2_address, $HL2_port, $errno, $errstr,3);
fwrite($HL2_socket, $HL2_command); $JunkHead = fread($HL2_socket,4);
$CheckStatus = socket_get_status($HL2_socket);
if($CheckStatus["unread_bytes"] == 0)
{
return 0;
}
$do = 1;
while($do)
{
$str = fread($HL2_socket,1);
$HL2_stats.= $str;
$status = socket_get_status($HL2_socket);
if($status["unread_bytes"] == 0)
{
$do = 0;
}
}
fclose($HL2_socket);
$x = 0;
while ($x <= strlen($HL2_stats))
{
$x++;
$result.= substr($HL2_stats, $x, 1);
}
$result = urlencode($result); // the output
return $result;
}
/* FORMAT SOURCE ENGINE QUERY (assumes the query's results were urlencode()'ed!) */
function format_source_query($string)
{
$string = str_replace('%07','',$string);
$string = str_replace("%00","|||",$string);
$sinfo = urldecode($string);
$sinfo = explode('|||',$sinfo);
$info['hostname'] = $sinfo[0];
$info['map'] = $sinfo[1];
$info['game'] = $sinfo[2];
if ($info['game'] == 'garrysmod') { $info['game'] = "Garry's Mod"; }
elseif ($info['game'] == 'cstrike') { $info['game'] = "Counter-Strike: Source"; }
elseif ($info['game'] == 'dod') { $info['game'] = "Day of Defeat: Source"; }
elseif ($info['game'] == 'tf') { $info['game'] = "Team Fortress 2"; }
$info['gamemode'] = $sinfo[3];
return $info;
}
cstrike.php
include('query.php');
$ip = 'play1.darkvoidsclan.com:27015';
$query = source_query($ip); // $ip MUST contain IP:PORT
$q = format_source_query($query);
$host = "<h5>Hostname: ".$q['hostname']."</h5>";
$map = "<h5>Map: ".$q['map']."</h5>";
$game = "<h5>Game: ".$q['game']."</h5>";
$players = "Unknown";
$stats = json_encode(array(
"map" => $map,
"game" => $game,
"hostname" => $host,
"players" => $players
));
You need to display the response in the $.post callback:
$.post( "serverstats-cstrike/cstrike.php", { func: "getStats" }, function( data ) {
$("#map").html(data.map);
$("#hostname").html(data.hostname);
$("#game").html(data.game);
$("#players").html(data.players);
}, "json");
You haven't shown your HTML, so I'm just making up IDs for the places where you want each of these things to show.
There are some things that I can't understand from your code, and echoMap() is a bit messed up... but assuming that your php is ok it seems you are not calling the echomap function when the post request is completed.
Add echoMap() right after players = ( data.players );
If the div id you want to modify is 'cstrike-map' you could use jQuery:
Change the JS echoMap to this
function echoMap(){
$("#cstrike-map").html("<h5>Map: " + map + "</h5>");
}
So what I did was I had to echo the content that I needed into the PHP file, then grab the HTML content and use it.
That seemed to be the most powerful and easiest way to do what I wanted to do in the OP.
<script type="text/javascript">
$(document).ready(function(){
$.post("stats/query.cstrike.php", {},
function (data) {
$('#serverstats-wrapper-cstrike').html (data);
$('#serverstats-loading-cstrike').hide();
$('#serverstats-wrapper-cstrike').show ("slow");
});
});
</script>
PHP
<?php
include 'query.php';
$query = new query;
$address = "play1.darkvoidsclan.com";
$port = 27015;
if(fsockopen($address, $port, $num, $error, 5)) {
$server = $query->query_source($address . ":" . $port);
echo '<strong><h4 style="color:green">Server is online.</h4></strong>';
if ($server['vac'] = 1){
$server['vac'] = '<img src="../../images/famfamfam/icons/tick.png">';
} else {
$server['vac'] = '<img src="../../images/famfamfam/icons/cross.png">';
}
echo '<b>Map: </b>'.$server['map'].'<br />';
echo '<b>Players: </b>'.$server['players'].'/'.$server['playersmax'].' with '.$server['bots'].' bot(s)<br />';
echo '<b>VAC Secure: </b> '.$server['vac'].'<br />';
echo '<br />';
} else {
echo '<strong><h4 style="color:red">Server is offline.</h4></strong>';
die();
}
?>

dynamic checkbox by getting data from database and tick the checkbox accordingly

what i am trying to do is getting data from database and tick the checkbox according to that but i did not success any idea?
i am not sure what is the problem
JS code
function editPer(role_pk) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState === 4) {
var jsonObj = JSON.parse(xmlHttp.responseText);
document.getElementById("role_pk1").value = jsonObj.pk;
document.getElementById("role_name1").value = jsonObj.name;
for (var i = 1; i < 29; i++){
if (jsonObj.permission_fk+i != null)
document.getElementById("per"+i).checked = true;
else
document.getElementById("per"+i).checked = false;
}
}
};
xmlHttp.open("GET", "classes/controller/RoleController.php?action=getp&pk=" + role_pk, true);
xmlHttp.send();
/*
* Reset error message
* Show update role button
*/
resetErrorMessage("roleMessage");
showUpdateRoleButton();
$("#perModalDetail").modal('show');
};
PHP code RoleController.php
}else if ($action == "getp") {
if (isset($pk)) {
/*
* Select single Role value
*/
$itemArray = $roleDao->getp($pk);
if (count($itemArray) == 0) {
echo NULL;
} else {
echo json_encode($itemArray[0]);
}
}
PHP code in the class roleDao
public function getp($pk) {
$query = "SELECT pk, name FROM roles WHERE pk=".$pk.";";
$result = mysql_query($query, $this->dbconn);
$itemArray = array();
while ($row = mysql_fetch_row($result)) {
$item = array("pk" => $row[0], "name" => $row[1]);
array_push($itemArray, $item);
}
$query = "SELECT permission_fk FROM role_permission WHERE role_fk=".$pk.";";
$result = mysql_query($query, $this->dbconn);
$i=1;
while ($row = mysql_fetch_row($result)) {
$item = array("permission_fk$i" => $row[0]);
$i++;
array_push($itemArray, $item);
}
//print $itemArray;
return $itemArray;
}

Categories