WebSocket: client does not response to server - php

I am new to websocket.
I have made a simple websocket implementation on PHP. Handshaking works fine, and the connection is definitely on, since my server can get messages from client.
But, once I try to send message back to client, the client does not response at all.
Here is the code that mask messages and send it back to client.
function myProcess($user,$buffer)
{
//$buffer=unwrap($buffer);
console("Request Caught.");
console("Content Length: ".strlen($buffer));
console("Content: ".unmask($buffer));
console("First 8 bits: ".ord($buffer[0]));
console("Opcode: ".opcode($buffer,true));
//console("Full Text: ".$buffer);
//socket_write($user->socket,mask(unmask($buffer)));
console("Masking: ");
$tmp=unmask($buffer);
console($tmp);
$masked=mask($tmp);
opcode($masked);
for($i=0;$i<strlen($masked);$i++)
{
console($i.": ".ord($masked[$i]));
}
console("length: ".strlen($masked));
console("Sending Response: ");
console(socket_write($user->socket,$masked,strlen($masked)));
}
function mask($text)
{
$first8bit=0x81;
$header;
console("mask: payload length:".strlen($text));
if(strlen($text)<=125)
{
$header=pack("CC",$first8bit,strlen($text));
}
else if(strlen($text)<65536)
{
$header=pack("CCS",$first8bit,126,strlen($text));
}
else
{
$header=pack("CCN",$first8bit,127,strlen($text));
}
return $header.$text;
}
If the client send "hi" to server, server is going to simply reply "hi". And the captured packets with wireshark is 81 02 68 69. It seems the bits on the wire is just as the protocol says. If server trys to send a longer string, for example "hi from server", the client response with an error: undefined.
Can anyone help? Thanks a lot.
This is the code of server:
while(true){
$changed = $sockets;
socket_select($changed,$write=NULL,$except=NULL,NULL);
foreach($changed as $socket){
if($socket==$master){
console("Master Socket Changed.");
$client=socket_accept($master);
if($client<0){ console("socket_accept() failed"); continue; }
else{ connect($client); }
}
else{
console($socket." Socket Changed.");
$str=socket_read($socket,2048);
$user=getuserbysocket($socket);
if(strlen($str)==0) disconnect($socket);
else if(!$user->handshake){ dohandshake($user,$str); }
else
{
myProcess($socket,$str);
}
console("Comunication Ends Here.");
}
}
}
This is the calculation of accept section:
function calc_accept($key)
{
$tmp=$key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
$tmp=sha1($tmp,true);
//console($tmp);
$tmp=base64_encode($tmp);
//console($tmp);
return $tmp;
}
This does the handshake:
function dohandshake($user,$buffer){
console("\nRequesting handshake...");
console($buffer);
list($resource,$host,$origin,$strkey,$data) = getheaders($buffer);
console("Handshaking...");
$upgrade="HTTP/1.1 101 Switching Protocols\r\n".
"Upgrade: websocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Accept: ".calc_accept($strkey)."\r\n\r\n";
socket_write($user->socket,$upgrade.chr(0),strlen($upgrade.chr(0)));
$user->handshake=true;
console($upgrade);
console("Done handshaking...");
return true;
}
These 2 function gets the handshake of client and get user object by a socket:
function getheaders($req){
$r=$h=$o=null;
if(preg_match("/GET (.*) HTTP/" ,$req,$match)){ $r=$match[1]; }
if(preg_match("/Host: (.*)\r\n/" ,$req,$match)){ $h=$match[1]; }
if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key=$match[1]; }
if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; }
return array($r,$h,$o,$key,$data);
}
function getuserbysocket($socket){
global $users;
$found=null;
foreach($users as $user){
if($user->socket==$socket){ $found=$user; break; }
}
return $found;
}

The format string for pack looks wrong for messages between 126 and 65536 bytes. Can you try changing
"CCS" - unsigned short (always 16 bit, machine byte order)
to
"CCn" - unsigned short (16-bit) in "network" (big-endian) order

I solved it.
To anyone whom is struggling on the strange problem, this is the answer:
DELETE chr(0) of socket_write($user->socket,$upgrade.chr(0),strlen($upgrade.chr(0))) from handshake function.
I will still mark simnoc's answer as correct one, because he inspired me. Thanks

Related

PHP socket_select() always detects socket as available to read from

