【问题标题】:.NET wcf call method freezes.NET wcf 调用方法冻结
【发布时间】:2017-03-23 17:52:56
【问题描述】:

我遇到了 WCF 服务方法调用的问题,该方法调用冻结并且不返回响应如果在同一进程上完成连接...

这是完整的代码(非常简化,只显示需要的内容):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;

namespace TestProject
{

    [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IBaseServiceCallback))]
    public interface IBaseService
    {
        [OperationContract]
        bool IsServiceInitiated();
    }

    public interface IBaseServiceCallback
    {
        [OperationContract]
        bool IsClientInitiated();
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, AutomaticSessionShutdown = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class BaseService : IBaseService
    {
        public bool IsServiceInitiated()
        {
            return true;
        }
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, AutomaticSessionShutdown = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class BaseServiceCallback : IBaseServiceCallback
    {
        public bool IsClientInitiated()
        {
            return true;
        }
    }

    public class CustomServer
    {
        private BaseService service;
        private ServiceHost host;

        public bool IsStarted
        {
            get { return host != null && host.State == CommunicationState.Opened; }
        }

        public CustomServer()
        {
            service = new BaseService();
            host = new ServiceHost(service, new Uri[] { new Uri("net.tcp://localhost:7780") });

            Type interfaceType = typeof(IBaseService);

            // Create service end point
            ServiceEndpoint endpointPipe = host.AddServiceEndpoint(interfaceType, new NetTcpBinding(), "CustomService");

            // Define TCP binding
            NetTcpBinding bindingPipe = (NetTcpBinding)endpointPipe.Binding;
            bindingPipe.MaxReceivedMessageSize = 5000000;
            bindingPipe.MaxBufferPoolSize = 5000000;
            bindingPipe.MaxBufferSize = 5000000;
            bindingPipe.ReaderQuotas.MaxDepth = 2048;
            bindingPipe.Security.Mode = SecurityMode.None;
            bindingPipe.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
            bindingPipe.Security.Message.ClientCredentialType = MessageCredentialType.None;

            // Increase MaxItemsInObjectGraph for all operations behaviors
            foreach (OperationDescription op in endpointPipe.Contract.Operations)
            {
                var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();

                if (dataContractBehavior != null)
                {
                    dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
                }
            }

            // In order to publish the service contract, it is important to publish the metadata
            ServiceMetadataBehavior smb = host.Description.Behaviors.Find<ServiceMetadataBehavior>();

            if (smb == null)
            {
                smb = new ServiceMetadataBehavior();
            }

            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;

            host.Description.Behaviors.Add(smb);

            // Add MEX endpoint
            host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexTcpBinding(), "net.tcp://localhost:7780/IDMmex");
        }

        public void Start()
        {
            // Open for listening
            host.Open();
        }

        public void Stop()
        {
            // Stop listening
            host.Close();
        }
    }

    public class CustomClient
    {
        public IBaseService ServiceProxy { get; private set; }
        private BaseServiceCallback callback;

        public CustomClient()
        {
            callback = new BaseServiceCallback();
        }

        public void Connect()
        {
            string serviceUrl = "net.tcp://localhost:7780/CustomService";

            // Create a channel in order to find the exact call back type.
            DuplexChannelFactory<IBaseService> sampleChannel = new DuplexChannelFactory<IBaseService>(callback, new NetTcpBinding(), new EndpointAddress(serviceUrl));

            Type duplexChannelFactory = typeof(DuplexChannelFactory<>).MakeGenericType(new Type[] { typeof(IBaseService) });

            object pipeFactory = Activator.CreateInstance(duplexChannelFactory, new object[] { callback, new NetTcpBinding(), new EndpointAddress(serviceUrl) });

            // Get the service end point
            ServiceEndpoint endpoint = (ServiceEndpoint)duplexChannelFactory.GetProperty("Endpoint").GetValue(pipeFactory, null);

            // Configure TCP binding
            NetTcpBinding tcpBinding = (NetTcpBinding)endpoint.Binding;
            tcpBinding.MaxReceivedMessageSize = 5000000;
            tcpBinding.MaxBufferPoolSize = 5000000;
            tcpBinding.MaxBufferSize = 5000000;
            tcpBinding.ReaderQuotas.MaxDepth = 2048;
            tcpBinding.Security.Mode = SecurityMode.None;
            tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.None;
            tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
            tcpBinding.SendTimeout = TimeSpan.MaxValue;
            tcpBinding.ReceiveTimeout = TimeSpan.MaxValue;
            tcpBinding.OpenTimeout = TimeSpan.MaxValue;
            tcpBinding.CloseTimeout = TimeSpan.MaxValue;

            // Increase MaxItemsInObjectGraph for all operations behaviors
            foreach (OperationDescription op in endpoint.Contract.Operations)
            {
                var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();

                if (dataContractBehavior != null)
                {
                    dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
                }
            }

            // Create the channel to retrieve the pipe proxy object
            MethodInfo method = duplexChannelFactory.GetMethod("CreateChannel", new Type[0]);
            object pipeProxyObject = method.Invoke(pipeFactory, new object[] { });

            // Set the service proxy with the retrieved pipe proxy object
            ServiceProxy = (IBaseService)pipeProxyObject;
        }
    }
}

