PHP套接字服务器内存泄漏

时间:2011-03-23 14:16:06

标签: php sockets client-server php-5.2

我通过一个连接发送和接收数据时检查了内存,而且我似乎正在清除变量,因为内存返回到之前的值。

但由于某些原因,如果我建立新连接,然后关闭连接,内存泄露。我相信当接受套接字时可能会出现问题。

我正在使用PHP 5.2.10

希望你们中的一个人能够找到时间与源头进行比赛,并找出出错的地方。提前致谢

<?php
    Class SuperSocket
        {
            var $listen = array();
            var $status_listening = FALSE;
            var $sockets = array();
            var $event_callbacks = array();
            var $recvq = 1;
            var $parent;
            var $delay = 100; // 10,000th  of a second
            var $data_buffer = array();

            function SuperSocket($listen = array('127.0.0.1:123'))
                {
                    $listen = array_unique($listen);
                    foreach ($listen as $address)
                        {
                            list($address, $port) = explode(":", $address, 2);
                            $this->listen[] = array("ADDR" => trim($address), "PORT" => trim($port));
                        };
                }

            function start()
                {
                    if ($this->status_listening)
                        {
                            return FALSE;
                        };
                    $this->sockets = array();
                    $cursocket = 0;
                    foreach ($this->listen as $listen)
                        {
                            if ($listen['ADDR'] == "*")
                                {
                                    $this->sockets[$cursocket]['socket'] = socket_create_listen($listen['PORT']);
                                    $listen['ADDR'] = FALSE;
                                }
                            else
                                {
                                    $this->sockets[$cursocket]['socket'] = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
                                };
                            if ($this->sockets[$cursocket]['socket'] < 0)
                                {
                                    return FALSE;
                                };
                            if (@socket_bind($this->sockets[$cursocket]['socket'], $listen['ADDR'], $listen['PORT']) < 0)
                                {
                                    return FALSE;
                                };
                            if (socket_listen($this->sockets[$cursocket]['socket']) < 0)
                                {
                                    return FALSE;
                                };
                            if (!socket_set_option($this->sockets[$cursocket]['socket'], SOL_SOCKET, SO_REUSEADDR, 1))
                                {
                                    return FALSE;
                                };
                            if (!socket_set_nonblock($this->sockets[$cursocket]['socket']))
                                {
                                    return FALSE;
                                };
                            $this->sockets[$cursocket]['info'] = array("ADDR" => $listen['ADDR'], "PORT" => $listen['PORT']);
                            $this->sockets[$cursocket]['channels'] = array();
                            $this->sockets[$cursocket]['id'] = $cursocket;
                            $cursocket++;
                        };
                    $this->status_listening = TRUE;
                }

            function new_socket_loop(&$socket)
                {
                    $socket =& $this->sockets[$socket['id']];
                    if ($newchannel =  @stream_socket_accept($socket['socket'], 0));//@socket_accept($socket['socket']))
                        {
                            socket_set_nonblock($newchannel);
                            $socket['channels'][]['socket'] = $newchannel;
                            $channel = array_pop(array_keys($socket['channels']));
                            $this->remote_address($newchannel, $remote_addr, $remote_port);
                            $socket['channels'][$channel]['info'] = array('ADDR' => $remote_addr, 'PORT' => $remote_port);
                            $event = $this->event("NEW_SOCKET_CHANNEL");
                            if ($event)
                            $event($socket['id'], $channel, $this);
                        };
                }

    function endswith($string, $test) {
        $strlen = strlen($string);
        $testlen = strlen($test);
        if ($testlen > $strlen) return false;
        return substr_compare($string, $test, -$testlen) === 0;
    }

            function recv_socket_loop(&$socket)
                {
                    $socket =& $this->sockets[$socket['id']];
                    foreach ($socket['channels'] as $channel_id => $channel)
                        {
                            unset($buffer);#Flush buffer
                            $status = @socket_recv($channel['socket'], $buffer, $this->recvq, 0);
                            if ($status === 0 && $buffer === NULL)
                                {
                                    $this->close($socket['id'], $channel_id);
                                }
                            elseif (!($status === FALSE && $buffer === NULL))
                                {
                                    $sockid = $socket['id'];
                                    if(!isset($this->data_buffer[$sockid]))
                                        $this->data_buffer[$sockid]='';

                                    if($buffer!="\r"&&$buffer!="\n")
                                    {
                                        //Putty ends with \r\n
                                        $this->data_buffer[$sockid].=$buffer;
                                    }
                                    else if($buffer!="\n") //ignore the additional newline char \n
                                    {
                                        $event = $this->event("DATA_SOCKET_CHANNEL");
                                        if ($event)
                                            $event($socket['id'], $channel_id, $this->data_buffer[$sockid], $this);
                                        unset($this->data_buffer[$sockid]);
                                    }

                                };
                        }
                }

            function stop()
                {
                    $this->closeall();
                    $this->status_listening = FALSE;
                    foreach ($this->sockets as $socket_id => $socket)
                        {
                            socket_shutdown($socket['socket']);
                            socket_close($socket['socket']);
                        };
                    $event = $this->event("SERVER_STOP");
                    if ($event)
                    $event($this);
                }

            function closeall($socket_id = NULL)
                {
                    if ($socket_id === NULL)
                        {
                            foreach ($this->sockets as $socket_id => $socket)
                                {
                                    foreach ($socket['channels'] as $channel_id => $channel)
                                        {
                                            $this->close($socket_id, $channel_id);
                                        }
                                }
                        }
                    else
                        {
                            foreach ($this->sockets[$socket_id]['channels'] as $channel_id => $channel)
                                {
                                    $this->close($socket_id, $channel_id);
                                };
                        };
                }

            function close($socket_id, $channel_id)
                {
                    unset($this->data_buffer[$socket_id]); //clear the sockets data buffer
                    $arrOpt = array('l_onoff' => 1, 'l_linger' => 1);
                    @socket_shutdown($this->sockets[$socket_id]['channels'][$channel_id]['socket']);
                    @socket_close($this->sockets[$socket_id]['channels'][$channel_id]['socket']);
                    $event = $this->event("LOST_SOCKET_CHANNEL");
                    if ($event)
                    $event($socket_id, $channel_id, $this);
                }

            function loop()
                {
                    while ($this->status_listening)
                        {
                            usleep($this->delay);
                            foreach ($this->sockets as $socket)
                                {
                                    $this->new_socket_loop($socket);
                                    $this->recv_socket_loop($socket);
                                };
                            $event = $this->event("END_SOCKET_CHANNEL");
                            if ($event)
                            $event($this);
                        };
                }

            function write($socket_id, $channel_id, $buffer)
                {   
                    @socket_write($this->sockets[$socket_id]['channels'][$channel_id]['socket'], $buffer);
                    @socket_write($this->sockets[$socket_id]['channels'][$channel_id]['socket'], 'Server memory usage: '.memory_get_usage().'/'.memory_get_peak_usage(true)."\r\n");
                }

            function get_channel_info($socket_id, $channel_id)
                {
                    return $this->sockets[$socket_id]['channels'][$channel_id]['info'];
                }

            function get_socket_info($socket_id)
                {
                    $socket_info = $this->sockets[$socket_id]['info'];
                    if (empty($socket_info['ADDR']))
                        {
                            $socket_info['ADDR'] = "*";
                        };
                    return $socket_info;
                }

            function get_raw_channel_socket($socket_id, $channel_id)
                {
                    return $this->sockets[$socket_id]['channels'][$channel_id]['socket'];
                }

            function remote_address($channel_socket, &$ipaddress, &$port)
                {
                    socket_getpeername($channel_socket, $ipaddress, $port);
                }

            function event($name)
                {
                    if (isset($this->event_callbacks[$name]))
                    return $this->event_callbacks[$name];
                }

            function assign_callback($name, $function_name)
                {
                    $this->event_callbacks[$name] = $function_name;
                }
        };

    ?>

