MrShoes
MrShoes

Reputation: 485

WCF Duplex Service Channel Close

I have an application based around a WCF Duplex service. I have problems when the user "Restarts" the work the application does... under the hood, the client side closes the connection to the WCF service and creates another. The Service contract is defined like so...

[ServiceContract(Namespace="net.tcp://namespace.MyService",
    SessionMode=SessionMode.Required,
    CallbackContract=typeof(IServiceCallback))]
public interface IMyService
{
    [OperationContract(IsOneWay=true)]
    void DoWork();
}


public interface IServiceCallback
{
    [OperationContract(IsOneWay=true)]
    void SendMessage(string message);
}

The implementation is defined as:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single,
    InstanceContextMode = InstanceContextMode.PerSession,
    UseSynchronizationContext = false,
    IncludeExceptionDetailInFaults = true)]
public class MyService : IMyService
{
    public void DoWork()
    {
        var callback = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
        callback.SendMessage("Hello, world.");
    }
}

The configuration for the client is as follows:

  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="net.tcp" receiveTimeout="02:00:00" sendTimeout="02:00:00" maxReceivedMessageSize="2147483647">
          <security mode="None"/>
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="net.tcp://localhost:8000/MyService/MyService"
          binding="netTcpBinding" bindingConfiguration="net.tcp" contract="ExternalServiceReference.IMyService">
      </endpoint>
    </client>
  </system.serviceModel>

Config for the service:

<system.serviceModel>
<behaviors>
  <serviceBehaviors>
    <behavior name="serviceBehaviour">
      <serviceMetadata />
    </behavior>
  </serviceBehaviors>
</behaviors>
<bindings>
  <netTcpBinding>
    <binding name="netTcp" sendTimeout="01:00:00" receiveTimeout="01:00:00" >
      <security mode="None">
      </security>
    </binding>
  </netTcpBinding>
</bindings>
<services>
  <service behaviorConfiguration="serviceBehaviour" name="MyService.MyService">
    <endpoint address="MyService" binding="netTcpBinding" bindingConfiguration="netTcp" name="net.tcp" contract="MyService.IMyService" />
    <endpoint binding="mexTcpBinding" bindingConfiguration="" name="net.tcp" contract="IMetadataExchange" />
    <host>
      <baseAddresses>
        <add baseAddress="net.tcp://localhost:8000/MyService" />
      </baseAddresses>
    </host>
  </service>
</services>

In the client's contructor:

var callback = new CallbackImplementation();
_context = new InstanceContext(callback);
_proxy = new MyServiceProxy(_context);

I'm trying the following before I establish a new connection:

        try
        {
            if (_context != null)
            {
                _context.ReleaseServiceInstance();
                _context.Close();                    
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            if (_context != null)
            {
                _context.Abort();
            }
        }

The issue I see is that the _context.Close() call always times out and throws an exception. Although I'm then aborting the channel, this feels wrong to me, and I believe it's the cause of freezing in my application. Does anybody know why the Close() call fails?

EDIT: I missed something earlier regarding my callback implementation that might be relevant. It looks something like this:

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Single, 
    UseSynchronizationContext = false, 
    IncludeExceptionDetailInFaults = true)]
public class CallbackImplementation : IServiceCallback
{
    public void SendMessage(string message)
    {
        // Do something with the message
    }
}

The exception message is "The ServiceHost close operation timed out after 00:00:30. This could be because a client failed to close a sessionful channel within the required time. The time allotted to this operation may have been a portion of a longer timeout.". There's no inner exception.

Thanks

Upvotes: 5

Views: 2871

Answers (4)

MrShoes
MrShoes

Reputation: 485

The issue was not only concurrency, but the binding type.

Making sure the concurrency mode of both the service and the callback was a step in the right direction. But changing the binding from netTcpBinding to wsDualHttpBinding has finally fixed the problem

Upvotes: 0

batwad
batwad

Reputation: 3665

At a guess, it could be a deadlock caused by the use of ConcurrencyMode.Single.

What could be happening is that the server is trying to call back to the client while it is still processing the original request (or vice versa). So server is trying to call back to client, but client is blocking because it is still waiting for a response from the server. Hey presto, deadlock and eventually a timeout.

http://msdn.microsoft.com/en-us/library/system.servicemodel.concurrencymode.aspx

Upvotes: 0

Mike Yeager
Mike Yeager

Reputation: 161

I don't see an issue right off, but I often find that running traces on both the client and server and examining the results usually points me to the solution. Put this in your .config files (client and server), make sure the path points to a folder that exists. Run your app, get the failure, then shut everything down and run SvcTraceViewer.exe to read the results.

<system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information,ActivityTracing"
        propagateActivity="true">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add initializeData="C:\logs\TracingAndLogging-service.svclog" type="System.Diagnostics.XmlWriterTraceListener"
        name="xml" />
    </sharedListeners>
    <trace autoflush="true" />
</system.diagnostics>

Upvotes: 1

user3797538
user3797538

Reputation: 35

have you tried receiveTimeout="infinite", to see if you still receive the error. You may just find that it's not the timeout creating the error. Have you thought about creating a base client class that automatically keeps the connections alive till physically closed?

Upvotes: 0

Related Questions