Solution at the end of the question
I am writing a PHP application that sends a message to a server and then reads a response back in using stream_get_contents
. I communicate with the same server in an android app in the same way. The android app works fine and responds quickly, however the PHP hangs when reading the response back from the server.
In the code sample below, I have set a tiny buffer size of 5 bytes to test a theory. If I remove this buffer size it hangs, however with the 5 byte size it only hangs on the last pass through the loop:
stream_set_timeout($this->socket, 10); //10 seconds read timeout
while (!feof($this->socket)) {
$breakOut = false;
echo 'Reading response'.time().'<br/>';
$data = stream_get_contents($this->socket, 5);
echo 'Read response'.time().'<br/>';
if ($data === false) {
$this->latestErrStr = "Timed out waiting for a response.";
return false;
} else {
$index = strpos($data, chr(3));
if ($index !== FALSE){
$breakOut = true;
$data = substr($data, 0, $index);
}
$response .= $data;
}
$stream_meta_data = stream_get_meta_data($this->socket);
//If we have no EOF marker then break if there are no bytes left to read
if($breakOut || $stream_meta_data['unread_bytes'] <= 0) {
break;
}
}
The output is as follows:
Reading response1387463602
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463603
Reading response1387463603
Read response1387463623
As you can see there is a 10s delay between the last two lines, but no noticeable delay between the others.
Also for your information I use an ETX marker (3) to signify the end of a message, so I also stop if I hit this rather than just the end of the stream.
Am I doing something wrong? Is there a better way of doing this?
Thanks in advance...
Edit: Just to be clear, the above code is only expecting one message response. It does not care about any data that comes back after it has received an ETX byte.
Edit2: Hangs have been seen of up to 40 seconds now. It doesn't appear to be fixed to 10 seconds, but it weirdly seems to be nice round numbers every time.
Solution (thanks to chathux):
stream_get_contents($stream, $bytes)
will block until it receives $bytes
bytes or the timeout expires. This means my code was reaching the end and trying to read 5 bytes (which didn't exist), it was therefore waiting 10s before giving up.
As I know the minimum size of a message coming back to me is 49 bytes, I first read those 49 bytes (blocking until I get them or 10s expires) in order to populate the stream_get_meta_data
's unread_bytes
field. Once I have this I dynamically adjust the buffer size to min(16*1024, unread_bytes)
so I either read 16k at a time or all of the remaining bytes, whichever is smaller. In my case this usually only means two goes through the loop as messages are often tiny (49 bytes + payload).
The system now hangs for approximately 3 seconds instead of 10, but it hangs waiting for the initial few bytes to arrive (rather than at the end) which can be put down to network latency and other normal factors.
documentation says "stream_get_contents() operates on an already open stream resource and returns the remaining contents in a string, up to maxlength bytes and starting at the specified offset."
so when you provide 5 as maxlength it will only read up to five bytes and continues. if it cant read up to 5 bytes it`ll wait and expires in 10 seconds which you have mentioned in stream_set_timeout
example :
//server side statement<br/>
$data = stream_get_contents($this->socket, 5);
//corresponding client code<br/>
fwrite($client, "1234");
in above case server will wait till you write one more byte
fwrite($client, "5");