The no frills, bare-bones example to Duplex WCF
30.01.2008 - 15:51 (2 years, 7 months, 2 days ago)
Filed under download, .NET, TrivadisContent, WCF
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
- 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 = "rf.services",
CallbackContract = typeof(IDataOutputCallback),
SessionMode = SessionMode.Required)]
public interface IServerWithCallback
{
[OperationContract(IsOneWay=true)]
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.
Server
Of course we need an implementation of the service (doh):
class ServerWCallbackImpl : IServerWithCallback {
#region IServerWithCallback Members
public void StartDataOutput() {
IDataOutputCallback callback = OperationContext.Current.GetCallbackChannel<IDataOutputCallback>();
for (int i = 0; i < 10; i++) {
Random r = new Random();
int interval = r.Next(500,3000);
System.Threading.Thread.Sleep(interval);
callback.SendDataPacket("Packet " + i.ToString());
}
callback.SendDataPacket("Last packet is this one :)");
}
#endregion
}<system.serviceModel>
<services>
<service name="ServerPart.ServerWCallbackImpl">
<endpoint address="net.tcp://localhost:9080/DataService" binding="netTcpBinding"
bindingConfiguration="" name="DataEndpoint" contract="CommonParts.IServerWithCallback" />
</service>
</services>
</system.serviceModel>
And some boilerplate to get it running e.g. in a command-line:
ServiceHost svc = new ServiceHost(typeof(ServerWCallbackImpl));
svc.Open();
Console.WriteLine("Listening according to configuration");
Console.ReadKey();
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"); duplex.Open();
Client
Want to call the server? Well, first you could go and configure it:
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:9080/DataService" binding="netTcpBinding"
bindingConfiguration="" contract="CommonParts.IServerWithCallback" name="Callback">
</endpoint>
</client>
</system.serviceModel>Then it'll make sense to provide an implementation of the Callback interface:
class CallbackImpl : IDataOutputCallback {
#region IDataOutputCallback Members
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(); srv.StartDataOutput();
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: There is a small vs2008 solution attached below that shows the programmatic setup of this blog post.
Comments
WCF Xample - where goes what
Guys, why can't you use a name when you comment? Anyway...
I Can't provide a downloadable solution right now, as the one I used is currently polluted, but I shall clean it up again
The chosen service name is a default when you do not provide anything else - It is the fully qualifiedname of the type that implements the contract. All contracts are defined in their own project that is shared between client and server. All contract="..." entries refer to the fully qualified names of the interfaces.
The name namespace in the attribute is in fact unnecessary in this example, as it refers to WSDL specifics and has nothing to do with .NET namespaces
The name Callback of the client endpoint is used again when creating the DuplexChannelFactory and those two must be equal. Hope this helps along a bit .
Nice example... DuplexChannelFactory is a Generic type 'DuplexChannelFactory ()' so your example will not complie.
DuplexChannelFactory cf = new DuplexChannelFactory( new CallbackImpl(), "Callback");
It sure is...the formatting got messed up, should look fine again.
Thanks for the example. Much easier to understand exactly what's happening. I appreciate the 100% programmatic approach too.
Great example - just what I was looking for - not a designer in sight!
FINALLY! No more stuffing around figuring out why I can't "Add Service Reference" and the endless config options. Just clean, simple CODE. Thank you!
This was very helpful -- exactly what I was looking for. Thank you!
will this work against a javascript client script callback? (ajax)
ynoT, I doubt it, since the technological foundation is probably too disparate, but I am not sure. To get that sort of connectivity you should have a look at Comet