I've been searching back and forth looking for a solution to this problem I'm having. It appears that socket_select() will always find accepted sockets (resources returned by socket_accept()) as available to read from, even though there are no packets waiting. I'm sure there's a little something I forgot to do, but I can't figure out what that is.
Here's the relevant code:
while(TRUE){
$read_sockets=$this->client_sockets;
$write_sockets=NULL;
$except=NULL;
if(socket_select($read_sockets, $write_sockets, $except, NULL)<1) continue;
foreach($read_sockets as $pending_socket){
echo "Read request received...".PHP_EOL;
if($this->socket==$pending_socket){
$new_client_socket=socket_accept($this->socket);
socket_write($new_client_socket,$this->read_data($new_client_socket),2048);
$this->client_sockets[]=$new_client_socket;
echo "New client...".PHP_EOL;
} else {
$pending_socket_data=$this->read_data($pending_socket);
if($pending_socket_data===false){
unset($this->client_sockets[array_search($pending_socket,$this->client_sockets)]);
continue;
}
socket_write($pending_socket,$pending_socket_data,2048);
echo "Old client...".PHP_EOL;
}
}
}
private function read_data(&$socket){
echo 'Reading data...'.PHP_EOL;
$client_data='';$tmp='';
while(socket_recv($socket,$tmp,$this->max_length,0) > 0){
$client_data .= $tmp;
}
return $client_data;
}
I know I'm not catching some exceptions, as socket_recv === FALSE, but this code is just a proof of concept, that I had to get down to while debugging the main code.
$this->socket is the server socket, and is non-blocking.
Thank's in advance!
private function read_data(&$socket){
echo 'Reading data...'.PHP_EOL;
$client_data='';$tmp='';
while(socket_recv($socket,$tmp,$this->max_length,0) > 0){
$client_data .= $tmp;
}
return $client_data;
}
Method read_data never return false, so the codes below will never run.
if($pending_socket_data===false){
unset($this->client_sockets[array_search($pending_socket,$this->client_sockets)]);
continue;
}
When socket_recv return false ,you should check socket_last_error() and decide if to socket_close($pending_socket) and unset($this->client_sockets[..]).
When socket_recv return 0, may be socket closed and you should call unset($this->client_sockets[..]).

How to know whenever the connection is reset by peer in php?

I have been working lately on building a TCP server using PHP (I know wrong choice to begin with but this is the work standard), so I have reached a point where there is a reliable prototype to do tests on it and it showed good results. at start I used socket functions to handle to connection for server and it was working good but one of the main things on the project is to make the channel secured so I switched to stream_socket.
what I want is a socket_last_error equivalent in stream_socket group so I can know whenever the connection with client is closed or not. the current situation all processes will wait for timeout timer to release even tho the client is already closed.
I have searched the net and I found that there is no way to figure it out through PHP and I have found that some people opened issue ticket about it asking for socket_last_error equivalent for stream.
https://bugs.php.net/bug.php?id=34380
so is there anyway to know whenever FIN_WAIT signal is raised or not?
Thank you,
I don't think it's possible the stream_socket family, it looks like it's too high level.
I tried making a very hackish solution, I don't know if it will work for you, it's not very reliable:
<?php
set_error_handler('my_error_handler');
function my_error_handler($no,$str,$file,$line) {
throw new ErrorException($str,$no,0,$file,$line);
}
$socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr);
if (!$socket) {
echo "$errstr ($errno)\n";
} else {
while ($conn = stream_socket_accept($socket)) {
foreach (str_split('The local time is ' . date('n/j/Y g:i a') . "\n") as $char) {
echo $char;
try {
fwrite($conn,$char);
} catch (ErrorException $e) {
if (preg_match("/^fwrite\(\): send of 1 bytes failed with errno=([0-9]+) ([A-Za-z \/]+)$/",$e->getMessage(), $matches)) {
list($errno,$errstr) = array((int) $matches[1], $matches[2]);
if ($errno === 32) {
echo "\n[ERROR] $errstr"; // Broken pipe
}
}
echo "\n[ERROR] Couldn't write more on $conn";
break;
}
fflush($conn);
}
fclose($conn);
}
fclose($socket);
}
echo "\n";
?>
Launch: php ./server.php
Connect: nc localhost 8000 | head -c1
Server output:
The loca
[ERROR] Broken pipe
[ERROR] Couldn't write more on Resource id #6

PHP checking if server is alive

I need to check if a group of servers, routers and switches is alive. I have been searching for something reliable that would work with IPs and ports for over an hour now, could anyone help?
Ended up using
function ping($addr, $port='') {
if(empty($port)) {
ob_start();
system('ping -c1 -w1 '.$addr, $return);
ob_end_clean();
if($return == 0) {
return true;
} else {
return false;
}
} else {
$fp = fsockopen("udp://{$addr}", $port, $errno, $errstr);
if (!$fp) {
return false;
} else {
return true;
}
}
}
Servers, routers and switches...the one commonality that all of them share is the ability to accept SNMP requests if an SNMP service is running. Sounds like what you are trying to do is implement a funny workaround to a monitoring system (nagios, etc....)
As per: http://php.net/manual/en/book.snmp.php
<?php
$endpoints = array('10.0.0.1','10.0.0.2','10.0.0.3','10.0.0.4','10.0.0.5');
foreach ($endpoints as $endpoint) {
$session = new SNMP(SNMP::VERSION_2c, $endpoint, 'boguscommunity');
var_dump($session->getError());
// do something with the $session->getError() if it exists else, endpoint is up
}
?>
This will tell you if the endpoint is alive and the SNMP service is running. Specific to seeing if the port is available / open, you can use fsockopen():
http://php.net/manual/en/function.fsockopen.php
<?php
$fp = fsockopen("udp://127.0.0.1", 13, $errno, $errstr);
if (!$fp) {
echo "ERROR: $errno - $errstr<br />\n";
}
?>
$ip = "123.456.789.0";
$ping = exec("ping -c 1 -s 64 -t 64 ".$ip);
var_dump($ping);
// and so forth.
if you are checking for mysql you can use something like
if (mysqli_ping($ip)) echo "sweet!";
else echo "oh dam";
I would be a little concerned using the ping option as that can be blocked, and out of no where ...

Determine in php script if connected to internet?

How can I check if I'm connected to the internet from my PHP script which is running on my dev machine?
I run the script to download a set of files (which may or may not exist) using wget. If I try the download without being connected, wget proceeds to the next one thinking the file is not present.
<?php
function is_connected()
{
$connected = #fsockopen("www.example.com", 80);
//website, port (try 80 or 443)
if ($connected){
$is_conn = true; //action when connected
fclose($connected);
}else{
$is_conn = false; //action in connection failure
}
return $is_conn;
}
?>
You can always ping good 'ol trusty google:
$response = null;
system("ping -c 1 google.com", $response);
if($response == 0)
{
// this means you are connected
}
This code was failing in laravel 4.2 php framework with an internal server 500 error:
<?php
function is_connected()
{
$connected = #fsockopen("www.some_domain.com", 80);
//website, port (try 80 or 443)
if ($connected){
$is_conn = true; //action when connected
fclose($connected);
}else{
$is_conn = false; //action in connection failure
}
return $is_conn;
}
?>
Which I didn't want to stress myself to figure that out, hence I tried this code and it worked for me:
function is_connected()
{
$connected = fopen("http://www.google.com:80/","r");
if($connected)
{
return true;
} else {
return false;
}
}
Please note that: This is based upon the assumption that the connection to google.com is less prone to failure.
The accepted answer did not work for me. When the internet was disconnected it threw a php error. So I used it with a little modification which is below:
if(!$sock = #fsockopen('www.google.com', 80))
{
echo 'Not Connected';
}
else
{
echo 'Connected';
}
Why don't you fetch the return code from wget to determine whether or not the download was successful? The list of possible values can be found at wget exit status.
On the other hand, you could use php's curl functions as well, then you can do all error tracking from within PHP.
There are various factors that determine internet connection. The interface state, for example. But, regardles of those, due to the nature of the net, proper configuration does not meen you have a working connection.
So the best way is to try to download a file that you’re certain that exists. If you succeed, you may follow to next steps. If not, retry once and then fail.
Try to pick one at the destination host. If it’s not possible, choose some major website like google or yahoo.
Finally, just try checking the error code returned by wget. I bet those are different for 404-s and timeouts. You can use third parameter in exec call:
string exec ( string $command [, array &$output [, int &$return_var ]] )
/*
* Usage: is_connected('www.google.com')
*/
function is_connected($addr)
{
if (!$socket = #fsockopen($addr, 80, $num, $error, 5)) {
echo "OFF";
} else {
echo "ON";
}
}
Also note that fopen and fsockopen are different. fsockopen opens a socket depending on the protocol prefix. fopen opens a file or something else e.g file over HTTP, or a stream filter or something etc. Ultimately this affects the execution time.
You could ping to a popular site or to the site you're wgetting from (like www.google.nl) then parse the result to see if you can connect to it.
<?php
$ip = '127.0.0.1'; //some ip
exec("ping -n 4 $ip 2>&1", $output, $retval);
if ($retval != 0) {
echo "no!";
}
else
{
echo "yes!"; }
?>
Just check the result of wget. A status code of 4 indicates a network problem, a status code of 8 indicates a server error (such as a 404). This only works if you call wget for each file in sequence, rather than once for all the files.
You can also use libcurl with PHP, instead of calling wget. Something like:
foreach (...) {
$c = curl_init($url);
$f = fopen($filepath, "w")
curl_setopt($c, CURLOPT_FILE, $f);
curl_setopt($c, CURLOPT_HEADER, 0);
if (curl_exec($c)) {
if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) {
// success
} else {
// 404 or something, delete file
unlink($filepath);
}
} else {
// network error or server down
break; // abort
}
curl_close($c);
}
This function handles what you need
function isConnected()
{
// use 80 for http or 443 for https protocol
$connected = #fsockopen("www.example.com", 80);
if ($connected){
fclose($connected);
return true;
}
return false;
}
You can use this by adding this inside a class:
private $api_domain = 'google.com';
private function serverAliveOrNot()
{
if($pf = #fsockopen($this->api_domain, 443)) {
fclose($pf);
$_SESSION['serverAliveOrNot'] = true;
return true;
} else {
$_SESSION['serverAliveOrNot'] = false;
return false;
}
}
+1 on Alfred's answer, but I think this is an improved version:
function hasInternet()
{
$hosts = ['1.1.1.1', '1.0.0.1', '8.8.8.8', '8.8.4.4'];
foreach ($hosts as $host) {
if ($connected = #fsockopen($host, 443)) {
fclose($connected);
return true;
}
}
return false;
}
My reasons:
This pings more than 1 server and will only fail if all 4 fails
If first host works, it will return true immediately
IP addresses are from CloudFlare and Google DNS which basically controls most of the internet and always online
1.1.1.1 is rated the fastest DNS resolver (Source)
Only doubt I have is to use port 443 or 80? Suggestions would be appreciated! :)
Very PHP way of doing it is
<?php
switch (connection_status())
{
case CONNECTION_NORMAL:
$txt = 'Connection is in a normal state';
break;
case CONNECTION_ABORTED:
$txt = 'Connection aborted';
break;
case CONNECTION_TIMEOUT:
$txt = 'Connection timed out';
break;
case (CONNECTION_ABORTED & CONNECTION_TIMEOUT):
$txt = 'Connection aborted and timed out';
break;
default:
$txt = 'Unknown';
break;
}
echo $txt;
?>
https://www.w3schools.com/php/func_misc_connection_status.asp

