簡體   English   中英

PHP套接字服務器內存泄漏

[英]PHP Socket Server Memory Leak

在通過一個連接發送和接收數據時,我已經檢查了內存,並且由於內存返回其先前的值,因此我似乎正在正確清除變量。

但是由於某種原因,如果我建立一個新的連接,然后關閉該連接,則內存泄漏。 我相信當接受套接字時,可能會出現問題。

我正在使用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']))

然后關閉連接,內存泄漏

這是一個棘手的問題-即使標准參考計數垃圾收集器也只能以難以預測的時間間隔啟動。 調用gc_collect_cycles ()應該會觸發gc。 嘗試在關閉連接時致電,以查看是否有幫助。

如果仍然遇到問題-請檢查是否已編譯了循環引用計數器-如果沒有,請獲取它。

關閉連接后,從未刪除Channel數組,對此沒有人感到驚訝。 現在,內存使用量非常緊張。

unset($ this-> sockets [$ socket_id] ['channels'] [$ channel_id]);

但這確實意味着LOST_SOCKET_CHANNEL的任何事件暫時都沒有用。

當堆棧溢出允許時,將接受我自己的答案。 感謝您的全力幫助。我想..

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM