The no frills, bare-bones example to Duplex WCF

30 Jan 2008 in download | dotnet | libs-and-frameworks |

Can a WCF client call a WCF service and have the server call back with whatever? Oh, yes. Can you get an example how it’s done? Indeed.

Are these good examples? Well, you can get MS samples for WCF (which in their entirety are really helpful) or another blog post on the subject. Don’t get me wrong, they work, but in my humble opinion they have shortcomings.

  • They have funny classes and interfaces generated by svcutil with some really horrid artefacts <li>The interfaces are not shared among client & server which annoys my desire for harmony and DRYness, which should be followed even in an example (or, maybe, especially in an example).

Anyway, here’s my version, which is quite reduced to just the duplex stuff.

Common bits

[ServiceContract(Namespace = "", 
 CallbackContract = typeof(IDataOutputCallback), 
 SessionMode = SessionMode.Required)]
public interface IServerWithCallback
    void StartDataOutput();

That’s the Server interface which the client will use to talk to the server. Please note the _CallBackContract _property of the _ServiceContract _attribute. That’s the interface that will have to be implemented by the client. The server will use it to call back. public interface IDataOutputCallback { [OperationContract(IsOneWay = true)] void SendDataPacket(string data); }

All this IsOneWay business is one way to avoid issues when you’re calling back a client within the method that is being called by the client. The issues are described here.


Of course we need an implementation of the service (d’oh):

class ServerWCallbackImpl : IServerWithCallback { 
  public void StartDataOutput() { 
    IDataOutputCallback callback = OperationContext.Current.GetCallbackChannel<IDataOutputCallback>(); 
    for (int i = 0; i &lt; 10; i++) { 
      Random r = new Random(); 
      int interval = r.Next(500,3000); 
      callback.SendDataPacket("Packet " + i.ToString()); 
    callback.SendDataPacket("Last packet is this one :)"); 


The important line is the very first one. The rest is just a crappy example to get some funny callback side-effects. Next we have the configuration of the thing…

    <service name="ServerPart.ServerWCallbackImpl">
      <endpoint address="net.tcp://localhost:9080/DataService" binding="netTcpBinding"
          bindingConfiguration="" name="DataEndpoint" contract="CommonParts.IServerWithCallback" />

And some boilerplate to get it running e.g. in a command-line:

ServiceHost svc = new ServiceHost(typeof(ServerWCallbackImpl)); 
Console.WriteLine("Listening according to configuration"); 

If you want to save yourself the configuration etry in the app.config file, you can also set up the ServiceHost programmatically:

ServiceHost duplex = new ServiceHost(typeof(ServerWCallbackImpl)); 
duplex.AddServiceEndpoint(typeof(IServerWithCallback), new NetTcpBinding(), "net.tcp://localhost:9080/DataService"); 


Want to call the server? Well, first you could go and configure it:

      <endpoint address="net.tcp://localhost:9080/DataService" binding="netTcpBinding"
            bindingConfiguration="" contract="CommonParts.IServerWithCallback" name="Callback">

Then it’ll make sense to provide an implementation of the Callback interface:

class CallbackImpl : IDataOutputCallback { 

  public void SendDataPacket(string data) { 
    Console.WriteLine(data); } #endregion 

Brill! Finally the boilerplate to kickstart the thing:

DuplexChannelFactory<IServerWithCallback> cf = 
  new DuplexChannelFactory<IServerWithCallback>( new CallbackImpl(), "Callback"); 
IServerWithCallback srv = cf.CreateChannel(); 

Again, if you prefer working without a configuration entry, you can set this up programmatically, too:

DuplexChannelFactory<IServerWithCallback> cf = 
  new DuplexChannelFactory<IServerWithCallback>(
  new CallbackImpl(), 
  new NetTcpBinding(), 
  new EndpointAddress("net.tcp://localhost:9080/DataService"));

That’s it, you shouldn’t need more, honest, dude. No generated class in sight, all interfaces are the same throughout Client and Server. Distribute over projects/threads/app domains at your leisure and enjoy.

Update: Here is a small vs2008 solution that shows the programmatic setup of this blog post.


comments powered by Disqus