Server.php

include("supersocket.class.php");

function startswith($string, $test) {
    return strpos($string, $test, 0) === 0;
}

function newdata($socket_id, $channel_id, $buffer, &$server)
    {
        //$server->write($socket_id, $channel_id, ">".$buffer."\r\n");
        if($buffer=="STOP")
        {
            $server->stop();
        }
        else if($buffer=="DATETIME")
        {
            $server->write($socket_id, $channel_id, ">".date("dmYHis")."\r\n");
        }
        else
        {
            $server->write($socket_id, $channel_id, ">BAD\r\n");
        }

    };

function newclient($socket_id, $channel_id, &$server)
{
    $server->write($socket_id, $channel_id, "HEADER\n\r");
}

$socket = new SuperSocket(array('127.0.0.1:12345')); 
$socket->assign_callback("DATA_SOCKET_CHANNEL", "newdata");
$socket->assign_callback("NEW_SOCKET_CHANNEL", "newclient");
$socket->start();
//set_time_limit(60*2);
set_time_limit(60*60*24*5); //5 days
$socket->loop();

编辑:抱歉,您可能需要将套接字接受更改回: if($ newchannel = @socket_accept($ socket ['socket']))

2 个答案:

答案 0 :(得分:1)

  

然后关闭连接,内存泄露

这是一个棘手的问题 - 即使是标准的引用计数垃圾收集器也只能以难以预测的间隔启动。调用gc_collect_cycles()应该触发gc。每当你关闭一个连接时,请尝试调用它,看看它是否有所作为。

如果你仍然遇到问题 - 那么检查你是否已经编译了循环引用计数器 - 如果没有,那么就得到它。

答案 1 :(得分:-1)

关闭连接后,Channel数组从未删除过,令人惊讶的是没人接听。内存使用现在非常紧张。

未设置(这 - $&GT;插座[$ socket_id] [ '通道'] [$ CHANNEL_ID]);

但这确实意味着LOST_SOCKET_CHANNEL的任何事件暂时都没用。

当堆栈溢出允许时,将接受我自己的答案。谢谢你的帮助ppl ..我猜..

相关问题