KallDrexx
KallDrexx

Reputation: 27813

Why do I get a WCF timeout even though my service call and callback are successful?

I'm playing around with hooking up an in-game console to a WCF interface, so an external application can send console commands and receive console output. To accomplish this I created the following service contracts:

public interface IConsoleNetworkCallbacks
{
    [OperationContract(IsOneWay = true)]
    void NewOutput(IEnumerable<string> text, string category);
}

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IConsoleNetworkCallbacks))]
public interface IConsoleInterface
{
    [OperationContract]
    void ProcessInput(string input);

    [OperationContract]
    void ChangeCategory(string category);
}

On the server I implemented it with:

public class ConsoleNetworkInterface : IConsoleInterface, IDisposable
{
    public ConsoleNetworkInterface()
    {
        ConsoleManager.Instance.RegisterOutputUpdateHandler(OutputHandler);
    }

    public void Dispose()
    {
        ConsoleManager.Instance.UnregisterOutputHandler(OutputHandler);
    }

    public void ProcessInput(string input)
    {
        ConsoleManager.Instance.ProcessInput(input);
    }

    public void ChangeCategory(string category)
    {
        ConsoleManager.Instance.UnregisterOutputHandler(OutputHandler);
        ConsoleManager.Instance.RegisterOutputUpdateHandler(OutputHandler, category);
    }

    protected void OutputHandler(IEnumerable<string> text, string category)
    {
        var callbacks = OperationContext.Current.GetCallbackChannel<IConsoleNetworkCallbacks>();
        callbacks.NewOutput(text, category);
    }
}

On the client I implemented the callback with:

public class Callbacks : IConsoleNetworkCallbacks
{
    public void NewOutput(IEnumerable<string> text, string category)
    {
        MessageBox.Show(string.Format("{0} lines received for '{1}' category", text.Count(), category));
    }
}

Finally, I establish the service host with the following class:

public class ConsoleServiceHost : IDisposable
{
    protected ServiceHost _host;

    public ConsoleServiceHost()
    {
        _host = new ServiceHost(typeof(ConsoleNetworkInterface), new Uri[] { new Uri("net.pipe://localhost") });
        _host.AddServiceEndpoint(typeof(IConsoleInterface), new NetNamedPipeBinding(), "FrbConsolePipe");

        _host.Open();
    }

    public void Dispose()
    {
        _host.Close();
    }
}

and use the following code on my client to establish the connection:

    protected Callbacks _callbacks;
    protected IConsoleInterface _proxy;

    protected void ConnectToConsoleServer()
    {
        _callbacks = new Callbacks();
        var factory = new DuplexChannelFactory<IConsoleInterface>(_callbacks,
            new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/FrbConsolePipe"));
        _proxy = factory.CreateChannel();
        _proxy.ProcessInput("Connected");
    }

So what happens is that my ConnectToConsoleServer() is called and then it gets all the way to _proxy.ProcessInput("Connected");. In my game (on the server) I immediately see the output caused by the ProcessInput call, but the client is still stalled on the _proxy.ProcessInput() call.

After a minute my client gets a JIT TimeoutException however at the same time my MessageBox message appears.

So obviously not only is my command being sent immediately, my callback is being correctly called. So why am I getting a timeout exception?

Note: Even removing the MessageBox call, I still have this issue, so it's not an issue of the GUI blocking the callback response.

Upvotes: 0

Views: 1594

Answers (2)

Irfan Bashir Malik
Irfan Bashir Malik

Reputation: 41

You need to specify CallbackBehavior for your client class implementing the Callback interface [CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Multiple, UseSynchronizationContext=false)]

See the following for a better explanation http://www.switchonthecode.com/tutorials/wcf-callbacks-hanging-wpf-applications

Upvotes: 4

Simon MᶜKenzie
Simon MᶜKenzie

Reputation: 8694

It's possible that it's your _proxy.ProcessInput("Connected") which is blocking the call - this is consistent with your timeout experience, as the server sends the response immediately, but the client can't receive it as it's stuck on "ProcessInput". When your call finally times out, the blocking call terminates, at which point the callback completes.

To verify this, could you try invoking using this (non blocking) call instead?

((Action)(() => _proxy.ProcessInput("Connected"))).BeginInvoke(null, null);

Upvotes: 2

Related Questions