If you are looking for the epic motorcycle journey blog that I've written, please see the Miles By Motorcycle site I put together. 
  • PHP Leaks Memory in socket_read()
    04/13/2009 1:51PM

    This has taken quite some time to track down. Ref this bug report over at bugs.php:

    http://bugs.php.net/bug.php?id=42846

    Personally from what I'm seeing, the comment in the report that this is just how PHP memory management works is incorrect. Calls to socket_read() in PHP 5.2.6 under Ubuntu 8.10 causes the process to grow without bounds.

    Here is a slightly modified sample client and server pulled from the PHP documentation socket examples page::

    The server code:

    <?php
    error_reporting(E_ALL);
    
    /* Allow the script to hang around waiting for connections. */
    set_time_limit(0);
    
    /* Turn on implicit output flushing so we see what we're getting
     * as it comes in. */
    ob_implicit_flush();
    
    $address = '127.0.0.1';
    $port = 10000;
    
    if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
        echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
    }
    
    if (socket_bind($sock, $address, $port) === false) {
        echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
    }
    
    if (socket_listen($sock, 5) === false) {
        echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
    }
    
    do {
        if (($msgsock = socket_accept($sock)) === false) {
            echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
            break;
        }
    
        do {
    
    	 	print( "MEMORY USAGE BEFORE READ IS '" . memory_get_usage() . "'\n" );
    
            if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
                echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n";
                break 2;
            }
    
      	 	print( "MEMORY USAGE AFTER READ IS '" . memory_get_usage() . "'\n" );
    
        } while (true);
    
        socket_close($msgsock);
    
    } while (true);
    
    socket_close($sock);
    
    ?>
    
    

    The client code:

    <?php
    error_reporting(E_ALL);
      echo "<h2>TCP/IP Connection</h2>\n";
      /* Get the port for the WWW service. */
    $service_port = 10000;
      /* Get the IP address for the target host. */
    $address = gethostbyname('localhost');
      /* Create a TCP/IP socket. */
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    if ($socket === false) {
        echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
    } else {
        echo "OK.\n";
    }
      echo "Attempting to connect to '$address' on port '$service_port'...";
    $result = socket_connect($socket, $address, $service_port);
    if ($result === false) {
        echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
    } else {
        echo "OK.\n";
    }
      while( true )
        {
          echo "Sending Hello";
          $in = "hello\n";
          socket_write($socket, $in, strlen($in));
        echo "OK.\n";
          sleep( 1 );
        }
      echo "Closing socket...";
    socket_close($socket);
    echo "OK.\n\n";
    ?>

    Running it:

     Running it will produce a constantly growing server process.

     MEMORY USAGE BEFORE READ IS '61324'
    MEMORY USAGE AFTER READ IS '61728'
    MEMORY USAGE BEFORE READ IS '61788'
    MEMORY USAGE AFTER READ IS '61924'
    MEMORY USAGE BEFORE READ IS '61924'
    MEMORY USAGE AFTER READ IS '61956'
    MEMORY USAGE BEFORE READ IS '61956'
    MEMORY USAGE AFTER READ IS '61988'
    MEMORY USAGE BEFORE READ IS '61988'
    MEMORY USAGE AFTER READ IS '62020'