2013-05-08 64 views
5

我很难过。也许有人可以对我正在观察的WCF客户端行为有所了解。WCF使用,关闭和扩展

使用WCF示例,我已经开始使用WCF客户端/服务器通信的不同方法。在并行执行1M个测试请求的同时,我使用SysInternals TcpView监视打开的端口。现在,至少有4点不同的方式来调用客户端:

  1. 创建客户端,做你的事,并让GC收集它
  2. 在使用块创建客户端,不是做你的事
  3. 在使用块从原厂的客户渠道,不是做你的事
  4. 创建客户端或通道,但使用WCF Extensions做你的事

现在,据我所知,只有选择2-4,显式调用client.Close()。在执行期间,我看到很多端口都处于TIME_WAIT状态。由于依赖GC,我期望选项1是最糟糕的情况。然而,令我惊讶的是,它似乎是其中最清洁的,这意味着它不会留下任何滞留的港口。

我错过了什么?

UPDATE:源代码

private static void RunClientWorse(ConcurrentBag<double> cb) 
    { 
     var client = new CalculatorClient(); 
     client.Endpoint.Address = new EndpointAddress("net.tcp://localhost:8000/ServiceModelSamples/service"); 
     RunClientCommon(cb, client);       
    } 

    private static void RunClientBetter(ConcurrentBag<double> cb) 
    { 
     using (var client = new CalculatorClient()) 
     { 
      client.Endpoint.Address = new EndpointAddress("net.tcp://localhost:8000/ServiceModelSamples/service"); 
      RunClientCommon(cb, client); 
     } 
    } 

    private static void RunClientBest(ConcurrentBag<double> cb) 
    { 
     const string Uri = "net.tcp://localhost:8000/ServiceModelSamples/service"; 
     var address = new EndpointAddress(Uri); 
     //var binding = new NetTcpBinding("netTcpBinding_ICalculator"); 
     using (var factory = new ChannelFactory<ICalculator>("netTcpBinding_ICalculator",address)) 
     { 
      ICalculator client = factory.CreateChannel(); 
      ((IContextChannel)client).OperationTimeout = TimeSpan.FromSeconds(60); 
      RunClientCommon(cb, client); 
     } 
    } 

    private static void RunClientBestExt(ConcurrentBag<double> cb) 
    { 
     const string Uri = "net.tcp://localhost:8000/ServiceModelSamples/service"; 
     var address = new EndpointAddress(Uri); 
     //var binding = new NetTcpBinding("netTcpBinding_ICalculator"); 
     new ChannelFactory<ICalculator>("netTcpBinding_ICalculator", address).Using(
      factory => 
       { 
        ICalculator client = factory.CreateChannel(); 
        ((IContextChannel)client).OperationTimeout = TimeSpan.FromSeconds(60); 
        RunClientCommon(cb, client); 
       }); 
    } 
+3

你错过了一些源代码...我们能否看到你的单元测试? – 2013-05-08 14:13:40

+0

请参阅http://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-the-wcf-client-using-block-issue - 使用块可能会导致WCF出现问题。 – TrueWill 2013-05-08 14:21:25

+0

感谢您的链接,相当有趣的阅读,但它仍然不能解释为什么GC没有离开TIME_WAITs,但client.Close()。 – Darek 2013-05-08 14:29:44

回答

1

我已经想通了,我想。 GC不会在ClientBase上调用Dispose。这就是为什么连接不会处于TIME_WAIT状态。所以,我决定遵循相同的模式,创造了新的WCF扩展:

public static void UsingAbort<T>(this T client, Action<T> work) 
     where T : ICommunicationObject 
    { 
     try 
     { 
      work(client); 
      client.Abort(); 
     } 
     catch (CommunicationException e) 
     { 
      Logger.Warn(e); 
      client.Abort(); 
     } 
     catch (TimeoutException e) 
     { 
      Logger.Warn(e); 
      client.Abort(); 
     } 
     catch (Exception e) 
     { 
      Logger.Warn(e); 
      client.Abort(); 
      throw; 
     } 
    } 
} 

这样,在请求结束便索性中止连接,而不是关闭它。

+0

您的新模式的问题是Abort()不通知客户端关机服务。通过在try块中的打开连接上不调用Close(),您将使服务器上的连接保持打开状态,直到它们超时。建议阅读:http://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-the-wcf-client-using-block-issue – ErnieL 2013-05-09 13:04:03

+0

我不认为这是这种情况@ErnieL。根据Microsoft文档,Abort()会导致ClientBase 对象立即从其当前状态转换到关闭状态。这似乎得到了端口关闭服务器端的确认。我错过了什么吗? – Darek 2013-05-13 12:54:31

+0

文档喜欢说Abort()是“立即”的,Close()是“优美的”。例如:http://msdn.microsoft.com/en-us/library/ms195520.aspx。这样说:你的模式*从不*调用Close()及其良好的文档记录,Dispose()调用Close()而不是Abort()。所以,如果你的模式是正确的,为什么在界面中的Close()呢? – ErnieL 2013-05-13 15:38:51