2011-06-01 128 views
3

我有这个奇怪的问题,当我的客户端将从我的WCF服务调用方法时挂起。现在真正奇怪的是,当客户端是控制台应用程序时,不会发生这种情况。它发生在客户端是WinForm或WPF应用程序时。从服务调用方法WCF客户端冻结

我创建了一个WCF客户端可以使用连接到服务的客户端库,在这里看到:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.ServiceModel; //needed for WCF communication 

namespace DCC_Client 
{ 
    public class DCCClient 
    { 
     private DuplexChannelFactory<ServiceReference1.IDCCService> dualFactory; 

     public ServiceReference1.IDCCService Proxy; 

     public DCCClient() 
     { 
      //Setup the duplex channel to the service... 
      NetNamedPipeBinding binding = new NetNamedPipeBinding(); 
      dualFactory = new DuplexChannelFactory<ServiceReference1.IDCCService>(new Callbacks(), binding, new EndpointAddress("net.pipe://localhost/DCCService")); 
     } 

     public void Open() 
     { 
      Proxy = dualFactory.CreateChannel(); 
     } 

     public void Close() 
     { 
      dualFactory.Close(); 
     } 
    } 

    public class Callbacks : ServiceReference1.IDCCServiceCallback 
    { 
     void ServiceReference1.IDCCServiceCallback.OnCallback(string id, string message, Guid key) 
     { 
      Console.WriteLine(string.Format("{0}: {1}", id, message)); 
     } 
    } 
} 

下面是工作 WCF控制台客户端代码:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

using DCC_Client; 

namespace Client_Console_Test 
{ 
    class Program 
    { 
     private static DCCClient DCCClient; 

     static void Main(string[] args) 
     { 
      try 
      { 
       DCCClient = new DCCClient(); 

       DCCClient.Open(); 

       DCCClient.Proxy.DCCInitialize(); //returns fine from here 

       Console.ReadLine(); 
       DCCClient.Proxy.DCCUninitialize(); 

       DCCClient.Close(); 
      } 
      catch (Exception e) 
      { 
       throw; 
      } 
     } 
    } 
} 

这里是WPF客户端的代码冻结(看评论)

using System; //etc 

using DCC_Client; //Used for connection to DCC Service 

namespace Client_WPF_Test 
{ 
    public partial class Main : Window 
    { 
     private static DCCClient DCCClient; 

     public Main() 
     { 
      InitializeComponent(); 

      DCCClient = new DCCClient(); 

      DCCClient.Open(); 
     } 

     private void Connect_btn_event() { 

      try 
      { 
       DCCClient.Proxy.DCCInitialize(); //**never returns from this** 
      } 
      catch (Exception e) 
      { 
       MessageBox.Show(e.Message); 
      } 
     } 

我进入了代码DCCClient.Proxy.DCCInitialize();,服务成功执行了这些命令,但是由于某些原因,客户端卡在这里并且不会继续执行。客户端不会例外,并且堆栈跟踪显示[外部代码]。

这就是说,控制台客户端运行完美。我想我在这里错过了一些简单的东西。我感谢您提供的任何帮助。

回答

13

如果您的服务直接从DCCInitialize回拨客户端,并且操作和回调操作都未标记为单向,则您的应用程序将会死锁。试着用这个属性标记您的回调实现:

[CallbackBehavior(ConcurrencyMode=ConcurrencyModel.Reentrant)] 

的这种相反,你也可以尝试用

[OperationContract(IsOneWay=true)] 

,以纪念这两个合约的操作,但两个操作必须返回void

在过去的如果这些都不能帮助您标记回调执行:

[CallbackBehavior(UseSynchronizationContext=false)] 

但在这种情况下,您的回调操作将在另一个线程中运行,并且将无法直接使用UI控件进行操作。

编辑:在UI线程托管时

WCF的行为不同。在这种情况下,所有的请求都会在标准的windows消息循环中按顺序处理,所以如果你调用服务,你阻塞了你当前的线程,但是服务调用了你的客户端,它等待处理消息,但是它不能,因为线程被阻塞初始调用=死锁直到初始请求超时。通过使用最后提到的行为,你会说WCF不加入Windows消息循环,而是像平常一样在单独的线程中处理消息。除此之外,没有任何安全问题,除了不能从其他线程中运行的方法访问UI控件 - WinForms和WPF都有办法从其他线程传递命令。

+0

感谢拉迪斯拉夫您的快速回复。我的回拨在我的合同中已经有'[OperationContract(IsOneWay = true)]'。我尝试在我的Callback实现中添加'[CallbackBehavior(ConcurrencyMode = ConcurrencyModel.Reentrant)]'并且客户端仍然会冻结。然后我使用了'[CallbackBehavior(UseSynchronizationContext = false)]',它工作!是否有任何安全问题,我应该知道,因为它是在一个不同的线程? – 2011-06-01 19:31:41

+0

我在描述中添加了我的答案。 – 2011-06-01 19:38:44

+0

+1。在尝试了其他一些事情之后,'[CallbackBehavior(UseSynchronizationContext = false)]'也是我的问题的解决方案。 – 2015-05-21 09:41:33