|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2010-02-16 10:34 UTC] magicaltux@php.net
Description:
------------
On a blocking stream, a call to fread() will return even if the passed
buffer size has not been reached.
A call to fread() should return immediatly if there is data pending to
be read (buffered by php). Instead of that, php will call poll() on
the stream to wait for more data to arrive, then will return the
previously read data and the new data.
Suggestion: if fread() is called on a blocking stream that already
contains data, PHP should call poll() with a 0 timeout, read any newly
available data and return immediatly.
If no data is currently in PHP's internal buffer, current behaviour
can be kept.
(it is also possible to skip completly the poll() part and directly
return any pending data without checking if the real stream has
anything, but I believe that it might not be as logical, a call to
fread() should read)
Reproduce code:
---------------
<?php
echo 'Testing PHP version: '.phpversion()."\n";
$pair = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
$pid = pcntl_fork();
if ($pid == -1) die("Failed to fork\n");
if ($pid > 0) {
// parent
fclose($pair[0]);
while(!feof($pair[1])) {
$start = microtime(true);
$data = fread($pair[1], 256);
printf("fread took %01.2fms to read %d bytes\n", (microtime(true)-$start)*1000, strlen($data));
}
exit;
}
// child
fclose($pair[1]);
while(!feof($pair[0])) {
fwrite($pair[0], "Hello 1\n"); // 8 bytes
usleep(5000);
fwrite($pair[0], str_repeat('a', 300)."\n"); // 301 bytes
sleep(1);
}
Expected result:
----------------
Testing PHP version: 5.3.1
fread took 0.09ms to read 8 bytes
fread took 5.08ms to read 256 bytes
fread took 0.00ms to read 45 bytes
fread took 1000.10ms to read 8 bytes
fread took 5.04ms to read 256 bytes
fread took 0.00ms to read 45 bytes
fread took 1000.10ms to read 8 bytes
fread took 5.04ms to read 256 bytes
(etc)
Actual result:
--------------
Testing PHP version: 5.3.1
fread took 0.09ms to read 8 bytes
fread took 5.08ms to read 256 bytes
fread took 1000.10ms to read 53 bytes
fread took 5.04ms to read 256 bytes
fread took 1000.10ms to read 53 bytes
fread took 5.04ms to read 256 bytes
(etc)
Patches51056-3.phpt.txt (last revision 2010-03-12 13:46 UTC by arnaud dot lb at gmail dot com)51056-2.phpt.txt (last revision 2010-03-11 01:05 UTC by arnaud dot lb at gmail dot com) 51056.phpt.txt (last revision 2010-03-11 01:04 UTC by arnaud dot lb at gmail dot com) Pull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Mon Oct 27 03:00:02 2025 UTC |
Hi, I know about fread() returning less data than asked for, however I could not modify this behaviour without passing some kind of value to lower-level read operation, which will call poll() if socket is blocking. When data is already available in buffer, an information should be passed to the lower-level read() to let it know it should not block. The only non-intrusive solution to fix this would be to temporarly pass socket in non-blocking mode if data was found in PHP buffer. Considering any application handling data from network should handle cases when received data is not complete, I believe it was best to return immediatly if data is found and let the application call fread() again rather than trying to workaround this problem with a dirty solution like passing temporarly in non- blocking mode. Another solution would be to add an argument to the internal read call ("do not block") however it would change the API for the internal stream api, and would require the argument to be handled into each stream wrapper.> Apache [...] uses timeouts [...] to detect dead clients This is what I was meaning :) (and I though you was meaning this too : "application handling data from network should handle cases when received data is not complete") Dead clients, or situations like this are not the "normal case", and sometimes this can be handled with timeouts. If you are in situations where this is the normal case, one solution is to use non blocking streams. The following code does exactly what you are asking for (if there is something to read, return it; else, block) : stream_set_blocking(..., 0); while (stream_select(...)) { $data = fread(...); } If it does not works with SSL streams, then SSL stuff should be fixed instead.I see your point in wanting read() behavior. Whether or not to implement fread() or read() one is arguable. However the specific behavior you are asking for is not reliable for several reasons, and IMHO (I may be wrong) you want this behavior for bad reasons. Let me explain this : > By the way using nonblocking mode makes no sense with provided example. It would just make the program use 100% cpu. This is why you don't want to use non-blocking streams. If you use stream_select() you will never end up using 100% CPU : Your PHP process will only do an idle wait in stream_select() and consume no CPU at all. Example : stream_set_blocking($stream, 0); while (stream_select($r,$w,$e, $stream, $sec, $usec)) { /* block until data is available for read and/or write in $stream. */ $data = fread($stream, 8192); /* read all available data, up to 8192 bytes. Returns only 1 byte if only 1 byte is available and never blocks. */ } > If end of email is reached while a read is in progress and a new read is called, it will block until the server closes connections With your patch (or with the read behavior you want) it will still block. And it will block randomly, in an unpredictable manner. Please see the following example : Say the buffer has 250 bytes in it. fread(100) -> buffer.length-=100, buffer.length == 100 fread(100) -> buffer.length-=100, buffer.length == 50 fread(100) -> with your patch it would return the last 50 available bytes Now this other example with a buffer with only 200 bytes in it : Say the buffer has 200 bytes in it. fread(100) -> buffer.length-=100, buffer.length == 100 fread(100) -> buffer.mength-=100, buffer.length == 0 fread(100) -> buffer is 0, this blocks, and you can't control this (you don't control the buffer, and don't know anything about it in a php script) Please see 51056-3.phpt. With current behavior it will block too, but in a predictable maner.A small correction: it's not that "never reads from the socket more than one packet at a time" as I and the manual say. It's that it does only one call to recv(). If we're blocking waiting for data and a packet arrives, then recv() will return only the contents of that packet ("The receive calls normally return any data available, up to the requested amount, rather than waiting for receipt of the full amount requested."). However, if several packets have been received since the last call to fread, recv() will return the most data it can, possibly several packets. But this is a minor documentation issue and not very relevant in this discussion.