Jump to content

How to retrieve results from a stream?


Go to solution Solved by kicken,

Recommended Posts

While this work, it doesn't seem right.  Please see my comments.  Thanks

<?php
$fp = fsockopen("73.11.165.217", 1337, $errno, $errstr, 30);
$msg=json_encode(['method'=>'test']);
$lng=pack("V", strlen($msg));
$out=$lng.$msg;
fwrite($fp, $out);
$prefix=fgets($fp, 5);  //Why not just 4?
$lng = unpack('Vlen', $prefix)['len'];
//Is buffering handled by PHP?
$rsp=fgets($fp, $lng+1);    //Why +1?
fclose($fp);
print_r(json_decode($rsp));

Output

stdClass Object ( [result] => success [id] => 123 ) 

 

 

 

Link to comment
https://forums.phpfreaks.com/topic/303810-how-to-retrieve-results-from-a-stream/
Share on other sites

4 should be right, there is no buffering involved, and you shouldn't need a +1. Why did you do those things?

 

var_dump(bin2hex($prefix)); // should be 0000xxyy, length = 256 * xx + yy
var_dump("*" . $rsp . "*"); // should be a complete json string *{"result":"success","id":123}*

I know, something is not right.

 

Note that I am using "V" with pack() which is unsigned long (always 32 bit, little endian byte order), so expected prefix is different than you showed.  It shows up as xxyy0000.  Looking at https://en.wikipedia.org/wiki/Endianness, it appears that "maybe" it should be yyxx0000?

 

This....
            $msg=json_encode(['method'=>'test','params'=>['guid'=>$config['guid']],'id'=>1]);
            echo('$msg=');
            var_dump($msg);
            $lng=pack("V", strlen($msg));
            echo('bin2hex($lng)=');
            var_dump(bin2hex($lng));
            fwrite($fp, $lng.$msg);
            $prefix=fgets($fp, 4);  //Why not just 4?
            echo('bin2hex($prefix)=');
            var_dump(bin2hex($prefix)); // should be 0000xxyy, length = 256 * xx + yy
            $lng = unpack('Vlen', $prefix)['len'];
            echo('$lng=');
            var_dump($lng);
            $rsp=fgets($fp, $lng+0);    //Why +1?
            echo('$rsp=');
            var_dump("*" . $rsp . "*");
            $rsp=json_decode($rsp)->result;
            fclose($fp);
produces the following.  Obviously, the important part is unpack(): Type V: not enough input, need 4, have 3.
$msg=string(81) "{"method":"test","params":{"guid":"0b74085b-03c9-4c2a-997f-fa554451a46c"},"id":1}"
bin2hex($lng)=string( "51000000"
bin2hex($prefix)=string(6) "170000"
<br />
<b>Warning</b>:  unpack(): Type V: not enough input, need 4, have 3 in <b>/var/www/html/index.php</b> on line <b>129</b><br />
$lng=NULL
<br />
<b>Warning</b>:  fgets(): Length parameter must be greater than 0 in <b>/var/www/html/index.php</b> on line <b>132</b><br />
$rsp=string(2) "**"
<br />
<b>Notice</b>:  Trying to get property of non-object in <b>/var/www/html/index.php</b> on line <b>135</b><br />
But when I only change 4 to 5 and add +1.
            $msg=json_encode(['method'=>'test','params'=>['guid'=>$config['guid']],'id'=>1]);
            echo('$msg=');
            var_dump($msg);
            $lng=pack("V", strlen($msg));
            echo('bin2hex($lng)=');
            var_dump(bin2hex($lng));
            fwrite($fp, $lng.$msg);
            $prefix=fgets($fp, 5);  //Why not just 4?
            echo('bin2hex($prefix)=');
            var_dump(bin2hex($prefix)); // should be 0000xxyy, length = 256 * xx + yy
            $lng = unpack('Vlen', $prefix)['len'];
            echo('$lng=');
            var_dump($lng);
            $rsp=fgets($fp, $lng+1);    //Why +1?
            echo('$rsp=');
            var_dump("*" . $rsp . "*");
            $rsp=json_decode($rsp)->result;
            fclose($fp);
produces this...
 
$msg=string(81) "{"method":"test","params":{"guid":"0b74085b-03c9-4c2a-997f-fa554451a46c"},"id":1}"
bin2hex($lng)=string( "51000000"
bin2hex($prefix)=string( "17000000"
$lng=int(23)
$rsp=string(25) "*{"result":"0123456789"}*"
Wireshark on this client appears to show correct data being received from the server.
 
The only place the server ever writes is below, and all appears to be correct.
    public function send($msg){
        //Do I need to parse???  Still need to implement drain
        if($this->isConnected()) {
            if($this->parseJson) {
                $msg=json_encode($msg);
            }
            if(!$this->socket->write(pack("V", strlen($msg)).$msg)) {
                syslog(LOG_ERR,"LengthPrefixStream::send() didn't write");
            }
            return true;
        }
        else {
            syslog(LOG_ERR,"LengthPrefixStream::send() not connected (should never happen). ".$msg);
            return false;
        }
    }
Any ideas where I should start looking?  Thanks

More information.  I just confirmed with the c++ client programmer that he didn't need to do my hack of adding the extra byte.  As such, I am 99% confident it has nothing to do with the server script.  Hopefully, I am not over confident :)

 

Side note.  Is it common to suppress errors/warnings for @fsockopen()?  While I didn't show it on my original post, I check for false and do the error check as described by the http://php.net/manual/en/function.fsockopen.php examples.

Note that I am using "V" with pack() which is unsigned long (always 32 bit, little endian byte order), so expected prefix is different than you showed.

Yeah, I knew it was LE but got it backwards in my post :sweat: yyxx0000 is correct

 

I wouldn't have expected fragmentation with such small packets... Try with blocking.

$fp = fsockopen(...);
stream_set_blocking($fp, true);
And no, I don't think it's normal to suppress errors on fsockopen: it's a call that should succeed and won't issue warnings with normal usage so I don't think @ is appropriate.

 

 

And a small correction - or at least what I think is a correction. There is a small bit of buffering done by PHP and/or the OS: when a packet arrives it does get buffered somewhere before it's read by the application (otherwise it would be lost, obviously). But that's more of a networking thing that it is a streams thing, so I wasn't considering that when I said there is no buffering involved. Technical detail.

No change in results :(

 

Wasn't thinking. Of course yyxx0000.

 

Testing server is set up to display warnings which causes fsockopen to issue a warning upon not being able to connect.  Would be nice to be able to use some version of @ to suppress warnings and not errors, but don't think it is possible.

  • Solution

You should be using fread($fp, 4), not fgets.

 

fgets stops reading on new lines which might exist in your binary representation of your length. For example if your length were 266 that'd be encoded as 0A010000. 0A is the byte for a new line so fgets would stop reading there and return only 1 bytes rather than the 4 you requested.

 

Always use fread() if you want a specific number of bytes read. Only use fgets if you want the special "read until end of line" behavior. Also you always need to check the return value of your read to see how much was read. Even with blocking operations it's possible for a read to return less data than you requested.

Edited by kicken
  • Like 2
This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.