3

一部のサーバーに 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 に設定すると、問題は解決しますが、常に可能とは限りません。

このプログラムがハンドルをリークしている理由を誰か知っていますか?

4

0 に答える 0