After installing the 4.5 runtime on some servers, we experienced many winsock-related errors. I tracked the issue down to a handle leak to \Device\Afd, when using WCF with ReliableSessions on .Net 4.5 runtime (not reproducible on 4.0).
I wrote a test program to isolate/proof the problem.
Assume a pretty dummy WCF-service, and Client (as could be generated from VS):
[ServiceContract]
interface IMyService
{
[OperationContract]
void foo();
}
class MyService : IMyService
{
public void foo() { }
}
//simulate a svcutil/VS-generated service reference
class MyServiceClient : ClientBase<IMyService>
{
public MyServiceClient(Binding binding, EndpointAddress remoteAddress)
: base(binding, remoteAddress) { }
}
And It's hosting and consuming.
class Program
{
static void Main(string[] args)
{
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None,false);
EndpointAddress address = new EndpointAddress("net.tcp://localhost:6660");
//Warmup (to stabilze handles for threads etc...)
PerformTest(binding, address, 1);
//Try without ReliableSession
binding.ReliableSession.Enabled = false;
PerformTest(binding, address, 1000);
//Try with ReliableSession
binding.ReliableSession.Enabled = true;
PerformTest(binding, address, 1000);
}
private static void PerformTest(NetTcpBinding binding, EndpointAddress address, int nbConnections)
{
int nbHandlesBefore = Process.GetCurrentProcess().HandleCount;
//Create & open Service
var host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IMyService), binding, address.Uri);
host.Open();
//Connect/Disconnect many times
for (int i = 1; i <= nbConnections; i++)
{
using (var proxy = new MyServiceClient(binding, address))
{
proxy.Open();
proxy.Close();
}
Console.Write("\r {0}/{1} ", i, nbConnections);
}
host.Close();
int nbHandlesAfter = Process.GetCurrentProcess().HandleCount;
Console.WriteLine("Handle increase: {0}", nbHandlesAfter - nbHandlesBefore);
}
}
The output of the test program is:
1/1 Handle increase: 144
1000/1000 Handle increase: -25
1000/1000 Handle increase: 1739
Remarks:
- I know I should do better error handling because Close() can throw, but in this case it doesn't, so I prefered pasting less code here.
- Here, I log the handle count for all handles (not only \Device\Afd). This makes it easier to test quickly, but I did many checks with sysinternals' handle.exe to verify the Name/Type of the handles
- Handles are leaked by the Client, not the service (at least they are cleaned up properly after closing the servicehost).
Variations:
- When changing the Proxy.Close() into Abort(), all handles in the are released properly (however the service has a hard time closing - as could be expected)
- Creating the channels by
IClientChannel proxy = ChannelFactory<IMyService>.CreateChannel(binding, address) as IClientChannel
gives the same results - handles are released properly when creating channels through a channelfactory instance:
IClientChannel proxy = new ChannelFactory<IMyService>(binding, address).CreateChannel() as IClientChannel
- setting the ClientBase<>.CacheSetting to AlwaysOn, fixes the problem but is not always possible.
Does anyone know why this program is leaking handles?