Hello!
During refactoring of one of my server projects written in PHP I was
curious what PHP
will do if one of FDs given to stream_select()
will gone away. Of
course by „gone away“
I mean some underlying error like forceful close of FD and not a
standard disconnection
of the client.
I did some tests, read documentation for select() call, dig into both
kernel & PHP sources
and I came to conclusion that there’s no way to cleanly recover from
stream_select()
error :(
It's also worth to mention PHP displays E_WARNING
with error, but
there's no clean
way to determine actual error.
I wrote simple test code: http://pastebin.com/6ws1xp5q
After running it one of it's sockets need to be killed. The easiest
way is to use
gdb on linux:
- gdb -p PID
- call close(3) //it will kill listener
- q
- y
After such sequence stream_select()
will return false and all arrays
will stay unmodified.
Also as I mentioned before "PHP Warning: stream_select()
: unable to
select [9]: Bad
file descriptor (max_fd=4)" will be triggered.
I tried is_resource()
, fread()
, stream_get_meta_data()
, feof()
- none
of them are giving
me any clues which socket errored.
Killing whole server on single socket error is not an option for me.
Is there any (even crazy) way to determine which socket is considered bad?
p.s. I found similar problem regarding C on SO, but they didn't found
a good solution:
http://stackoverflow.com/questions/12838041/method-to-handle-bad-file-descriptor-error
Greg 'aka kiler129
Hello!
by „gone away“
I mean some underlying error like forceful close of FD and not a
standard disconnection
of the client.
What problem exactly are you trying to solve, and do you have a way of
showing the problem that isn't just what looks like a programming
mistake?
e.g. The following code works fine. The file being closed doesn't
affect the filehandle
$fileHandle = fopen('foo.txt', 'r+');
unlink('foo.txt');
$data = fread($fileHandle, 20);
The following code doesn't work fine. The filehandle being closed,
surprisingly enough, makes it not usable any more:
$fileHandle = fopen('foo.txt', 'r+');
fclose($fileHandle);
$data = fread($fileHandle, 20);
or, as the link you sent puts it:
If a connection is closed at client side, and you call select() on the server side socket, FD_READ will be triggered and recv() will return zero, then you can close the socket at server side.
The "bad file descriptor" error only happens when you call API on an already closed socket.
So I don't understand what problem you're trying to analyze, other
than closed file descriptors are closed.
cheers
Dan
2016-03-28 11:19 GMT-05:00 Dan Ackroyd danack@basereality.com:
by „gone away“
I mean some underlying error like forceful close of FD and not a
standard disconnection
of the client.What problem exactly are you trying to solve, and do you have a way of
showing the problem that isn't just what looks like a programming
mistake?
You're missing one point - the native file descriptor living inside a
kernel and somewhat
managed by PHP C code can change it's state independently from PHP knowledge.
The problem is stream resource is only an loosely representation of
native FD, not an
actual file descriptor.
TBH I don't know any way to damage the actual FD by programmer mistake in PHP.
I came across the problem in real environment in two cases:
- Socket was listening with some specific IP and network card gone
away. Every call
tostream_select()
produced situation described in OP and send the
code into dead-loop. - Using hardware-dependent socket (like ttys to USB<>RS232 converters) you can
trigger such error by simply disconnecting the USB cord (not always,
because some
drivers will just trigger EOF on socket and close it gracefully).
e.g. The following code works fine. The file being closed doesn't
affect the filehandle$fileHandle = fopen('foo.txt', 'r+');
unlink('foo.txt');
$data = fread($fileHandle, 20);
It's perfectly understandable - files have nothing to do with file
descriptors opened for
them.
The following code doesn't work fine. The filehandle being closed,
surprisingly enough, makes it not usable any more:$fileHandle = fopen('foo.txt', 'r+');
fclose($fileHandle);
$data = fread($fileHandle, 20);
Yes, that's true - after you callfclose()
PHP gracefully closes the
socket and marks
the memory, so $fileHandle will no longer be a valid stream resource.
So I don't understand what problem you're trying to analyze, other
than closed file descriptors are closed.
If the socket was properly closed there's no discussion - you deal
with thousand times
a day, but the problem comes when the underlying FD will gone away.
Actually I found in C you can just call fcntl(FD, F_GETFD) and you'll
get -1 if given FD is
no longer valid FD. In PHP there's no way to do it - that's my problem.
Greg 'aka kiler129