MemoryLeak in .NET Socket implementation ?

T

Tristan Braun

hi,

I have a quesition regarding sockets and memory leaks. i searched for a
while in dotnet newsgroups and found some questions similar to mine, but no
answer. hopefully someone can answer my question.

i am developing a socketserver in c#. after i did my first lines of code i
whatched my memory in the taskmanager. so longer my server was running the
memory increased continiuously. i tried to watch this behaviour in deepth
and used the ".NET Memory Profiler 2.0". my suspicion was rigth. the objects
that holds the socket never been removed or collected by the
garbagecollector.

i made 2 versions of a simple "pingpong" socket implementation. messages
recieved by the server will immediatly send back to the connected client. i
made a asynchronous and a synchronous version with a thread. in both
versions the objects who holds the socket are not removed by the runtime. i
implemented explicit a dispose (IDisposable Interface) methods in the
classes to shutdown the socket and closes them. i tried alternativly to

GC.Collect();
GC.WaitForPendingFinalizers();

with no success.

if i look deeper in the ".NET Memory Profiler 2.0" the objects will move to
the disposed column but never to the removed.

now the question, does it depend on the ".NET Memory Profiler 2.0" and it
does not tell me the truth or is there a bug in the .Net Framework
(v1.1.4322) socket implementation that there are still references to the
socket object that they cannot be removed ? at last the garbagecollector can
only remove objects if there a no references anymore.

when i have a look at the asynchronous variant, which i prefer, there are at
last 3 AsyncCallback respectively OverlappedAsyncResult (???) which
references to the object which implements the CallBack method. i thought
that the AsyncCallbacks, after the asynchronous operation respectively
Socket.EndReceive(IAsyncResult ar) is ended, they should be released or
removed. what they apperantly not do ?!


last but not least it cannot be that objects (whatever they are; synchronous
or asynchronous ones) not will completly removed or cleaned up. can someone
tell me what causes this behaviour ? when the app is running as a service on
a productive system i will avoid to run a script every 24 hours to restart
the service to release the memory.

thanks in advance,

Tristan

attached you will find the code.

if the code is not readable you will find a textfile on the following url:

http://www.trinic.de/Test/code.txt

<pre>
using System;

namespace ChatApp
{
using ChatServer;
class Class1
{
[STAThread]
static void Main(string[] args)
{
ChatListener _Listener = new ChatListener();

// Entweder
//_Listener.Start(ChatState.Synchron);

// oder
_Listener.Start(ChatState.Asynchron);

Console.ReadLine();
}
}
}

