I have this piece of PHP code that is a very basic UDP server. The problem is that it has a memory leak that is driving me insane.
Facts/observations: - The script will run out of memory if just started on its own. - When I output memory usage or any text in the while loop, it doesn't crash and is shows consistent memory usage. - However, when a client connects to the server, each iteration of the while loop consumes an additional 96 bytes of memory until it crashes. The client doesn't even need to be sending data. In fact, most iterations are being handled by the first IF statement (if the buffer is empty) in the process() function followed by the return. - Allocating more memory to the script/process only delays the inevitable crash for a little longer. - Upgrading from PHP5.3.3 on CentOS 6 to 5.4 did not help.
Any help or pointers would be appreciated!
<?php
ini_set( 'display_errors', true );
class UDP_Server {
protected $_socket = null;
protected $_host = '';
protected $_port = 0;
protected $_clients = array();
protected $_debug = false;
public function __construct( $host = '', $port = 0 ) {
$this->_host = $host;
$this->_port = $port;
$this->_socket = $this->_create_udp_server( $host, $port );
}
public function set_debug( $value = false ) {
$this->_debug = $value;
}
public function process() {
$buffer = stream_socket_recvfrom( $this->_socket, 1024, 0, $remote_host );
if( empty( $buffer ) ) {
return;
}
if( $this->_debug ) {
echo $remote_host, ': ', $buffer, "\n";
}
if( strpos( $buffer, 'udp.register.ip' ) !== false ) {
if( ! in_array( $remote_host, $this->_clients ) ) {
$this->_clients[] = $remote_host;
}
stream_socket_sendto( $this->_socket, 'udp.register.complete', 0, $remote_host );
return;
}
foreach( $this->_clients as $client ) {
if( $client === $remote_host ) {
continue;
}
stream_socket_sendto( $this->_socket, $buffer, 0, $client );
}
}
public function __destruct() {
fclose( $this->_socket );
}
protected static function _create_udp_server( $host = '0.0.0.0', $port = 0 ) {
$address = 'udp://' . $host . ':' . $port;
$socket = stream_socket_server( $address, $error_number, $error_message, STREAM_SERVER_BIND );
if( ! $socket ) {
die( 'could not create UDP server for ' . $address . '; Reason: [' . $error_number . '] - ' . $error_message );
}
stream_set_blocking( $socket, 0 );
return $socket;
}
}
$at_data_server = new UDP_Server( '0.0.0.0', 5556 );
$at_data_server->set_debug( true );
while( 1 ) {
$at_data_server->process();
}
It seems your code meets the condition in this bug report - https://bugs.php.net/bug.php?id=71613
Avoid using the fourth parameter of
stream_socket_recvfrom()
if it's possible.