然后,我有一个 Windows 窗体应用程序,它启动一个 CustomServer、一个 CustomClient 并尝试进行通信:

CustomClient customClient = new CustomClient();
CustomServer customServer = new CustomServer();
customServer.Start();
customClient.Connect();

if (customClient.ServiceProxy.IsServiceInitiated()) // FREEZE HERE !!
{
    MessageBox.Show("Server initiated");
}

连接完成,但调用“IsServiceInitiated”方法时,应用程序冻结。

但是,如果创建一个专用于启动服务器的应用程序,以及另一个专用于连接到服务器的客户端的应用程序,则该方法不会冻结并返回 true。

真的需要任何帮助。

非常感谢。

编辑: 好的,我在控制台中添加了消息检查器和调度检查器,并输出了 BeforeSendRequest 和 AfterReceiveRequest 的结果。结果如下:

Before send request: Action = http://tempuri.org/IBaseService/IsServiceInitiated Reply = 
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/IBaseService/IsServiceInitiated</a:Action>
    <a:MessageID>urn:uuid:d14b0af4-81c3-46c7-9b38-83a8fb092028</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPoy1+4Je4rwBHkZsB8NCWGqQAAAAAAM8Ng9k570ayit0OK365Vn8yY2g0amdHlrkBcRNGylUACQAA</VsDebuggerCausalityData>
  </s:Header>
  <s:Body>
    <IsServiceInitiated xmlns="http://tempuri.org/" />
  </s:Body>
</s:Envelope>

After receive request: Action = http://tempuri.org/IBaseService/IsServiceInitiated Reply = 
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/IBaseService/IsServiceInitiated</a:Action>
    <a:MessageID>urn:uuid:d14b0af4-81c3-46c7-9b38-83a8fb092028</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPoy1+4Je4rwBHkZsB8NCWGqQAAAAAAM8Ng9k570ayit0OK365Vn8yY2g0amdHlrkBcRNGylUACQAA</VsDebuggerCausalityData>
    <a:To s:mustUnderstand="1">net.tcp://localhost:7780/CustomService</a:To>
  </s:Header>
  <s:Body>
    <IsServiceInitiated xmlns="http://tempuri.org/">
    </IsServiceInitiated>
  </s:Body>
</s:Envelope>

我等待冻结的方法,终于得到了一个CommunicationException: 服务器没有提供有意义的回复;这可能是由合同不匹配、会话过早关闭或内部服务器错误引起的

新编辑:

太棒了!!! 当使用单独的应用程序启动服务器并且第一个应用程序将客户端连接到该服务器时,它可以工作并且消息检查器不会启动我的 AfterReceiveRequest 事件,但我得到了 BeforeReceiveReply 输出:

Before receive reply: Action = http://tempuri.org/IBaseService/IsServiceInitiatedResponse Reply = 
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/IBaseService/IsServiceInitiatedResponse</a:Action>
    <a:RelatesTo>urn:uuid:bc055419-68d6-4f08-8170-0d1097e11d39</a:RelatesTo>
    <a:To s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/anonymous</a:To>
  </s:Header>
  <s:Body>
    <IsServiceInitiatedResponse xmlns="http://tempuri.org/">
      <IsServiceInitiatedResult>true</IsServiceInitiatedResult>
    </IsServiceInitiatedResponse>
  </s:Body>
</s:Envelope>

再次编辑: 我注意到如果在显示 Windows 窗体之前打开服务器主机(“显示”方法),我的服务可以完美运行,但是如果在显示任何 Windows 窗体之后或在 Windows 窗体的构造函数中打开主机,则 WCF 方法调用freeze.......很奇怪......

【问题讨论】:

  • 如果你在不同的线程上创建和打开你的 serviceHost 会发生什么?事情有效吗?在不同的 AppDomain 上呢?
  • 那个是解决办法,但是我不明白为什么我不能在表单的同步上下文中启动主机并与它通信...

标签: c# .net wcf


【解决方案1】:

