【问题标题】:WCF ServiceHost.Close() DelayWCF ServiceHost.Close() 延迟
【发布时间】:2010-11-04 17:52:12
【问题描述】:

我有一个简单的 WCF 双工 TCP 服务,我试图以编程方式停止它。如果我没有任何连接的用户,ServiceHost.Close() 非常快,但如果我什至有一个连接的用户,我发现 Close() 函数需要相当长的时间,有时 > 30 秒。这是通常的行为吗?

另一方面,Abort() 几乎是即时的,我很想用它来代替。

【问题讨论】:

    标签: c# wcf duplex


    【解决方案1】:

    可能是这样。 docs 声明

    Close 方法允许任何未完成的 在返回之前完成的工作。 例如,完成发送任何 缓冲消息。

    Close() 有一个重载,它需要一个 TimeSpan(如果超过时间跨度,throws

    Abort() 看起来是立即停止 WCF 主机的最佳方式。

    【讨论】:

      【解决方案2】:

      确保您正在关闭客户端连接,如下所示:

      var channel = factory.CreateChannel();
      var channel.DoSomethingForMe();
      (channel as ICommunicationObject).Close();
      

      如果您不在频道上执行此 Close(),则服务器上的 Close() 会等待非常非常长的时间,即使您指定了很短的超时时间。

      【讨论】:

        【解决方案3】:

        如果您可以终止任何正在进行的服务调用,那么 Abort() 就是要走的路。 Close() 是关闭服务的礼貌方式。

        【讨论】:

          【解决方案4】:

          为了方便,我可以看到 Abort() 优于 Close() 的好处,但我想可能会发生一些不好的事情。就我而言,我想等待 Close() 以便我可以重用端口。此代码将等待服务实际关闭后再恢复。

          Semaphore cont=new Semaphore(0, 1);
          foreach (ServiceHost s in WCFServices) {
              try {
                  s.Closed += delegate(Object o, System.EventArgs n) {
                      cont.Release();
                  };
                  s.Close();
                  cont.WaitOne();                 
              } catch (Exception) {
              }//try
          }//for
          

          【讨论】:

          • 我相信你可以重新安排,这样所有的“s.Close()”都会先发出,然后在信号量上等待。
          【解决方案5】:

          我也注意到了这个问题。 我的代码最初看起来像这样:

          [TestMethod]
          [Timeout(2000)]
          public void ApiClientTest()
              {
                  bool ApiSuccessSet, ClientSuccessSet = ApiSuccessSet = false;
                  Api apiService = new ApiTestService();
                  var clientService = new ClientTestService();
          
                  ServiceHost clientHost = new ServiceHost(clientService, new Uri(PipeService));
                  ServiceHost apiHost = new ServiceHost(apiService, new Uri(PipeService));
          
                  //To let us know the services were successfully opened
                  clientHost.Opened += (s, e) => ClientSuccessSet = true;
                  apiHost.Opened += (s, e) => ApiSuccessSet = true;
                  clientHost.AddServiceEndpoint(typeof(IClientService), new NetNamedPipeBinding(), ClientPipeServiceName);
                  apiHost.AddServiceEndpoint(typeof(IApiService), new NetNamedPipeBinding(), ApiPipeServiceName);
                  clientHost.BeginOpen(OnOpen, clientHost);
                  apiHost.BeginOpen(OnOpen, apiHost);
          
                  //This allows both services to be open for communication.
                  while (!ApiSuccessSet || !ClientSuccessSet)
                      Thread.Yield();
          
          
                  EndpointAddress ApiEndpoint = new EndpointAddress(PipeService + @"/" + ApiPipeServiceName);
                  EndpointAddress clientEndpoint = new EndpointAddress(PipeService + @"/" + ClientPipeServiceName);
          
                  InstanceContext context = new InstanceContext((IClientCallback)new TestClientCallback());
                  var ClientChannelFactory = new DuplexChannelFactory<IClientService>(context, new NetNamedPipeBinding(), clientEndpoint);
                  var ApiChannelFactory = new ChannelFactory<IApiService>(new NetNamedPipeBinding(), ApiEndpoint);
                  var ClientChannel = ClientChannelFactory.CreateChannel();
                  var ApiChannel = ApiChannelFactory.CreateChannel();
          
          
                  clientHost.Close();
                  apiHost.Close();
              }
          
          void OnOpen(IAsyncResult ar)
              {
                  ServiceHost service = (ServiceHost)ar.AsyncState;
                  service.EndOpen(ar);
              }
          

          我注意到这段代码需要 20 秒才能运行。然后我决定像这样关闭渠道工厂:

              [TestMethod]
              [Timeout(2000)]
              public void ApiClientTest()
              {
                  bool ApiSuccessSet, ClientSuccessSet = ApiSuccessSet = false;
                  Api apiService = new ApiTestService();
                  var clientService = new ClientTestService();
          
                  ServiceHost clientHost = new ServiceHost(clientService, new Uri(PipeService));
                  ServiceHost apiHost = new ServiceHost(apiService, new Uri(PipeService));
          
                  //To let us know the services were successfully opened
                  clientHost.Opened += (s, e) => ClientSuccessSet = true;
                  apiHost.Opened += (s, e) => ApiSuccessSet = true;
                  clientHost.AddServiceEndpoint(typeof(IClientService), new NetNamedPipeBinding(), ClientPipeServiceName);
                  apiHost.AddServiceEndpoint(typeof(IApiService), new NetNamedPipeBinding(), ApiPipeServiceName);
                  clientHost.BeginOpen(OnOpen, clientHost);
                  apiHost.BeginOpen(OnOpen, apiHost);
          
          
                  //This allows both services to be open for communication.
                  while (!ApiSuccessSet || !ClientSuccessSet)
                      Thread.Yield();
          
          
                  EndpointAddress ApiEndpoint = new EndpointAddress(PipeService + @"/" + ApiPipeServiceName);
                  EndpointAddress clientEndpoint = new EndpointAddress(PipeService + @"/" + ClientPipeServiceName);
          
                  InstanceContext context = new InstanceContext((IClientCallback)new TestClientCallback());
                  var ClientChannelFactory = new DuplexChannelFactory<IClientService>(context, new NetNamedPipeBinding(), clientEndpoint);
                  var ApiChannelFactory = new ChannelFactory<IApiService>(new NetNamedPipeBinding(), ApiEndpoint);
                  var ClientChannel = ClientChannelFactory.CreateChannel();
                  var ApiChannel = ApiChannelFactory.CreateChannel();
          
          
                  ClientChannelFactory.Close();
                  ApiChannelFactory.Close();
                  clientHost.Close();
                  apiHost.Close();
              }
          

          这让我相信处理客户端的实例上下文需要很长时间。

          我怀疑有 3 种方法可以更好地处理此解决方案。

          首先是在管理结束会话的客户端上创建一个函数。这样您就可以在服务计划关闭之前调用该方法,从而加快关闭时间。

          第二种是异步关闭,在连接关闭的同时做其他处理。

          第三个是向客户端编程何时关闭连接(特别是如果您同时控制客户端和服务),以便客户端可以自行终止会话,服务可以优雅快速地关闭。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-12-28
            • 2014-07-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-26
            • 1970-01-01
            相关资源
            最近更新 更多