Long time ago WCF has introduced DUPLEX channels (bindings). A duplex service, can send messages back to the client endpoint, providing event-like behavior. Duplex communication is established when one client connects to a service and provides the service with a channel on which the service can send messages back to the client. The idea behind duplex is very nice and promising. Unfortunately there are few limitations which prevents this pattern to be widely used in almost all scenarios.
For example, duplex works on top of the session which is established between client and the service. If this session is broken the callbacks cannot be invoked etc., etc..
Moreover, if the client is invoking the service operation from internet location which is hidden by firewall NAT, the service will not be able to reach the client.
In fact, Duplex has infrastructural and pattern driven limitations.
This should be slightly changed in WCF 4.5. The WCF 4.5 introduces new binding NetHttpBinding, which establishes the duplex-session on top of WebSocket protocol. This protocol allows calling back operation event if the client is behind the firewall.
How does this can work?
On the beginning of the session client sends the typical HTTP request to service. This request contains a few more header like Upgrade-, Sec-WebSocket-Version-, Sec-WebSocket-Key header etc. After that Binding can use polling or it can open the permanent TCP connection between client and service (ConnectionMode= Upgrade/Allowed).
Today we expect that TCP connection which is basically TCP- streaming between client and service established after first HTTP-request. This is very powerful mechanism to establish real time communication are real evening. Remember Duplex was as a pattern the simulation of evening. If the connection is not upgraded the polling will still be just a simulation of evening. However what ever the connection mode is used, the NAT issue is behind us. It will just work.
Please do not be confused with other implementations of WebSockets. At the moment there are four implementations.Not all of them provide the same thing.
For example, following four libraries provides the functionality around WebSocket specification. They all are compatible with each other. Note that .NET implementation of the client functionality is missing and as I know it is unfortunately currently not at Microsoft Agenda. That means, if you want to use WebSockets as a .NET client you will have to use WinRT Windows.Networking.Sockets.Dll.
Microsoft.ServiceModel.WebSockets
Provides the native WebSocket Server implementation which can be hosted on top of WCF. (See WebSocketService)
Microsoft.Web.WebSockets
Provides the native WebSocket Server implementation of WebSockets handlers in ASP.NET and ASP.NET MVC. (See WebSocketHandler)
WIndows.Networking.Sockets
WinRT implementation of WebSocket client functionality .
Java Script
Browser imlementation of client functionality, curentlly suported on IE10 only (in the Windows world)
Last but not least, the namespace System.ServiceModel contains the Duplex implementation based of WebSockets. You cannot use this one in combination with any of libraries listed above. This post is related to this namespace only! Don’t get confused.
Let’s build one sample
Service
Following code-snipet shows the service contract related to CallBack-ISampleServiceCallback .
namespace Service { [ServiceContract(Namespace="http://daenet.com/orderservice", CallbackContract = typeof(ISampleServiceCallback))] public interface ISampleService { [OperationContract] bool CreateOrder(long productId, int amount, long customerId); } } |
An this is how the callback contract is designed:
namespace Service { [ServiceContract] public interface ISampleServiceCallback { [OperationContract(IsOneWay=true)] void OrderPurchased(long orderId, string comment); [OperationContract(IsOneWay = true)] void OrderCanceled(string reason); } }
|
An here is the the service implementation:
namespace Service { public class SampleService : ISampleService { private static Dictionary<Guid, ISampleServiceCallback> m_CallbackChannels = new Dictionary<Guid, ISampleServiceCallback>(); public bool CreateOrder(long productId, int amount, long customerId) { var ctxId = Guid.NewGuid(); m_CallbackChannels.Add(ctxId, OperationContext.Current.GetCallbackChannel<ISampleServiceCallback>()); new Thread(new ThreadStart(delegate() { Thread.Sleep(5000); if (amount > 10) m_CallbackChannels[ctxId].OrderCanceled("Not in store"); else m_CallbackChannels[ctxId].OrderPurchased(new Random().Next(10000), "Purchased successfully"); })).Start(); return true; } } } |
To make this working we will need a peace of configuration. This is a part of web.config file. That means I used IIS8 to host the web socket. This is important, because to host WebSockets you need to enable WebSocket protocol which is at the moment specifically on Windows 8 only. As already mentioned, I used IIS8, but self-hosted service will work too, as long you are running it on Windows 8. To make WebSockets working Microsoft has slightly changed few things in the http.sys and IIS pipeline implementation. This is why IIS Express does not support WebSockets at the moment (not yet).
<bindings> <netHttpBinding> <binding> <webSocketSettings connectionMode="Allowed" subProtocol="orders" /> <security mode="None" /> </binding> </netHttpBinding> </bindings> |
As you see I used here NetHttpBinding.
Client
Now let’s implement the Client. Create the console or any other application and add the service reference to running service. This is automatically generated.
<customBinding> <binding name="NetHttpBinding_ISampleService"> <binaryMessageEncoding /> <httpTransport> <webSocketSettings connectionMode="Allowed" subProtocol="orders" /> </httpTransport> </binding> </customBinding> |
Generated binding looks a strange, but it works. In fact NetHttpBinding is CustomBinding implementation which use http-transport and binary message encoder.
You can do the same thing by using NetHttpBinding instead of CustomBinding:
<bindings> <netHttpBinding> <binding> <webSocketSettings connectionMode="Allowed" subProtocol="orders" /> <security mode="None" /> </binding> </netHttpBinding> </bindings> |
Posted
Nov 26 2011, 05:36 PM
by
Damir Dobric