一部のサーバーに 4.5 ランタイムをインストールした後、多くの winsock 関連のエラーが発生しました。.Net 4.5 ランタイムで ReliableSessions を使用して WCF を使用している場合 (4.0 では再現できません)、\Device\Afd へのハンドル リークまで問題を追跡しました。
問題を特定/証明するためのテストプログラムを作成しました。
かなりダミーの WCF サービスとクライアント (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) { }
}
そしてそれはホスティングと消費です。
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);
}
}
テスト プログラムの出力は次のとおりです。
1/1 Handle increase: 144
1000/1000 Handle increase: -25
1000/1000 Handle increase: 1739
備考:
- Close() がスローされる可能性があるため、より適切なエラー処理を行う必要があることはわかっていますが、この場合はスローされないため、ここに貼り付けるコードを減らすことを好みました。
- ここでは、(\Device\Afd だけでなく) すべてのハンドルのハンドル数をログに記録します。これにより、すばやくテストしやすくなりますが、sysinternals の handle.exe で多くのチェックを行い、ハンドルの名前/タイプを確認しました。
- ハンドルは、サービスではなくクライアントによってリークされます (少なくとも、サービスホストを閉じた後に適切にクリーンアップされます)。
バリエーション:
- Proxy.Close() を Abort() に変更すると、すべてのハンドルが適切に解放されます (ただし、予想どおり、サービスを閉じるのは困難です)。
- でチャネルを作成して
IClientChannel proxy = ChannelFactory<IMyService>.CreateChannel(binding, address) as IClientChannel
も同じ結果が得られます - channelfactory インスタンスを介してチャネルを作成すると、ハンドルが適切に解放されます。
IClientChannel proxy = new ChannelFactory<IMyService>(binding, address).CreateChannel() as IClientChannel
- ClientBase<>.CacheSetting を AlwaysOn に設定すると、問題は解決しますが、常に可能とは限りません。
このプログラムがハンドルをリークしている理由を誰か知っていますか?