Server / Client. Клиент использует много памяти - PullRequest
2 голосов
/ 07 ноября 2011

Это мой первый вопрос, и я надеюсь, что вы мне поможете.

У меня есть 2 программы, сервер и клиент, и моя проблема в клиенте.

После 2 или 3дни работы он использует более 300 МБ ОЗУ (я могу сказать это, увидев TaskManager) и никогда не выпускает его!Кроме того, я должен сказать, что этот Клиент получает данные каждую секунду от устройств GPS.Я проанализировал мой клиент с помощью ANTS Memory Profiler и заметил, что я создаю объект несколько раз, и он никогда не уничтожается.

Вот мой код:

private static TcpClient _client;
private readonly ManualResetEvent _receiveDone = new ManualResetEvent(false);

public void Receive()
{
    if (_client.Connected)
    {
        _receiveDone.Reset();
        ObjectState state = new ObjectState(); 
        state.WorkSocket = _client.Client;
        state.Data = new byte[_client.ReceiveBufferSize];
        _client.Client.BeginReceive(state.Data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, state);

        if (!_receiveDone.WaitOne(20000)) 
        {
            //after 20 seconds WITHOUT RECEIVING DATA, do some code to test if the connection is alive                        
        }
    }
}

void ReceiveCallback(IAsyncResult ar)
{
    ObjectState state = (ObjectState)ar.AsyncState;
    Socket client = state.WorkSocket;
    if (client.Connected)
    {
        int bytesRead = client.EndReceive(ar);
        if (bytesRead > 0)
        {
            string response = Encoding.ASCII.GetString(state.Data, 0, bytesRead);
            doProcess(response);
            client.BeginReceive(state.Data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, state);
            _receiveDone.Set();
        }
        else
        {
        //Disconnection
        }
    }
}

public class ObjectState
{
    public Socket WorkSocket;
    public byte[] Data;
}

Профилировщик памяти ANTS сообщает мне, что у меня есть тысячи живых экземпляров byte[].(потому что я всегда создаю новые экземпляры ObjectState)

Первое, что я хотел сделать: избавиться от всех ObjectState, которые я создаю после вызова BeginReceive, но я получаю только первое сообщение.

Тогда я хотел прекратить использовать ObjectState ... Как?Это мой модифицированный код:

private _data byte[];

public void Receive()
{
    if (_client.Connected)
    {
        _receiveDone.Reset();
        _data = new byte[_client.ReceiveBufferSize];
        _client.Client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);

        if (!_receiveDone.WaitOne(20000)) 
        {
            //after 20 seconds of inactivity do some code to test if the connectio is alive                        
        }
    }
}

void ReceiveCallback(IAsyncResult ar)
{
    Socket client = _cliente.Client;
    if (client.Connected)
    {
        int bytesRead = client.EndReceive(ar);
        if (bytesRead > 0)
        {
            string response = Encoding.ASCII.GetString(_data, 0, bytesRead);
            doProcess(response);
            client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);
            _receiveDone.Set();
        }
        else
        {
            //Disconnection
        }
    }
}

Что с этим не так?Я получаю только первое сообщение, так что это не хорошо.Затем, если я удаляю _receiveDone.Set, он получает все сообщения, НО _receiveDone.WaitOne(2000) всегда выполняется каждые 20 секунд, независимо от того, действительно ли я получаю данные, и это тоже не хорошо.

Так что мойвопрос в том, что я могу сделать, чтобы уменьшить использование такого большого количества оперативной памяти?Надеюсь, это хорошо объясняет мою проблему.

РЕДАКТИРОВАТЬ: Я загрузил эти изображения, надеюсь, они тоже могут быть полезны.Summary Instant Retention Graph

Ответы [ 2 ]

1 голос
/ 07 ноября 2011
if (client.Connected)
{
    int bytesRead = client.EndReceive(ar);
    // etc..

Вызов EndReceive является , а не необязательным, у вас есть , чтобы вызвать его, или вы потеряете ресурсы. Используйте try / catch, чтобы перехватить ObjectDisposedException.

0 голосов
/ 07 ноября 2011

Я думаю, что основная проблема в том, что вы зависите от BeginReceive, чтобы вернуть 0 байтов, когда ничего не доступно.Но это не так, как это работает.BeginReceive будет блокироваться до тех пор, пока не станут доступны данные или пока клиент не отключится.

В первом случае ReceiveCallback передает объект state в client.BeginReceive.Теперь у вас есть экземпляр ObjectState, ожидающий данных от клиента.Событие _receiveDone установлено, поэтому ваш метод Receive завершается, но клиентское соединение все еще открыто, и вы ожидаете асинхронного чтения.

При следующем вызове Receive он создаст еще один ObjectState экземпляр и отправьте еще один запрос асинхронного чтения.

Второй случай довольно пугающий.Ваш массив _data находится в области видимости класса и каждый раз перераспределяется.Таким образом, возможно, что асинхронное чтение может заполнить буфер, который вы передаете, но тогда данные будут потеряны, когда вы достигнете ReceiveCallback.

Ваш код выполняет проверку тайм-аута только для первого приема.После этого вещи могут зависать до бесконечности.Я бы предложил что-то вроде:

private _data byte[];
private bool _receiving = false;

public void StartReceiving()
{
    if (_receiving)
    {
        // ERROR: Already receiving
        throw new InvalidOperationException();
    }
    _receiving = true;
    Receive();
}

public void Receive()
{
    if (_client.Connected)
    {
        _data = new byte[_client.ReceiveBufferSize];
        var ir _client.Client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);

        if (!_ir.WaitOne(20000)) 
        {
            //after 20 seconds of inactivity do some code to test if the connectio is alive                        
        }
    }
}

void ReceiveCallback(IAsyncResult ar)
{
    Socket client = _cliente.Client;
    int bytesRead;
    try
    {
        bytesRead = client.EndReceive(ar);
        if (bytesRead > 0)
        {
            string response = Encoding.ASCII.GetString(_data, 0, bytesRead);
            doProcess(response);
            // Calling Receive here ensures that the timeout check happens
            // with every read.
            Receive();
        }
        else
        {
            _receiving = false;
        }
    }
    catch
    {
        // Catch SocketException and others here
    }
}

Обратите внимание, что вам не нужно событие _receiveDone.Вы можете проверить это с помощью IAsyncResult.WaitHandle.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...