我遇到了一个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");
}
连接已完成,但当&#34; IsServiceInitiated&#34;方法被调用,应用程序冻结。
但是,如果创建专用于启动服务器的应用程序,以及专用于连接到服务器的客户端的另一个应用程序,则该方法不会冻结并返回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窗体之前打开服务器主机(&#34;显示&#34;方法),但是如果在显示任何Windows窗体后打开的主机或在Windows的构造函数内,我的服务都能正常工作表格,WCF方法调用冻结.......非常奇怪...
答案 0 :(得分:0)
您的代码似乎有几个问题。绑定设置有问题,您的服务定义和接口有问题等。
以下是基于您的代码的工作副本。确保正确调整绑定。
如果这不能解决您的问题,您可以粘贴客户端调用堆栈吗?
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();
}
}
}
答案 1 :(得分:0)
我找到了解决方案。我不明白为什么在Windows Forms的这种特定情况下会发生这种情况,但我必须在一个单独的线程上启动服务器。 所以我在表单构造函数中添加了这个更改:
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");
}
它现在正在工作!!
如果有人知道为什么会出现这种行为,我真的很感兴趣。
由于
答案 2 :(得分:0)
在初始化基本窗口之前打开主机。这样主机就不会将自己与表单的SynchronizationContext关联,而是在一个单独的线程上运行。
e.g:
ServiceHost host = new ServiceHost(typeof(BaseService));
host.Open();
Application.Run(new BaseForm()); // already exists