一种解决方案中从wpf客户端调用服务器期间出现超时异常

Timeout Exception during call from wpf client to server in one solution

问题是什么:System.TimeoutException:'请求通道在 00:00:59.977913 之后等待回复时超时。所以,基本上什么都可以。

我有什么:

我创建了一个简单的解决方案来找到修复方法。真正让我感到困惑的是控制台应用程序工作得很好,但是具有相同配置的 wpf 应用程序不起作用。解决方案包括四个项目(调试代码,请勿评判):

  1. 包含合同及其实施的图书馆。

    public class DeviceService : IDeviceService
    {
      public string GetDevices()
      {
        return "hello world";
      }
    } 
    
    [ServiceContract]
    public interface IDeviceService
    {
        [OperationContract]
        string GetDevices();
    }
    
  2. 带主机的库。

    public class DeviceServiceHostFactory
    {
    
      ServiceHost host;
    
      public DeviceServiceHostFactory()
      {
        ServiceMetadataBehavior metadataBehavior;
    
        BasicHttpBinding binding = new BasicHttpBinding();
    
        Uri address = new Uri("http://localhost:4000/");
        host = new ServiceHost(typeof(DeviceService), address);
        Type contract = typeof(IDeviceService);
        host.AddServiceEndpoint(contract, binding, "");
      }
    
      public void Start()
      {
        host.Open();
      }
    
      public void Stop()
      {
        host.Close();
      }
    }
    
  3. 启动服务并使用它的桌面应用程序(不工作)

     public partial class MainWindow : Window
     {
       private DeviceServiceHostFactory _deviceService;
    
       public MainWindow()
       {
         InitializeComponent();
    
         try
         {
            _deviceService = new DeviceServiceHostFactory();
            _deviceService.Start();
         }
        catch (Exception ex)
        {
            _deviceService.Stop();
            Console.WriteLine(ex.StackTrace);
        }
      }
    
      private void Btn_custom_Click(object sender, RoutedEventArgs e)
      {
        BasicHttpBinding binding = new BasicHttpBinding();
        EndpointAddress endpoint =
            new EndpointAddress("http://localhost:4000/");
    
        var factory =
            new ChannelFactory<IDeviceService>(
                binding, endpoint);
    
        var channel = factory.CreateChannel();
        txt_custom.Text = channel.GetDevices();
    
        Console.WriteLine();
      }
    } 
    
     [ServiceContract]
     public interface IDeviceService
     {
       [OperationContract]
       string GetDevices();
     }
    
  4. 控制台应用程序(工作正常)

    class Program
    {
    
      static void Main(string[] args)
      {
        DeviceServiceHostFactory _deviceService = new 
             DeviceServiceHostFactory();
    
       try
       {
            _deviceService.Start();
    
            BasicHttpBinding binding = new BasicHttpBinding();
            EndpointAddress endpoint =
                new EndpointAddress("http://localhost:4000/");
    
            var factory =
                new ChannelFactory<IDeviceService>(
                    binding, endpoint);
    
            var channel = factory.CreateChannel();
            Console.WriteLine(channel.GetDevices());
            Console.ReadLine();
        }
        catch (Exception ex)
        {
            _deviceService.Stop();
            Console.WriteLine(ex.StackTrace);
        }
      }
    }
    
    [ServiceContract]
    public interface IDeviceService
    {
      [OperationContract]
      string GetDevices();
    }
    

我真的为此花了很多时间,我将非常感谢每一个解决方案或想到如何调试它更高级。

首先,我们在占用操作系统端口托管服务时,应该给当前账号权限。
这个功能可以通过下面的命令来完成。

Netsh http add urlacl url=https://+:80/MyUri user=DOMAIN\user

https://docs.microsoft.com/en-us/windows/win32/http/add-urlacl
如果我们不想这样做,我们可以直接 运行 使用管理员帐户的服务。 因此,我怀疑托管服务的过程有问题。您是否尝试过 运行使用管理员帐户运行 WPF 应用程序?
此外,我建议你在服务契约中添加一个命名空间。

[ServiceContract(Namespace ="MyNamespace")]
public interface IDeviceService
{
    [OperationContract]
    string GetDevices();
}

有时,如果服务合同没有命名空间 属性,它可能 运行 会出现问题 属性。
如果问题仍然存在,请随时告诉我。

在 UI 的应用程序中托管 wcf 服务有点棘手,所以我希望这对某人有所帮助。

从书Learning WCF: A hands-On Guide By Michele Leroux Bustamante, Chapter 4,如需更多信息,请查找本书。

要在 Windows 应用程序或 WPF 应用程序中托管服务,我们必须创建一个新线程以在新的同步上下文中启动它。可以通过两种方式完成:

首先,是在创建UI线程之前创建服务主机。这里服务在应用程序启动之前在新的同步上下文中执行。

static class Program
{
    static void Main()
    {
        DeviceServiceHostFactory deviceService = new DeviceServiceHostFactory();
        deviceService.Start();

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainWindow);
    }
}

其次,在UI创建

之后,在单独的线程上初始化服务主机
 public partial class MainWindow : Window
 {

    public MainWindow()
    {
        InitializeComponent();

        Thread thread;
        thread = new Thread(ServiceInitialize); 
        thread.IsBackground = true;
        thread.Start();
    }

    private void ServiceInitialize()
    {       
        var service  = new DeviceServiceHostFactory();
        service.Start();
    }
 }

这意味着消息是在线程池中的线程上处理的,而不是通过消息循环。