PHP script to show google ranking results

does anyone know if it is possible to display google page rank of a particular website using php script?
if it is possible, how do i do it?
Okay, i re-wrote my Answer and extracted only the relevant part of my SEO Helper (my previous version had other stuff like Alexa Rank, Google Index, Yahoo Links etc in it. If you are looking for that, just see check an older revision of this answer!)
Please be aware that there are pages that have NO PAGERANK and by no I DON'T MEAN ZERO. There is just none. This may be because the page is so very unimportant (even less im portant than PR 0) or just so new but might very well be important.
This is consiedered the same as PR 0 in my class!
This has some pros and some cons. If possible you should handle it seperately in your logic, but this is not always possible, so 0 is the next best approach.
Furthermore:
This code is reverse engeneered and does not utilize some sort of API that has any form of SLA or whatever.
So it might stop working ANY TIME!
And PLEASE DONT FLOOD GOOGLE!
I made the test. If you have only a very short period of sleep, google blocks you after 1000 requests (for quite some time!). With a random sleep between 1.5 and 2 secs it looks fine.
I once crawled the pagerank for 70k pages. Only once, because I just needed it. I did only 5k a day from several IPs and now i have the data and It doesnt get outdated because the pages are there for decades.
IMO its totally OK to check a pagerank once in a while or even some at once, but dont miss-use this code or google may lock us out all together!
<?php
/*
* #author Joe Hopfgartner <joe#2x.to>
*/
class Helper_Seo
{
protected function _pageRankStrToNum($Str,$Check,$Magic) {
$Int32Unit=4294967296;
// 2^32
$length=strlen($Str);
for($i=0;$i<$length;$i++) {
$Check*=$Magic;
//If the float is beyond the boundaries of integer (usually +/- 2.15e+9 = 2^31),
// the result of converting to integer is undefined
if($Check>=$Int32Unit) {
$Check=($Check-$Int32Unit*(int)($Check/$Int32Unit));
//if the check less than -2^31
$Check=($Check<-2147483648)?($Check+$Int32Unit):$Check;
}
$Check+=ord($Str {
$i
});
}
return $Check;
}
/*
* Genearate a hash for a url
*/
protected function _pageRankHashURL($String) {
$Check1=self::_pageRankStrToNum($String,0x1505,0x21);
$Check2=self::_pageRankStrToNum($String,0,0x1003F);
$Check1>>=2;
$Check1=(($Check1>>4)&0x3FFFFC0)|($Check1&0x3F);
$Check1=(($Check1>>4)&0x3FFC00)|($Check1&0x3FF);
$Check1=(($Check1>>4)&0x3C000)|($Check1&0x3FFF);
$T1=(((($Check1&0x3C0)<<4)|($Check1&0x3C))<<2)|($Check2&0xF0F);
$T2=(((($Check1&0xFFFFC000)<<4)|($Check1&0x3C00))<<0xA)|($Check2&0xF0F0000);
return($T1|$T2);
}
/*
* genearate a checksum for the hash string
*/
protected function CheckHash($Hashnum) {
$CheckByte=0;
$Flag=0;
$HashStr=sprintf('%u',$Hashnum);
$length=strlen($HashStr);
for($i=$length-1;$i>=0;$i--) {
$Re=$HashStr {
$i
};
if(1===($Flag%2)) {
$Re+=$Re;
$Re=(int)($Re/10)+($Re%10);
}
$CheckByte+=$Re;
$Flag++;
}
$CheckByte%=10;
if(0!==$CheckByte) {
$CheckByte=10-$CheckByte;
if(1===($Flag%2)) {
if(1===($CheckByte%2)) {
$CheckByte+=9;
}
$CheckByte>>=1;
}
}
return '7'.$CheckByte.$HashStr;
}
public static function getPageRank($url) {
$fp=fsockopen("toolbarqueries.google.com",80,$errno,$errstr,30);
if(!$fp) {
trigger_error("$errstr ($errno)<br />\n");
return false;
}
else {
$out="GET /search?client=navclient-auto&ch=".self::CheckHash(self::_pageRankHashURL($url))."&features=Rank&q=info:".$url."&num=100&filter=0 HTTP/1.1\r\n";
$out.="Host: toolbarqueries.google.com\r\n";
$out.="User-Agent: Mozilla/4.0 (compatible; GoogleToolbar 2.0.114-big; Windows XP 5.1)\r\n";
$out.="Connection: Close\r\n\r\n";
fwrite($fp,$out);
#echo " U: http://toolbarqueries.google.com/search?client=navclient-auto&ch=".$this->CheckHash($this->_pageRankHashURL($url))."&features=Rank&q=info:".$url."&num=100&filter=0";
#echo "\n";
//$pagerank = substr(fgets($fp, 128), 4);
//echo $pagerank;
#echo "DATA:\n\n";
$responseOK = false;
$response = "";
$inhead = true;
$body = "";
while(!feof($fp)) {
$data=fgets($fp,128);
if($data == "\r\n" && $inhead) {
$inhead = false;
} else {
if(!$inhead) {
$body.= $data;
}
}
//if($data == '\r\n\r\n')
$response .= $data;
if(trim($data) == 'HTTP/1.1 200 OK') {
$responseOK = true;
}
#echo "D ".$data;
$pos=strpos($data,"Rank_");
if($pos===false) {
}
else {
$pagerank=trim(substr($data,$pos+9));
if($pagerank === '0') {
fclose($fp);
return 0;
} else if(intval($pagerank) === 0) {
throw new Exception('couldnt get pagerank from string: '.$pagerank);
//trigger_error('couldnt get pagerank from string: '.$pagerank);
fclose($fp);
return false;
} else {
fclose($fp);
return intval( $pagerank );
}
}
}
fclose($fp);
//var_dump($body);
if($responseOK && $body=='') {
return 0;
}
//return 0;
throw new Exception('couldnt get pagerank, unknown error. probably google flood block. my tests showed that 1req/sec is okay! i recommend a random sleep between 1.5 and 2 secs. no sleep breaks at ~1000 reqs.');
//trigger_error('couldnt get pagerank, unknown error. probably google flood block.');
return false;
}
}
}
$url = "http://www.2xfun.de/";
$pagerank = Helper_Seo::getPagerank($url);
var_dump($pagerank);
?>

Categories