在基本窗口初始化之前打开主机。这样主机就不会将自己与表单的 SynchronizationContext 关联起来,而是在单独的线程上运行。

例如:

ServiceHost host = new ServiceHost(typeof(BaseService));
host.Open();

Application.Run(new BaseForm()); // already exists

【讨论】:

    【解决方案2】:

    您的代码似乎有几个问题。绑定设置有问题,你的服务定义和接口有问题等等。

    以下是基于您的代码的工作副本。确保正确调整绑定。

    如果这不能解决您的问题,能否请您粘贴客户端调用堆栈?

    using System;
    using System.Reflection;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    
    namespace ConsoleApplication5
    {
        class Program
        {
            static void Main(string[] args)
            {
                CustomServer server = new CustomServer();
                server.Open();
    
                CustomClient client = new CustomClient();
                client.Connect();
    
                Console.WriteLine("Press Enter");
                Console.ReadLine();
    
                server.Close();
            }
        }
    
        [ServiceContract(CallbackContract = typeof(IBaseServiceCallback))]
        public interface IBaseService
        {
            [OperationContract]
            bool IsServiceInitiated();
        }
    
        public interface IBaseServiceCallback
        {
            [OperationContract]
            void TheCallback(string str);
        }
    
        //Change3
        //[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, AutomaticSessionShutdown = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
        [CallbackBehavior]
        public class BaseServiceCallback : IBaseServiceCallback
        {
            public void TheCallback(string str)
            {
                Console.WriteLine(str);
            }
        }
    
        [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, AutomaticSessionShutdown = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
        public class BaseService : IBaseService
        {
            public bool IsServiceInitiated()
            {
                return true;
            }
        }
    
        public class CustomServer
        {
            private BaseService service;
            private ServiceHost host;
    
            public bool IsStarted
            {
                get { return host != null && host.State == CommunicationState.Opened; }
            }
    
            public CustomServer()
            {
                service = new BaseService();
                host = new ServiceHost(service, new Uri[] { new Uri("net.tcp://localhost:7780") });
    
                Type interfaceType = typeof(IBaseService);
    
                // Create service end point
                ServiceEndpoint endpointPipe = host.AddServiceEndpoint(interfaceType, new NetTcpBinding(), "CustomService");
    
                // Define TCP binding
                NetTcpBinding bindingPipe = (NetTcpBinding)endpointPipe.Binding;
    
                //Change1
                //bindingPipe.MaxReceivedMessageSize = long.MaxValue;
                //bindingPipe.MaxBufferPoolSize = long.MaxValue;
                //bindingPipe.MaxBufferSize = int.MaxValue;
    
                //bindingPipe.ReaderQuotas.MaxDepth = 2048;
                //bindingPipe.Security.Mode = SecurityMode.None;
                //bindingPipe.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
                //bindingPipe.Security.Message.ClientCredentialType = MessageCredentialType.None;
    
                // Increase MaxItemsInObjectGraph for all operations behaviors
                foreach (OperationDescription op in endpointPipe.Contract.Operations)
                {
                    var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
    
                    if (dataContractBehavior != null)
                    {
                        dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
                    }
                }
    
                // In order to publish the service contract, it is important to publish the metadata
                ServiceMetadataBehavior smb = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
    
                if (smb == null)
                {
                    smb = new ServiceMetadataBehavior();
                }
    
                smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
    
                host.Description.Behaviors.Add(smb);
    
                // Add MEX endpoint
                host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexTcpBinding(), "net.tcp://localhost:7780/IDMmex");
            }
    
            public void Open()
            {
                if (host != null)
                {
                    host.Open();
                }
            }
    
            public void Close()
            {
                if (host != null)
                {
                    host.Close();
                }
            }
        }
    
        public class CustomClient
        {
            private IBaseService serviceProxy;
            private BaseServiceCallback callback;
    
            public CustomClient()
            {
                callback = new BaseServiceCallback();
            }
    
            public void Connect()
            {
                string serviceUrl = "net.tcp://localhost:7780/CustomService";
    
                // Create a channel in order to find the exact call back type.
                DuplexChannelFactory<IBaseService> sampleChannel = new DuplexChannelFactory<IBaseService>(callback, new NetTcpBinding(), new EndpointAddress(serviceUrl));
    
                Type duplexChannelFactory = typeof(DuplexChannelFactory<>).MakeGenericType(new Type[] { typeof(IBaseService) });
    
                object pipeFactory = Activator.CreateInstance(duplexChannelFactory, new object[] { callback, new NetTcpBinding(), new EndpointAddress(serviceUrl) });
    
                // Get the service end point
                ServiceEndpoint endpoint = (ServiceEndpoint)duplexChannelFactory.GetProperty("Endpoint").GetValue(pipeFactory, null);
    
                // Configure TCP binding
                //NetTcpBinding tcpBinding = (NetTcpBinding)endpoint.Binding;
                //tcpBinding.MaxReceivedMessageSize = long.MaxValue;
                //tcpBinding.MaxBufferPoolSize = long.MaxValue;
                //tcpBinding.MaxBufferSize = int.MaxValue;
                //tcpBinding.ReaderQuotas.MaxDepth = 2048;
                //tcpBinding.Security.Mode = SecurityMode.None;
                //tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.None;
                //tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
                //tcpBinding.SendTimeout = TimeSpan.MaxValue;
                //tcpBinding.ReceiveTimeout = TimeSpan.MaxValue;
                //tcpBinding.OpenTimeout = TimeSpan.MaxValue;
                //tcpBinding.CloseTimeout = TimeSpan.MaxValue;
    
                // Increase MaxItemsInObjectGraph for all operations behaviors
                foreach (OperationDescription op in endpoint.Contract.Operations)
                {
                    var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
    
                    if (dataContractBehavior != null)
                    {
                        dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
                    }
                }
    
                //serviceProxy = sampleChannel.CreateChannel();
    
                // Create the channel to retrieve the pipe proxy object
                MethodInfo method = duplexChannelFactory.GetMethod("CreateChannel", new Type[0]);
                object pipeProxyObject = method.Invoke(pipeFactory, new object[] { });
    
                // Set the service proxy with the retrieved pipe proxy object
                serviceProxy = (IBaseService)pipeProxyObject;
    
                //Change2
                ((IChannel)serviceProxy).Open();
    
                // FREEZE HERE...
                bool isServerInitiated = serviceProxy.IsServiceInitiated();
            }
        }
    }
    

    【讨论】:

    • 感谢您的回答。我更正了,因为事实上,我没有测试这个简化的代码。看到我的问题的结尾,我指定了两个应用程序,一个用于服务器,一个用于客户端,没有问题。实际上,我更正了 NetTcpBinding 以确保它正常工作。如果需要,我可以分享整个项目...
    • 我试过你的修改(没有 NetTcpBinding 配置和客户端连接,将服务代理转换为 IChannel 并调用“打开”方法)但 IsServiceInitiated 方法调用仍然冻结...
    • 抱歉,我没有注意到您的请求。如何检索客户端调用堆栈?非常感谢。
    • 好的,我添加了消息检查器和调度检查器,并在控制台输出了BeforeSendRequest和AfterReceiveRequest的结果:
    【解决方案3】:

    我找到了解决方案。我不知道为什么会在这种特定情况下使用 Windows 窗体发生这种情况,但我必须在单独的线程上启动服务器。 所以我在我的表单构造函数中添加了这个更改:

    CustomClient customClient = new CustomClient();
    CustomServer customServer = new CustomServer();
    
    Exception serverStartException = null;
    
    System.Threading.Tasks.Task.Factory.StartNew(() =>
    {
        try
        {
            customServer.Start();
        }
        catch (Exception e)
        {
            serverStartException = e;
        }
    }
    
    int maxTries = 5;
    int currentTry = 0;
    
    while (!customServer.IsStarted && currentTry < maxTries && serverStartException == null)
    {
        System.Threading.Thread.Sleep(1000);
        currentTry++;
    }
    
    if (serverStartException != null)
    {
        throw new Exception("The server couldn't start", serverStartException );
    }
    else if (!customServer.IsStarted)
    {
        throw new Exception("The server couldn't start for unknown reason");
    }
    
    customClient.Connect();
    
    if (customClient.ServiceProxy.IsServiceInitiated())
    {
        MessageBox.Show("Server initiated");
    }
    

    它现在正在工作!

    如果有人知道为什么会出现这种行为,我真的很感兴趣。

    谢谢

    【讨论】:

    • 不要做 while (!isStarted) 的事情......它令人讨厌,hacky 和可怕:) 使用 ManualResetEvent 代替。此外,如果它未能在分配的时间范围内启动,请给它一些超时并抛出。这很可能是由于 WPF 的同步上下文。不幸的是,如果没有完整的转储,就无法知道导致这种情况发生的原因。
    • 实际上,我写了一个简单的代码,但实际上,如果服务器在睡眠 X 次后没有启动,我添加了一个最大尝试计数器来抛出异常,但我会看看ManualResetEvent 因为我不知道这个技巧。
    猜你喜欢
    • 1970-01-01
    • 2018-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多