100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
.NET Framework

Duplex Communication in WCF

Covers how WCF services push data back to clients using callback contracts, duplex-capable bindings, and session-aware instance management.

Communication PatternsAdvanced9 min readJul 10, 2026
Analogies

What Is Duplex Communication

Duplex communication lets a WCF service call back into the client on its own initiative, rather than only replying to requests. This requires declaring a CallbackContract on the ServiceContract, which specifies an interface the client must implement and expose to the service. On the server side, the operation implementation calls OperationContext.Current.GetCallbackChannel<T>() to obtain a proxy back to that specific client's callback instance and invoke methods on it directly, effectively turning the client into a mini service for the duration of the session.

🏏

Cricket analogy: Getting a callback channel is like a broadcaster handing the on-field reporter a direct line to the studio so the studio can cut to them unprompted, instead of only answering questions the reporter asks — the studio can now initiate contact.

Configuring the Callback Contract

The callback interface is wired up by setting CallbackContract on the ServiceContract attribute, for example [ServiceContract(CallbackContract = typeof(IChatCallback))], and its methods are typically marked IsOneWay = true since the server usually doesn't need a reply from a notification push. On the client, you cannot use a plain ChannelFactory<T> as with ordinary services; instead you construct a DuplexChannelFactory<T> and pass an InstanceContext that wraps an object implementing the callback interface, so incoming calls from the server are dispatched to that object's methods.

🏏

Cricket analogy: Declaring the callback interface is like a team pre-registering exactly which staff member the broadcaster is allowed to interrupt during play — only that designated liaison can receive live updates, nobody else on the bench.

Session and Instance Management

Duplex only works over sessionful bindings because the service needs a durable channel back to a specific client instance: WSDualHttpBinding achieves this over HTTP using two separate one-way channels (the client's callback endpoint must be reachable at its own address), while NetTcpBinding keeps a single persistent TCP connection open in both directions, which avoids the NAT/firewall problems of the dual-HTTP approach. Because of this, services are usually configured with InstanceContextMode.PerSession so each connected client gets its own service instance that can track and reuse its callback channel — for broadcast scenarios, the service typically stores active callback channels in a thread-safe collection, such as a ConcurrentDictionary keyed by a client or subscription ID.

🏏

Cricket analogy: Keeping a persistent session per client is like a stadium assigning each broadcaster a dedicated radio channel for the whole match rather than reconnecting on every over — NetTcpBinding is the single open line, while WSDualHttpBinding is more like using two separate walkie-talkies, one for sending and one for receiving.

Reliability, Threading and Pitfalls

Callback invocations execute on a client-side ThreadPool thread rather than the UI thread, so a WPF or WinForms client must marshal any UI updates through Dispatcher.Invoke or Control.Invoke before touching bound controls. If the client disconnects or its proxy faults, calling a stale callback channel throws a CommunicationObjectFaultedException (or a general CommunicationException), so services holding onto callback references for broadcast should catch these, remove the dead subscriber, and continue rather than letting one bad channel take down the notification loop. A related pitfall is deadlock: if a client call into the service is still in progress and the service tries to call back into that same client synchronously, the default single-threaded ConcurrencyMode can block; ConcurrencyMode.Reentrant is often needed on the service contract to allow that nested callback safely.

🏏

Cricket analogy: Forgetting to marshal a callback onto the UI thread is like a runner sprinting straight onto the pitch mid-delivery to update the scoreboard by hand instead of signaling the official scorer — it touches something it isn't allowed to touch directly and things break.

csharp
[ServiceContract(CallbackContract = typeof(IChatCallback))]
public interface IChatService
{
    [OperationContract]
    void Join(string userName);

    [OperationContract]
    void SendMessage(string text);
}

public interface IChatCallback
{
    [OperationContract(IsOneWay = true)]
    void OnMessageReceived(string userName, string text);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
                 ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class ChatService : IChatService
{
    private static readonly ConcurrentDictionary<string, IChatCallback> Subscribers = new();

    public void Join(string userName)
    {
        var callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
        Subscribers[userName] = callback;
    }

    public void SendMessage(string text)
    {
        foreach (var kvp in Subscribers.ToArray())
        {
            try { kvp.Value.OnMessageReceived("server", text); }
            catch (CommunicationException) { Subscribers.TryRemove(kvp.Key, out _); }
        }
    }
}

WSDualHttpBinding requires the client's callback endpoint to be reachable at a real, routable address — problematic behind NAT or restrictive firewalls. NetTcpBinding avoids this entirely because it multiplexes both directions over a single already-established TCP connection, which is why it's the more common choice for duplex on an intranet.

Storing raw callback channel references indefinitely without pruning faulted ones is a common source of memory leaks and noisy exceptions in long-running duplex services. Always wrap calls to a cached callback channel in a try/catch for CommunicationException and ObjectDisposedException, and remove the entry on failure.

  • Duplex requires a CallbackContract declared on the ServiceContract and a duplex-capable binding.
  • Clients must use DuplexChannelFactory<T> with an InstanceContext wrapping their callback implementation.
  • Servers obtain the client's callback channel via OperationContext.Current.GetCallbackChannel<T>().
  • NetTcpBinding uses one persistent bidirectional TCP connection; WSDualHttpBinding uses two separate HTTP channels and needs a reachable client endpoint.
  • InstanceContextMode.PerSession is typically used so each client's callback channel is tracked against its own service instance.
  • Callback methods run on a client ThreadPool thread and must be marshaled to the UI thread manually.
  • ConcurrencyMode.Reentrant helps avoid deadlocks when the server calls back into a client mid-request.

Practice what you learned

Was this page helpful?

Topics covered

#NETFramework#WCFWindowsCommunicationFoundationStudyNotes#MicrosoftTechnologies#DuplexCommunicationInWCF#Duplex#Communication#WCF#Configuring#StudyNotes#SkillVeris