socket_select() hangs sometimes; Bug?

From: Thomas Weber (thomas_at_youngarts.org)
Date: 11/25/03

  • Next message: L'Angel Admirable: "checkbox select state"
    To: "PHP-List" <php-general@lists.php.net>
    Date: Tue, 25 Nov 2003 01:50:41 +0100
    
    

    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

    ---
    thomas@youngarts.org
    neo@gothic-chat.de
    

  • Next message: L'Angel Admirable: "checkbox select state"

    Relevant Pages

    • sockets, closing and TIME_WAIT
      ... During heavy load the server can't follow anymore because the sockets ... my server should be able to handle 10 clients connecting ... This gets a free position in the array of connections, ...
      (comp.unix.programmer)
    • Re: network programming: how does s.accept() work?
      ... The program you contact at Google is a server. ... so, the server will usually assign a new port, say 56399, specifically ... connections to a server remain on the same port, ... sockets is what identifies them. ...
      (comp.lang.python)
    • Re: WinXP and 2K: Inbound Connections Limited to 10?
      ... sockets without any problem ... > Microsoft might probably take the position that a program (like my server) ... > program that handles more than than 10 connections concurrently. ... > know of any recent version of Windows that enforces this limit in the ...
      (microsoft.public.win32.programmer.networks)
    • RE: Windows Update KB905915 issues
      ... I have been experiencing similar problems. ... > Over the last 2 weeks my SBS 2000 server has been experiencing very erratic ... > 1) Unable to establish new VPN connections. ... > just hangs at the "saving your settings" window. ...
      (microsoft.public.backoffice.smallbiz2000)
    • Re: WinXP and 2K: Inbound Connections Limited to 10?
      ... the Web) have convinced me that my server will work just fine and that WinXP ... program that handles more than than 10 connections concurrently. ... many simultaneous sockets can I have open with Winsock?" ... "...I have seen reports of a 64 MB Windows NT 4.0 machine hitting the wall ...
      (microsoft.public.win32.programmer.networks)