php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #21485 feof() doesn't return true on a non-blocking socket stream
Submitted: 2003-01-07 06:00 UTC Modified: 2003-12-11 09:46 UTC
Votes:2
Avg. Score:5.0 ± 0.0
Reproduced:2 of 2 (100.0%)
Same Version:2 (100.0%)
Same OS:1 (50.0%)
From: bjarne at sourcetech dot se Assigned: pollita (profile)
Status: Closed Package: Documentation problem
PHP Version: 4.3.0 OS: Win2K/Linux
Private report: No CVE-ID: None
 [2003-01-07 06:00 UTC] bjarne at sourcetech dot se
The following code doesn't work with php4.3.0

As foef() never returns true it got stuck in the while()-loop...

When running the same code in php4.2.3 it works as it should so it seems like a 4.3.0 issue...
( feof() returns true in 4.2.3... )

The scenario consists of the script connecting to a host which immediatly replies with a textstring; "OK", which in turn is printed out...

/Regards
Bjarne

// Open a socket and connects to the host
$fp = fsockopen ($myhost, $myport, $errno, $errstr, 30); 

// check for valid filepointer
if (!$fp) 
{ 
   echo "$errstr ($errno)<br>\n"; 
} 
else 
{ 
  // sets the socket to non-blocking
  socket_set_blocking($fp,FALSE); 
  
  // read data from socket upto EOF in 128 byte chunks
  while (!feof($fp)) // <= FAILS IN PHP 4.3.0!!!!!
  { 
    $buffer .= fgets ($fp,128); 
  } 
  // closing socket
  fclose ($fp); 
  
  // printing the buffer
  print "Buffer:" . $buffer;
}

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-01-08 21:40 UTC] iliaa@php.net
It seems that socket_set_blocking($fp,FALSE); results in other peculiar behaviour.
When connecting to SMTP server, the server sends some data across the socket right away, usually something identifying the server.

$fp = fsockopen("mail_server", 25);
var_dump(fgets($fp, 1024));
fclose($fp);
returns the server identify string

$fp = fsockopen("mail_server", 25);
socket_set_blocking($fp,FALSE);
var_dump(fgets($fp, 1024));
fclose($fp);

returns (false) 

$fp = fsockopen("mail_server", 25);
sleep(1);
socket_set_blocking($fp,FALSE);
var_dump(fgets($fp, 1024));
fclose($fp);

returns identify string.

The position of the sleep() call can be after socket_set_blocking() as well.
 [2003-01-09 02:48 UTC] bjarne at sourcetech dot se
I've noticed that I need the sleep() in order to retrieve the data.

The behaviour with blocking/non blocking sockets haven't changed though, its still the same in php 4.3.0 as it were in previous php versions.

What has changed is the feof() that never return true.

Regards
/Bjarne
 [2003-01-09 04:05 UTC] wez@php.net
The nature of non-blocking sockets means that your script must always be prepared to handle a false or zero length return from fgets/fread, so I'm not worried about that aspect.
However, the feof() does seem to be a problem.
Looking into it...
 [2003-01-09 04:23 UTC] wez@php.net
The following works for me:

$fp = fsockopen("localhost", 25);
stream_set_blocking($fp, false);
fwrite($fp, "QUIT\r\n");
while(!feof($fp)) {
  $data = fgets($fp);
  var_dump($data);
}
echo "\nAll done\n";

Remember that feof() will only return true when there is no more data in the internal buffer held by the stream, so you need to drain off any input by consuming it all first.
 [2003-01-09 04:33 UTC] nicos@php.net
The following works for me:

$fp = fsockopen("localhost", 25);
stream_set_blocking($fp, false);
fwrite($fp, "QUIT\r\n");
while(!feof($fp)) {
  $data = fgets($fp);
  var_dump($data);
}
echo "\nAll done\n";

Under Win2000, WinXP, Linux2.4.19 (with IPv6), FreeBSD4.5 (with IPv6)...

Not verified at all for me.
 [2003-01-25 01:00 UTC] php-bugs at lists dot php dot net
No feedback was provided for this bug for over 2 weeks, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
 [2003-01-27 03:42 UTC] bjarne at sourcetech dot se
Sorry for my late respons, i didnt know u were waiting for input...

I think the reason your example works is because the smtp-server closes the socket after the "QUIT" -command.

Try to "misspell" it and see what happens...(i.e 'QUIIT')
You get a "unrecognized command" response from the server, but the important thing is that the smtp-server doesnt close the socket.
The while-loop will now continue to loop until "maximum execution time" (30 secs in my case)
The feof() -never- returns true in php 4.3.0 but does so in 4.2.3.


And this is exacly my problem.
(In my "real" case I dont connect to a smtp-server but the scenario is the same.)

Regards
/Bjarne
 [2003-01-27 06:23 UTC] wez@php.net
This is expected behaviour.

feof() will only return true for a socket stream when the connection has been closed.

If you are running in non-blocking mode, you need to check the return value of fgets to see if it is false.
If it returns false it simply means that there is no more data at that time.  You can then check if the EOF has been reached.

Your script is responsible for implementing some kind of timeout to catch the kind of communication error you have described (both sides of the socket are waiting for the other to send data).

This is difficult to do with a raw non-blocking socket, so instead of doing that, there are two other techniques which are more flexible (for most users):

1. Use stream_set_timeout() on a regular blocking socket.
Useful only if you are using a single socket to read from.

2. Use stream_select() on a number of regular blocking sockets.
Useful if your script is talking to a number of servers concurrently.

Both of these allow you to set a timeout duration; the first will cause your fgets() and fread() calls to timeout after the specified duration; they will return false in that case, which you should check for and break out of your while loop.

while (!feof($fp)) {
    $data = fgets($fp);
    if ($data === false)
        break;
}

stream_select() will wait for up to the timeout duration for data to arrive on any of the streams that you pass as parameters.  You can then read data from the relevant streams.  This is slightly more complicated to implement than using stream_set_timeout, but more powerful overall.

feof() is definitely working the way it should, as are the other functions that I have mentioned.
I'm changing this to a documentation problem, because we should explain about non-blocking sockets and some of the common pitfalls more clearly in the docs.

 [2003-02-21 20:25 UTC] wez@php.net
Assigning to Philip who agreed to XML-ify any explanations I made for the docs.
Philip - there should be more than enough info in this report - if you need clarifications, you know where to find me :)
 [2003-06-21 21:24 UTC] philip@php.net
Assigning to Pollita, she is the stream Goddess.
 [2003-12-11 09:46 UTC] nlopess@php.net
This bug has been fixed in CVS.

In case this was a PHP problem, snapshots of the sources are packaged
every three hours; this change will be in the next snapshot. You can
grab the snapshot at http://snaps.php.net/.
 
In case this was a documentation problem, the fix will show up soon at
http://www.php.net/manual/.

In case this was a PHP.net website problem, the change will show
up on the PHP.net site and on the mirror sites in short time.
 
Thank you for the report, and for helping us make PHP better.


 [2020-02-07 06:12 UTC] phpdocbot@php.net
Automatic comment on behalf of nlopess
Revision: http://git.php.net/?p=doc/en.git;a=commit;h=6bd9d1c2069825c8900fc5e029ecb88046c797d4
Log: fixed #21485
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Sep 18 17:01:27 2024 UTC