namespace ChatServer
{

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;

public enum ChatState
{
Asynchron,
Synchron
}

public delegate void DestroyDelegateClient(ChatClient Client);

public delegate void DestroyDelegateThread(Thread Client);

public class ChatListener
{
private Socket _Listener;
private ArrayList _Clients = new ArrayList();

public void Start(ChatState State)
{
IPAddress _Adresse = Dns.GetHostByName(Dns.GetHostName()).AddressList[0];
_Listener = new
Socket(_Adresse.AddressFamily,SocketType.Stream,ProtocolType.Tcp);
_Listener.Bind(new IPEndPoint(_Adresse, 9999));
_Listener.Listen(50);
if ( State == ChatState.Synchron )
while(true)
{
OnAccept(_Listener.Accept()); // Synchroner Modus
}
else
// Asynchroner Modus
_Listener.BeginAccept(new AsyncCallback(this.OnAccept), null);
}

// Asynchrone Accept Methode
public void OnAccept(IAsyncResult Result)
{
try
{
Socket _Socket = _Listener.EndAccept(Result);
ChatClient _Client = new ChatClient(_Socket,new
DestroyDelegateClient(OnDispose));
_Clients.Add(_Client);
_Listener.BeginAccept(new AsyncCallback(this.OnAccept), null);
}
catch (Exception e)
{ Console.WriteLine(e.Message); }
}

// Synchrone Accept Methode mit Thread
public void OnAccept(Socket Incoming)
{
try
{
Thread _Client = ChatClientSync.CreateClient(Incoming,new
DestroyDelegateThread(OnDispose));
_Clients.Add(_Client);
}
catch (Exception e)
{ Console.WriteLine(e.Message); }
}

// Asynchrone Dispose Methode
public void OnDispose(ChatClient Client)
{
if (_Clients.Contains(Client))
_Clients.Remove(Client);
Console.WriteLine("{0} zerstört.", Client.ID);
}

// Synchrone Dispose Methode mit Thread
public void OnDispose(Thread Client)
{
if (_Clients.Contains(Client))
{
try
{
_Clients.Remove(Client);
if (Client.ThreadState != ThreadState.Aborted)
Client.Abort();
}
catch(Exception e)
{ Console.WriteLine(e.Message); }
finally
{ Console.WriteLine("{0} zerstört.", Client.Name); }

Client = null;
}
}
}

public class ChatClient : IDisposable
{
public string ID;
private Socket _Socket;
private DestroyDelegateClient _Destroy;
private byte[] _Buffer = new byte[1024];

// Konstruktor der Klasse
public ChatClient(Socket Incoming, DestroyDelegateClient Destroy)
{
ID = Guid.NewGuid().ToString();
Console.WriteLine("Neue Verbindung " + ID);
_Socket = Incoming;
_Destroy = Destroy;
StartRelay();
}

// Asynchrone BeginReceive Methode einleiten
public void StartRelay()
{
_Socket.BeginReceive(
_Buffer,
0,
_Buffer.Length,
SocketFlags.None,
new AsyncCallback(this.OnReceive),
null);
}

// Asynchrone Receive Methode abschließen
public void OnReceive(IAsyncResult Result)
{
try
{
int _Rx = _Socket.EndReceive(Result);
if (_Rx <= 0)
{
Dispose();
return;
}
Console.WriteLine("{1} bytes empfangen. {0}",ID,_Rx);

_Socket.BeginSend(
_Buffer,
0,
_Rx,
SocketFlags.None,
new AsyncCallback(this.OnSend),
null);
Debug(_Buffer,_Rx);

}
catch (Exception e)
{
Console.WriteLine(e.Message);
Dispose();
}
}

// Asynchrone Send Methode abschließen
public void OnSend(IAsyncResult Result)
{
try
{
int _Tx = _Socket.EndSend(Result);
if (_Tx > 0)
{
Console.WriteLine("{1} bytes gesendet. {0}",ID,_Tx);
StartRelay();
return;
}
}
catch {}
Dispose();
}
private void Debug(byte[] Data, int Length)
{
Console.WriteLine("--- {0} ---",ID);
Console.WriteLine( System.Text.Encoding.ASCII.GetString( Data, 0,
Length ) );
Console.WriteLine("----{0}----", new string('-',ID.Length) );
}
// Socket explizit schließen und Destroy event auslösen.
public void Dispose()
{
try
{ _Socket.Shutdown(SocketShutdown.Both); }
catch { }
Console.WriteLine("Verbindung beendet " + ID);
if (_Socket != null)
_Socket.Close();
_Socket = null;
if (_Destroy != null)
_Destroy(this);
}
~ChatClient()
{
Dispose();
}
}

public class ChatClientSync : IDisposable
{
public string ID;
private Socket _Socket;
private DestroyDelegateThread _Destroy;
private byte[] _Buffer = new byte[1024];

// statische Methode um den Thread zu erzeugen und zu starten.
public static Thread CreateClient(Socket Incoming, DestroyDelegateThread
Destroy)
{
ChatClientSync _Sync = new ChatClientSync(Incoming,Destroy);
Thread _Thread = new Thread(new ThreadStart(_Sync.StartRelay));
_Thread.Name = _Sync.ID;
_Thread.Start();
return _Thread;
}

// Konstruktor der Klasse
public ChatClientSync(Socket Incoming, DestroyDelegateThread Destroy)
{
ID = Guid.NewGuid().ToString();
Console.WriteLine("Neue Verbindung " + ID);
_Socket = Incoming;
_Destroy = Destroy;
}

// Synchrone Schleife starten.
public void StartRelay()
{
try
{
while(true)
{
if (_Socket.Connected
&& _Socket.Poll(3000000,SelectMode.SelectRead)) // Besteht die
Verbindung ?
{
if (_Socket.Available > 0)
{
int _Rx = _Socket.Available;
_Socket.Receive(_Buffer,0,_Rx,SocketFlags.None); // Daten synchron
empfangen.
Console.WriteLine("{1} bytes empfangen. {0}",ID,_Rx);
Debug(_Buffer,_Rx);
int _Tx = _Socket.Send(_Buffer,0,_Rx,SocketFlags.None); // Daten
synchron senden.
Console.WriteLine("{1} bytes gesendet. {0}",ID,_Tx);
}
Thread.Sleep(100);
}
else
break;
}
}
catch (Exception e) { Console.WriteLine(e.Message); }
finally { Dispose(); }
}

private void Debug(byte[] Data, int Length)
{
Console.WriteLine("--- {0} ---",ID);
Console.WriteLine( System.Text.Encoding.ASCII.GetString( Data, 0,
Length ) );
Console.WriteLine("----{0}----", new string('-',ID.Length) );
}

public void Dispose()
{
try
{ _Socket.Shutdown(SocketShutdown.Both); }
catch { }

Console.WriteLine("Verbindung beendet " + ID);

if (_Socket != null)
_Socket.Close();
_Socket = null;
if (_Destroy != null)
_Destroy(Thread.CurrentThread);
}
~ChatClientSync()
{
Dispose();
}
}
}</pre>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top