Hi folks,
I am battling around with socket_select() for some months now and still have
an unsolved problem with random hanging.
PHP: 4.3.4 with -sockets, -sigchild, -pcntl and some other things
OS: FreeBSD 5.0 (same problem with 4.8 and 5.1)
In simple words, I have a server-daemon (runs with PHP4.3.4 now) wich uses
socket_select() for accepting new connections and dealing with existing
ones. It is an IRC-like chatserver talking with PHP-script running on a
webserver on _the same_ machine, same IP, long connects for output, short
connects for input. For debugging I use normal TCP-sockets, not
unix-domain-sockets. The select runs on all connected sockets for read and
error, write is untouched.
In testing-enviroment everything works perfect, but in production-eviroment
the server hangs after some time.
Some facts:
- in testing, even 32.000 fast connects (made synchronous with 16 processes)
couldn't get the server to hang, in production it hangs after about 3000 -
7000 connects, regardless of low CPU-load
- debug-outputs to console before and after the select show clearly the
socket_select() is hanging
- tried both blocking and non-blocking select, same effect in both cases
- both test and production are on the same machine, same PHP
- both are getting only local connects from other PHP-scripts
- socket-dumps before the select show all sockets in normal state, even if
directly before a hang
- select hangs sometimes with about 80 synchronous connections, sometimes
with only about 20
I thought it might be a bug in PHPs socket-extension, because the hangups
come random, indipendent of number of synchronous connects, number of
processed connects, id of hightest socket-descriptor, used CPU-time.
Maybe i just forgot something, but it doesn't go into my head why it hangs
exactly during the select :-/
The code should be okay, it is basically indentical to an example posted
some time ago in the php-dev-mailinglist (i've pasted it below).
#!/usr/local/bin/php -q
<?php
set_time_limit(0);
// defaults...
define('MAXLINE', 1024); // how much to read from a socket at a time
define('LISTENQ', 10); // listening queue
define('PORT', 10000); // the default port to run on
define('FD_SETSIZE', 5); // file descriptor set size (max number of
concurrent clients)...
// for kill the biatch...
function killDaemon()
{
global $listenfd, $client;
socket_close($listenfd);
$msg = "Daemon going down!\n";
for ($i = 0; $i < FD_SETSIZE; $i++)
{
if ($client[$i] != null)
{
socket_write($client[$i], $msg, strlen($msg));
socket_close($client[$i]);
}
}
print "Shutting down the daemon\n";
exit;
}
// whenever a client disconnects...
function closeClient($i)
{
global $client, $remote_host, $remote_port;
print "closing client[$i] ({$remote_host[$i]}:{$remote_port[$i]})\n";
socket_close($client[$i]);
$client[$i] = null;
unset($remote_host[$i]);
unset($remote_port[$i]);
}
// set up the file descriptors and sockets...
// $listenfd only listens for a connection, it doesn't handle anything
// but initial connections, after which the $client array takes over...
$listenfd = socket_create(AF_INET, SOCK_STREAM, 0);
if ($listenfd)
print "Listening on port " . PORT . "\n";
else
die("AIEE -- socket died!\n");
socket_setopt($listenfd, SOL_SOCKET, SO_REUSEADDR, 1);
if (!socket_bind($listenfd, "0.0.0.0", PORT))
{
socket_close($listenfd);
die("AIEE -- Couldn't bind!\n");
}
socket_listen($listenfd, LISTENQ);
// set up our clients. After listenfd receives a connection,
// the connection is handed off to a $client[]. $maxi is the
// set to the highest client being used, which is somewhat
// unnecessary, but it saves us from checking each and every client
// if only, say, the first two are being used.
$maxi = -1;
for ($i = 0; $i < FD_SETSIZE; $i++)
$client[$i] = null;
// the main loop.
while(1)
{
$rfds[0] = $listenfd;
for ($i = 0; $i < FD_SETSIZE; $i++)
{
if ($client[$i] != null)
$rfds[$i + 1] = $client[$i];
}
// block indefinitely until we receive a connection...
$nready = socket_select($rfds, $null, $null, null);
// if we have a new connection, stick it in the $client array,
if (in_array($listenfd, $rfds))
{
print "listenfd heard something, setting up new client\n";
for ($i = 0; $i < FD_SETSIZE; $i++)
{
if ($client[$i] == null)
{
$client[$i] = socket_accept($listenfd);
socket_setopt($client[$i], SOL_SOCKET, SO_REUSEADDR, 1);
socket_getpeername($client[$i], $remote_host[$i],
$remote_port[$i]);
print "Accepted {$remote_host[$i]}:{$remote_port[$i]} as
client[$i]\n";
break;
}
if ($i == FD_SETSIZE - 1)
{
trigger_error("too many clients", E_USER_ERROR);
exit;
}
}
if ($i > $maxi)
$maxi = $i;
if (--$nready <= 0)
continue;
}
// check the clients for incoming data.
for ($i = 0; $i <= $maxi; $i++)
{
if ($client[$i] == null)
continue;
if (in_array($client[$i], $rfds))
{
$n = trim(socket_read($client[$i], MAXLINE));
if (!$n)
closeClient($i);
else
{
// if a client has sent some data, do one of these:
if ($n == "/killme")
killDaemon();
else if ($n == "/quit")
closeClient($i);
else
{
// print something on the server, then echo the incoming
// data to all of the clients in the $client array.
print "From {$remote_host[$i]}:{$remote_port[$i]},
client[$i]: $n\n";
for ($j = 0; $j <= $maxi; $j++)
{
if ($client[$j] != null)
socket_write($client[$j], "From client[$i]:
$n\r\n");
}
}
}
if (--$nready <= 0)
break;
}
}
}
?>
thanks for help,
Thomas 'Neo' Weber
---
[EMAIL PROTECTED]
[EMAIL PROTECTED]
--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php