Reputation: 81
I have a problem with a WCF service method call that freeze and doesn't return a response if connection is done on the same process...
Here is the full code (very simplified to show only what is needed):
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;
}
}
}
Then, I have a Windows Forms application that initiate a CustomServer, a CustomClient and that try to communicate:
CustomClient customClient = new CustomClient();
CustomServer customServer = new CustomServer();
customServer.Start();
customClient.Connect();
if (customClient.ServiceProxy.IsServiceInitiated()) // FREEZE HERE !!
{
MessageBox.Show("Server initiated");
}
The connection is done, but when the "IsServiceInitiated" method is called, the application freezes.
But if a create an application dedicated for starting a server, and another application dedicated to a client that connect to the server, the method doesn't freeze and return true.
Any help is really needed.
Thanks a lot.
EDIT: OK, I added a message inspector and dispatch inspector and output results of BeforeSendRequest and AfterReceiveRequest in the console. Here is the result:
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>
I waited on the freezed method and finally got a CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error
NEW EDIT:
Amazing!!! When using a separated application starting the server and the first application connect a client to this server, It works and the message inspector doesn't launch me the AfterReceiveRequest event, but I get the BeforeReceiveReply output :
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>
EDIT AGAIN: I notice that my service works perfectly if the server host is opened before a Windows Form is displayed ("Show" method), but if the host in opened after any Windows Form is displayed or within the constructor of a Windows Form, the WCF method call freeze....... Very strange...
Upvotes: 1
Views: 1162
Reputation: 81
I found a solution. I don't know exactely why this happens in this specific case with Windows Forms, but i have to start the server on a separated thread. So I add in my form constructor this change:
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");
}
And it's now working !!
If anyone knows why there is this behavior, I'm really interested.
Thanks
Upvotes: 0
Reputation: 9763
Open up the host before the base window is initialized. This way the host will not associate itself with the form's SynchronizationContext and will run on a separate thread.
e.g:
ServiceHost host = new ServiceHost(typeof(BaseService));
host.Open();
Application.Run(new BaseForm()); // already exists
Upvotes: 1
Reputation: 361
You seem to have several issues with your code. The binding settings have issues, your service definition and interfaces have problems, etc.
Below is a working copy based off your code. Make sure you adjust the bindings properly.
If this doesn't fix your issue, could you please paste the client callstack?
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();
}
}
}
Upvotes: 0