服务器/客户端。客户端使用大量内存

时间:2011-11-07 16:22:25

标签: c# sockets asynchronous

这是我的第一个问题,希望你能帮助我。

我有2个程序,一个服务器和一个客户端,我的问题在客户端。

运行2到3天后,它使用超过300MB的RAM(我可以通过查看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 Memory Profiler告诉我,我有数千个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 个答案:

答案 0 :(得分:1)

if (client.Connected)
{
    int bytesRead = client.EndReceive(ar);
    // etc..

调用EndReceive 是可选的,你来调用它,否则你会泄漏资源。使用try / catch捕获ObjectDisposedException。

答案 1 :(得分:0)

我认为主要的问题是,当没有任何可用的东西时,你依靠BeginReceive返回0字节。但这不是它的工作原理。 BeginReceive将阻止,直到有可用数据,或者直到客户端断开连接。

在第一种情况下,ReceiveCallbackstate对象传递给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进行检查。