Monday, March 30, 2009

Building Push Notifications using WCF Duplex binding in Silverlight v2.0/v3.0


Building Push Notifications using WCF Duplex binding in Silverlight v2.0 and v3.0

This article will explain how the WCF Duplex “Push” style architecture works inside of Silverlight. To get started a quick definition of Push needs to be explained, as well as its twin sister, Pull.
Here is the definition for Push and Pull as found on Wikipedia:
Push technology, or server push, describes a style of Internet-based communication where the request for a given transaction originates with the publisher or central server. It is contrasted with pull technology, where the request for the transmission of information originates with the receiver or client.
If I may reword the above definition for the purpose of this article and using WCF Push style notifications in Silverlight, let’s redefine the definition of “Push” to mean a style of communication where requests can originate from a receiver or a sender, as opposed to “Pull” which means for every request, it must originate with the receiver.
In the above definition I purposely leave off terms like polling, and who sends the request first and so forth, as this would easily cloud the definition of the two terms. As a side note however, usually “Pull” is associated with polling, while “Push” is not.
Doing a quick search for the Differences between Push and Pull you quickly run across many blogs, articles and examples outling each. One such study that caught my attention was a 12 page report completed by some students at Delft University of Technology explaining the pros and cons of both architectures using a non Microsoft framework. In their research they found that:
“if we want high data coherence and high network performance, we should choose the push approach. However, push brings some scalability issues; the server application CPU usage is 7 times higher as in pull. According to our results, the server starts to saturate at 350-500 users. For larger number of users, load balancing and server clustering techniques are unavoidable.” (Full 12-page report found here: http://swerl.tudelft.nl/twiki/pub/Main/TechnicalReports/TUD-SERG-2007-016.pdf)

The Push architecture that was tested used an AJAX framework with a push style methodology called COMET. I’m not going to discuss COMET versus anything Microsoft has, however what I will take a broad look at is the Microsoft approach to building a push style Rich Internet based Application, similar to AJAX, using the Silverlight Framework and the new WCF Duplex binding. Within, I’ll point out some similarities between the AJAX Comet style design outlined within the research document, and WCF “Push” style within the MS Silverlight design. This document will not attempt to provide any form of measurement, or test results as outlined within the study. This document simply serves as a practical study on how to implement the same type of architecture utilizing Silverlight and WCF’s Duplex binding.

Silverlight is Microsoft’s flagship product that rivals Adobe’s Flash technology. While Adobe Flash has the capabilities to make calls out into basic web services, and REST styled endpoints, from the time of this writing, it lacks the capability to do “Push” styled applications. The best Flash can do is provide a Pull or Polling styled design within its framework. Silverlight rivals this design with the promise to deliver “Push” style designs within its framework using WCF’s Duplex bindings, but I have to ask myself is it really “Push” or just another hidden Polling mechanism that tries its best to mimic “Push”.

For Starters, let’s discuss WCF in general and get a general overview of WCF and its Duplex communication pattern. WCF stands for Windows Communication Foundation and it is a framework for building distributed .NET applications. The framework is a part of the .NET 3.0 release with updates being applied in versions 3.5 and 3.5 service pack 1.
The WCF framework is also a part of the .NET Compact Framework 3.5, as well as the .NET Framework that ships with Silverlight v2.0. These smaller frameworks previously mentioned contain a small subset of WCF features and functionality for their respective platforms.
The full blown version of WCF has a very rich feature set and functionality. It boasts the ability to communicate with many protocols such as TCP, HTTP, UDP, SOAP, SMTP, MSMQ, NamedPipes just to name a few. It also includes the ability monitor other WCF applications using WMI, and performance counters. In addition to maintenance and health of currently running WCF applications, there is also the ability to log messages and track various events that occur during the normal processing of the WCF framework. On top of all of this WCF supports the majority of WS-Specifications such as WS-Security, WS-Addressing, WS-MTOM encoding, WS-Coordination and WS-Transactions. The point I’m driving at is that WCF is the new plumbing behind how to build distributed applications using the .NET 3.0 and above Framework. The Bottom line is if you’re building distributed software for Microsoft platforms, and you’re not using WCF today, you’re not using the right toolset.
In general WCF is broken down into a couple of different layers: the Service Model, and the messaging layer. The Service Model layer is like a software factory. It creates software or components that help a .NET Application communicate across the wire. The Service Model layer builds various messaging layer components to enforce a Service Level Agreement, found inside of contracts. These agreements contain message exchange patterns, security requirements, transaction requirements, session support, and settings such as the type of network protocol needed to send and receive data.
The Messaging layer is composed of a group of these components. Each component, also known as a channel, individually has a specific task to perform that adhere to one or more items within a Service Level Agreement. The way in which these channels measure up to the overall policy or agreement is by executing only the task assigned to it, such as encrypting a message using a specific Encryption algorithm. In order to adhere to the complete contract, there are usually many more requirements which must be in place such as which networking stack protocol should be used to send and receive data, and what is the format or encoding of the data being sent and received. This is accomplished through the use of different channels.
The Messaging layer allows for multiple channels to be placed on top of each other to provide an ordered structure as to which component/channel will run first, next and last. This ordered structure adheres to the Service Level Agreement. In WCF, this ordering is referred to as the “Channel Stack”.
The Channel Stack helps maintain and enforce a set of policies as outlined by the contract which two applications use to communicate. It also helps explain the design of the Message exchange pattern (MEP) being used for the contract. MEPs are a design that helps two systems communicate back and forth by outlining what is to be expected for receipt and transmission of data. There are many MEPs. One such MEP is known as a Datagram, which is a one –way asynchronous design. The Datagram pattern can flow one way asynchronously in any direction. It could be a one-way send, or a one-way receive. There are also the synchronous one-way send and receive patterns, where the difference is the system which sends or receives waits to get an acknowledgement back that the synchronous Send/Receive has successfully completed, or errored, as opposed to the asynchronous versions, which no acknowledgement is returned, commonly referred to as a “fire –and-forget” design. The Channel stack also supports the two-way MEP patterns commonly referred to as Request/Response and Solicit/Response in both synchronous and asynchronous forms.

In the WCF framework channels implement the MEPs through various different implementations of WCF Interfaces. WCF Supports 5 different interfaces that outline which type of MEP will be used inside a Channel. The interface implemented, also known in WCF as the “Channel Shape” determines if it will be a one-way or a two-way MEP. A declarative attribute, IsOneWay=true, helps the Service Model determine within the channel shape if the MEP will be asynchronous or synchronous. Without going into all 5 of the WCF channel shapes in depth, I will focus however on a few channel shapes in particular: IInputChannel, IOutputChannel, IDuplexChannel and IRequestChannel for comparison sake.
The IInputChannel and IOutputChannel shapes are interfaces that have very basic signatures:
public interface IInputChannel
{
Message Receive()
…[Asyncrhronous Programming model omitted for brevity]…
}

public interface IOutputChannel
{
void Send(Message msg);
…[Asyncrhronous Programming model omitted for brevity]…

}

The IInputChannel shape is used to receive messages from the transport protocol channel, which effectively reads the data off the wire. The IOutputChannel is used to send messages to the transport protocol channel, which effectively writes data on the wire for transmission. Notice that the signature of these methods is one way, being either one way receive or one way send. In case you still don’t see it, there is no way for a client trying to invoke the Receive() method, to send in any data, there are no parameters to be able to pass in any information. When sending, the same is present only this time for receipt. As a client, there is no way to get any response back when sending a message because the return value is “void”. What this boils down to is that any WCF Channel that implements one or more of these shapes will support the one-way MEP. Keep in mind however, that true asynchronous behavior requires that the executing thread not block and wait for a response. This is exactly what the IsAsync=true attribute implies. The client application’s thread invoking the Receive() or Send(Msg) methods will not block and wait for the methods to complete. This effectively means that if the Receive() or Send(Msg) methods throw exceptions the client applications will not be able to catch them.
Moving on to the IDuplexChannel. the IDuplexChannel interface is nothing more than the combination of the IInputChannel and IOutpuChannel interfaces.
public interface IDuplexChannel : IInputChannel, IOutputChannel { … }

What this boils down to is that any channel in the WCF Framework that implements this interface has the ability to do One-way Receives, independent of One-way Sends. In other words, A one way receive can occur unrelated to a one-way send, and they can happen in any order, and in any design. There is no correlation between when the Receive() is invoked and when the Send() is invoked. When we take this duplex design and apply it to both sides of the communication cable, both the sender and receiver applications have the capability to send and receive independent of the other without any special correlation or thread blocking.
Let me digress a little and revisit the authoritative definition of “Push” and “Pull”:
If I may reword the above definition for the purpose of this article and using WCF Push style notifications in Silverlight, let’s redefine the definition of “Push” to mean a style of communication where requests can originate from a receiver or a sender, as opposed to “Pull” which means for every request, it must originate with the receiver.

When we take a look at the Duplex pattern, there are no constraints placed on who sends first, who receives first, or if a send corresponds to a receive and vice versa. In a normal Request/Response pattern, the receiver of the request would send some data, expecting a response to return. Every response is constrained by a mandatory request being sent in as in the signature of the IRequestChannel design:
public interface IRequestChannel
{
Message Request (Message msg);
…(APM omitted for brevity)…
}

Shown for the sake of completion, IReplyChannel, is an interface that usually is implemented by the opposite end to respond to the IRequestChannel interface.
public interface IReplyChannel
{
RequestContext ReceiveRequest();
…(APM omitted for brevity)…
}

This brings us to the obvious point; any WCF channel that implements the IRequestChannel shape supports the “Pull” design. This however does not mean that Channels that implement the IInput/IOutput cannot support the “Pull” design. In order for the IInput/IOutput to support the “Pull” design, there needs to be some correlation and thread blocking, or synchronization between and Send and Receive. WCF supports this too. This can be done using some form of correlated thread blocking session channel. In WCF, this can also be accomplished through fairly custom, and possibly performance bloated CallbackContracts.
A CallbackContract is simply an interface that a Client implements for the purpose of handling a synchronization between client-server exchanges. The interface outlines what is expected for a server/service/sender to send, and what a client/receiver/proxy expects to receive. CallbackContracts are created by using the CallbackContract property of a ServiceContract attribute. As a service developer, you would place this attribute inside the ServiceContract like so:
[ServiceContract(CallbackContract(typeof(INotifyClient))]
public interface IDataService { … operations here … }

Where the INotifyClient is an interface defined without a ServiceContract attribute as such:
public interface INotifyClient
{
//IsOneWay makes this Asynchronous and supports Duplex MEP
[OperationContract(IsOneWay=true)]
void SendToClient(Message msg);
}

Keeping correlation in mind, for the Push design, randomly sending and receiving does not do any justice. Client “A” sending some data to Client “B” dictates in certain solutions that Client “B” needs to be aware that Client “A” is sending some information, thus even in the Duplex pattern there needs to be some form of correlation between the two. WCF Supports this through the ISessionChannel in general. There is also an IDuplexSessionChannel for supporting Duplex Sessions. And Lastly, there are Duplex CallbackContracts that supports this as well such as the INotifyClient displayed above. The difference between regular CallbackContracts, and Duplex supported is simply the IsOneWay=true property on the Operation contract. When you leave this off, extra acknowledgement messages are sent back and forth creating a chatty like behavior and eventually degrading performance.

Now that we’ve discussed a little on what Push is, and how WCF Implements Push Style architecture with Duplex channels, what is available in Silverlight to provide the capability to do Push Style notifications?

Silverlight v2.0 contains a small subset of WCF features and components. Within this feature and component set, there is support for Channels, Channel Factories, and Bindings: basically the messaging layer constrained to specific network protocols. There is no support for the ServiceModel. This design is very similar to WCF for the .NET Compact Framework. What this means is that there is no support for reading a configuration file in Silverlight and having the ServiceModel dynamically build the channel stack. While there’s nothing stopping you from coding this yourself, it’s not included out of the box.

WCF for Silverlight supports only one network protocol channel: HTTP/HTTPS. This means that you can’t use TCP, UDP, MSMQ, NamedPipes or any of the other cool network protocols mentioned earlier with the WCF framework for Silverlight. Don’t be alarmed however; Silverlight does support TCP using the .NET TCP Socket classes, which also support building push style solutions.

Another thing that’s not included in this release of WCF for Silverlight are the WS-* channels that support WS-Coordination, WS-Transactions, WS-Security and the likes. So this means there is no support for using any of the predefined channel stacks known as “Bindings” that come with WCF as well, such as the WSHttpBinding.

So you may be wondering if all the cool things of WCF are removed in WCF for Silverlight what’s left?

Update: Silverlight v3.0 promises new updates in its support for WCF. A scaled down service model will be available for reading configuration files, a new new binary encoder for performance, support for WS-Security specifications, and syntax enhancements to make building WCF Duplex bindings easier.

WCF for Silverlight supports the Basic HTTP binding for WCF, and only SOAP version 1.1 with no Addressing, and Basic Security is also supported using Transport layer security. But this is basic stuff.

The cool support I want to bring to your attention is a new server side WCF predefined binding, as well as the clientside Silverlight binding, that gets installed when you install Silverlight: the PollingDuplexHttpBinding. Don’t let the name fool you. Even though it says: PollingDuplexHttpBinding, it’s still push architecture simply by the authorized definition of “Push”. Threre’s polling on the server side, and polling on the Client side. However on the Silverlight client side the polling is occurring in the network layer using the WS-MC (Make Connection standard). On the server side, the polling is just to check the session timeout which occurrs less frequently than the client. This polling architecture is due in part to the limitation of the HTTP transport protocol, and constraints within the current version of Silveright. HTTP will not allow for a server to listen on a separate address within the same connection as in the TCP protocol, and Silverlight will not allow for a mini HTTP listener within its engine which would allow for a http addressable endpoint for communication, thus, the reason for the WS-MC implementation, and polling architecture. This is also the reason for the statement:
“Silverlight rivals this design with the promise to deliver “Push” style designs within its framework using WCF’s Duplex bindings, but I have to ask myself is it really “Push” or just another hidden Polling mechanism that tries its best to mimic “Push”.”

The binding is nothing more than a Duplex BasicHttpBinding, that removes support for the WS-* specifications. It does however add support for the WS-MC (MakeConnections) standard. In WCF there are quite a few Duplex bindings, one in particular is a WSHttpDual binding that supports Duplex communication using the WS-*, Session, Security, Transaction mechanism. This binding cannot be used with Silverlight simply because Silverlight for WCF doesn’t support any WS-* channels. So theoretically you can build a WCF service using a Custom binding that contains a Http Channel, with a Text Encoder, a Duplex Channel, and a Custom Session Channel that removes the WS-ReliabileMessaging tags, and adds the ability to store the “pushed” messages from the server to an internal queue which a client uses to “pull/poll” and retrieve, as well as support for WS-MC, and you have it, the PollingDuplexHttpBinding.

The WS-MakeConnection standard is a specification for the transfer of messages between two endpoints when the sending endpoint is unable to initiate a new connection to the receiving endpoint, as in the case with the HTTP protocol when a client sends a requests to the server and unexpectedly or purposely disconnects before the server can send the response back. This specification defines a mechanism to create a new connection by uniquely identifying non-addressable endpoints, and a mechanism by which messages destined for those endpoints can be delivered. It also defines a SOAP binding that is required for interoperability. (http://docs.oasis-open.org/ws-rx/wsmc/200702/wsmc-1.1-spec-os.html)
The Server Polling Mechanism
The PollingDuplex Binding has a timer mechanism on the server that checks or polls to see if the client is still connected. By default the server waits 10 minutes and then closes the session, and connection. If a client application sends a WS-MC request within the 10 minute timeout, the session resets itself to another 10 minutes from the time it received the WS-MC. Each session is is stored within an in memory dictionary collection by its session ID. If the session is timed out or no activity has transpired, and no activity means, there are no ws-mc messages flowing to and from the client to the service, not the standard RPC operations that are called from the client to the service, then the session is removed from the list, the current channel is faulted and its connection is closed. This timeout setting can be controlled by the by setting the InactivityTimeout property on the PollingDuplexHttpBinding instance in code in the service.
When we take a look at the AJAX COMET style implementation, its design is somewhat different. It too polls for a timeout. It sends a message to the client to verify if the client is still connected, not requiring the client to send a request first, and it too has a model that can control the timeout a client should check for which helps controls how often a client needs to poll for data, thus there is a form of polling implemented here as well.

The PollingDuplexHttpBinding requires either an IDuplessSessionChannel or IReplyChannel in order to create and open the Channel Stack for sending and receiving messages, again showing you the Push style design. The IReplyChannel is basically what the server side polling mechanism uses to reply asynchronously to a client WS-MC request. The reply can be three types of messages: A Successful response to the WS-MC request, an HTTP error code message, or a custom message defined by the CallbackContract for the purpose of sending data down to the client. When the service needs to send a message to the client, the binding queues the message by session id, and simply dequeues the message when the client polling WS-MC request comes in.

On the server side, to build a service that supports sending data to and from a Silverlight WCF Duplex binding, you’ll need to use the Server side PollingDuplexHttpBinding located inside a particular assembly found at %Program Files%\Microsoft SDKs\Silverlight\v2.0\Librarries\Server\System.ServiceModel.PollingDuplex.dll. What makes this assembly so special is that the assembly is built referencing the Full .NET 3.5 Framework. In addition to this, the PollingDuplexSessionChannel in this assembly does not implement a ChannelFactory, only a ChannelListener. The ChannelListener will only support a IduplexSession shape, or the IreplyChannel shape. This effectively means that the WCF Service will only listen to receive messages with a duplex session, or send data as Responses to Requests from the WS-Make Connection call, or through a customized callback, as built from a CallbackContract.
This is where the standard definition of “PUSH” gets a little fuzzy. In this design, WCF for Silverlight requires that a client send a request as a WS-MC message internally, as a polling request, in order for it to receive the Message from the Service. However what makes this design still follow the authoritative definition is that a Silverlight Designed Duplex Service does not need every request originating from a receiver in order to respond. It can respond at will, by simply sending messages to the message queue per session id. This will work as long as the message originates inside an operation context so that the callback instance is still valid. The Message/Responses will simply be placed on a queue, until the next client polling interval, which at that time the Message is dequeued and sent to the client.

The Client Pushing Mechanism

The client also creates a PollingDuplexHttpBinding instance in code, however its design is slightly different. On the client side, it is the opposite. The PollingDuplexHttpBinding can be found at %Program Files%\Microsoft SDKs\Silverlight\v2.0\Librarries\Client\System.ServiceModel.PollingDuplex.dll. This assembly references all of the Silverlight specific assemblies and will only work in a Silverlight v2.0 project. The PollingDuplexSessionChannel class found in this assembly does not implement a ChannelListener. Instead it implements a ChannelFactory for sending requests, and a ChannelDispatcher that starts the polling mechanism controlled by the ClientPollTimeout property. This polling mechanism sends a WS-MC using the IRequestChannel to the Server.
If we revisit the IRequestChannel, you’ll recall that it returns a Message from the method signature. This message can either be a custom message that eventually gets unwrapped as a call into a custom class implementation of the CallbackContract, or simply a message for you to parse. In any case you have a choice.

This is where it’s important to understand the true meaning behind one-way versus two-way MEPs. In this two-way MEP for the IRequestChannel, the client will do a thread block if built using a synchronous design. In this case the implementation of this two-way MEP is an asynchronous design, which in Silverlight means we would invoke this on a different thread usually called the “worker” thread, than the “Main” UI thread. This also means that the blocking “worker” thread will wait for an indeterminate amount of time, or one that is specified again by the ClientPollTimeout property. How long the thread waits for the client poll request will also be affected by how long the server takes to respond to the poll request. The Server binding contains a ServerPollTimeout property that allows us to address this as well. By Default this is set to 15 seconds, on the server. This basically means that the if there are no Messages from the queue to return, at most the server has up until 15 seconds to return a response. If a response if found in the queue or a basic empty WS-MC message with an HTTP Status code message is successfully created and returned, the channel stays open otherwise a fault is thrown and the connection is terminated. This process occurs until a session timeout, or the connection to the open channel stack are explicitly closed through the Channel Factory on the client.

This may be a little too much to take in all at once, and in reality, you don’t really need to know all these things happen under the hood. You just really need to know how to build a Client that can support the Duplex binding, and how to build a WCF Service that knows how to communicate with a Silverlight Client.

Creating The Client Code
To Create a Polling channel you would write code such as this inside a Silverlight application:
PollingDuplexHttpBinding pushChannel = new PollingDuplexHttpBinding();
//Still follow disconnected designs here keep the connection polling time low…
pushChannel.InactivityTimeout = TimeSpan.FromMinutes(1);
IChannelFactory channelStack = pushChannel.BuildChannelFactory (new BindingParameterCollection());
channelStack.BeginOpen(new AsyncCallback(onChannelsOpen), channelStack);

One thing to note when working with WCF in Silverlight, is that it can be very daunting, simply because Silverlight forces the developer to follow UI coding best practices. Best practices such as always update a UI control from the Main controlling thread, as opposed to some worker thread. Silverlight forces this constraint internally as it checks whenever you try to do so. The reason I say this is because usually you invoke services on separate threads so as not to block your UI from performing. When you do this you’ll need to update your controls on the main thread only otherwise you’ll get an InvalidOperationException thrown from Silverlight.

There are many ways to handle this, one way is by using a SynchronizationContext. Another way is by using Callback and events, or a combination of both the context and the callbacks. Even so another method a colleague of mine pointed out is using the method shown here (http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx). You create a method to do whatever main thread ops you need, and call it from all threads. The method itself checks whether or not it needs to re-invoke itself from the UI thread to do the updates. Either way, a good amount of code will need to be written and a good understanding of the .NET Asynchronous Programming Model (APM) will be required in order to do this.

Using the SynchrnoizationContext is fairly straight forward. The first thing you would do is create a class level variable to hold the state of the current ThreadContext:
SynchronizationContext _currentThreadContext = null;
The next thing you would do is gather the current thread before making the service invocation call on a separate thread such as code like this:
_currentThreadContext = SynchronizationContext.Current;
The above line of code gives you a pointer to the main thread context, which allows you to make a callback into the main thread using a callback delegate for the purpose of updating a UI control. Here’s an example:
void onComplete(IasyncResult ar)
{
IduplexSessionChannel pushChannel = (IduplexSessionChannel)ar.AsyncState;
pushChannel.EndSend(ar);
_currentThreadContext.Post(UpdateUI, “Sending Complete”);
}

void UpdateUI (object msg)
{
silverLightTextBox.Text = (string)msg;
}


Creating the Service Code
The steps to build a WCF Service that supports Silverlight Duplex clients are straight forward. The first step is to create a Service Contract of your choosing that serves as the main Service Level Agreement you want to expose. Below is an example:
[ServiceContract(Namespace=”http://www.quicklearn.com/ChatService”)]
public interface IChatService
{
[OperationContract(Action=”RegisterClient”, IsOneWay=true)]
void RegisterClient (string clientName);

[OperationContract(Action=”SendMessage”, IsOneWay=true)]
void SendMessage(Message msg);

[OperationContract(Action=”UnRegisterClient”, IsOneWay=true)]
void UnRegisterClient(string clientID);
}

The next thing to do is to create a Client Callback interface for the purpose of sending data back to a client:
//No service contract here… only operation contract
public interface INotifyClient
{
[OperationContract(Action=”NotifyClient”, IsOneWay=true)]
void NotifyClient (Message msgToClient);
}

Once the Callback contract is created, add its type definition to the ServiceContract attribute as such:
[ServiceContract(Namespace=”http://www.quicklearn.com/ChatService”, CallbackContract=typeof(INotifyClient))]
public interface IChatService
{ … }

Next you implement the Service Contract in an Implementation class:
public class ChatService : IChatService
{

public static IDictionary clients = new Dictionary();
public static StringBuilder ChatWindow = new StringBuilder();

#region IChatService Members

public void RegisterClient(string clientName)
{
Guid g = Guid.NewGuid();
clients.Add(clientName, g);
//notify client that they have been registered
INotifyClient client = OperationContext.Current.GetCallbackChannel();
client.NotifyClient(Message.CreateMessage(MessageVersion.Default, "NotifyClient", string.Format("{0} has been registered with clientID: {1}", clientName, g.ToString() )));
}

public void SendMessage(System.ServiceModel.Channels.Message msgFromClient)
{
//Add to the Chat window buffer
string msg = msgFromClient.GetBody();
ChatWindow.Append(msg);
INotifyClient client = OperationContext.Current.GetCallbackChannel();
client.NotifyClient(Message.CreateMessage(MessageVersion.Default, "NotifyClient", ChatWindow.ToString()));

}

public void UnRegisterClient(string clientID)
{
clients.Remove(clientID);
INotifyClient client = OperationContext.Current.GetCallbackChannel();
client.NotifyClient(Message.CreateMessage(MessageVersion.Default, "NotifyClient", string.Format("{0} has been un registered", clientID )));

}

#endregion
}

To access the client callback, you use the OperationContext.Current.GetCallbackChannel() method call. This callback will only be available inside an operation context. This means that it is only available in the context of an instance of a Operation marked as a Operation Contract. Private methods, will not have access to this, neither will any other method. Trying to pass this item through private method calls will not work because the context will be switched and thus render the OperationContext null.

Once the implementation is complete, you’ll need a service host that knows how instruct the ServiceModel how to create an endpoint which contains the Service Level Agreement, and the PollingDuplexHttpBinding to work with Silverlight.

I’ll host this service inside of IIS/ASP.NET and I’ll use a configuration file that uses the PollingDuplexHttpBinding configuration. This configuration can tell the default WCF ServiceHostFactory to create a ServiceHost that reads the configuration for building a SLA and PollingDuplexHttpBinding. To do this create a new Web site based off the WCF Service Application template and change the “Service1.svc” file to use the ChatService class create earlier.

Also change the web.configuration file to use configure the “ChatService” along with changing the contract to say IChatService found on the endpoint.

The last thing we need to do is to change the binding from WsHttpBinding to PollingDuplexHttpBinding. The only issue here is that the developers didn’t create this binding as an Configuration Binding element, which means there is not registration for this binding to be used within a configuration file. So to use it inside a configuration file, I’ll just create a custom Binding class and wrap its functionality for the purpose of using it inside a configuration file. Then I’ll register it using the Advanced tab inside the WCF Configuration editor.

To create the wrapper classes, create a class that derives from the StandardBindingCollectionElement, and another class that derives from the StandardBindingElement abstract class. Override the InitializeFrom and onApplyConfiguration methods, and override the BindingElementType property. Below provides the sample code:
public class PollingDuplexHttpElementCollection : StandardBindingCollectionElement
{
public const string SECTIONNAME = "pollingDuplexHttpBinding";
public static PollingDuplexHttpElementCollection GetBindingCollectionElement()
{
PollingDuplexHttpElementCollection collElem = null;
BindingsSection sect = ConfigurationManager.GetSection("system.serviceModel/bindings") as BindingsSection ;
if (sect != null)
{
collElem = sect[SECTIONNAME] as PollingDuplexHttpElementCollection;
}
return collElem;
}
}


public class PollingDuplexHttpBindingElement: StandardBindingElement
{
protected override Type BindingElementType
{
get { return typeof(PollingDuplexHttpBinding); }
}

[ConfigurationProperty("inactivityTimeoutInMinutes")]
public int InactivityTimeoutInMinutes { get; set; }

[ConfigurationProperty("serverPollTimeoutInSeconds")]
public int ServerPollTimeOutInSeconds { get; set; }

protected override void OnApplyConfiguration(System.ServiceModel.Channels.Binding binding)
{
PollingDuplexHttpBinding poll = (PollingDuplexHttpBinding)binding;
poll.InactivityTimeout = new TimeSpan(0, 0, this.InactivityTimeoutInMinutes, 0);
poll.ServerPollTimeout = new TimeSpan(0, 0, this.ServerPollTimeOutInSeconds);

}

protected override void InitializeFrom(System.ServiceModel.Channels.Binding binding)
{
base.InitializeFrom(binding);
this.ServerPollTimeOutInSeconds = (int)((PollingDuplexHttpBinding)binding).ServerPollTimeout.TotalSeconds;
this.InactivityTimeoutInMinutes = (int)((PollingDuplexHttpBinding)binding).InactivityTimeout.TotalMinutes;

}
}


Once you’ve built this class register this class using the WCF Configuration editor by clicking the Advanced tab and add a new Binding Exension element as such:

This step now allows us to choose the Silverlight PollingDuplexHttpBinding and even configure it if we want to, as such:

At this point your WCF Service is complete. You can run this service and it is available to communicate with Silverlight Duplex clients.

Building the Silverlight Client application.
To build the Client application we can use two approaches. The first approach deals with using the Callback Contract INotifyClient to receive callbacks from the service. The Second approach uses a more loosely coupled design to generically use the IReceiveChannel of the IDuplexSessionChannel interface to receive callbacks. In my sample code, I’ve chosen to use the IDuplexSessionChannel approach.

The first step in creating a client is opening a channel to the PollingDuplexHttpBinding, channel stack. To create an open channel you use the ChannelFactory.Open() method. However when working with Silverlight everything must be done in an Asynchronous design, thus you’d use the BeginOpen()… EndOpen() APM pair, as shown in the sample code below.

private string clientName = null;
SynchronizationContext _currentThreadContext = null;
IDuplexSessionChannel duplexChannel = null;
PollingDuplexHttpBinding channelStack = new PollingDuplexHttpBinding();

IChannelFactory stackFactory = null;

void frmLogin_OnRegister(object sender, LoginPopup.RegisterEventArg e)
{
clientName = e.ClientName;
_currentThreadContext = SynchronizationContext.Current;


channelStack.InactivityTimeout = TimeSpan.FromMinutes(3);

stackFactory = channelStack.BuildChannelFactory
(new BindingParameterCollection());

IAsyncResult ar = stackFactory.BeginOpen(new AsyncCallback(onChannelFactoryOpenComplete), stackFactory);

if (ar.CompletedSynchronously)
{
OpenChannelFactory(ar);
}

TimerCallback tcb = new TimerCallback(CheckForNewMessage);
UiPoller = new Timer(tcb);


}


Once the factory is open, you have a pointer to the channels in the channel stack, which at this point you can now tell the factory to create and open the channels as shown in the code below. Again keep in mind that we must use the Asynchronous pattern here as well as shown in the sample code below.


private void OpenChannelFactory(IAsyncResult ar)
{
IChannelFactory stackFactory = (IChannelFactory)ar.AsyncState;
stackFactory.EndOpen(ar);

duplexChannel = stackFactory.CreateChannel(new EndpointAddress("http://localhost:60688/Service1.svc"));

IAsyncResult duplexAr = duplexChannel.BeginOpen(new AsyncCallback(onChannelOpenComplete), duplexChannel);
if (duplexAr.CompletedSynchronously)
{
OpenChannel(duplexAr, ActionType.Register );
}
}


Once the Channels are all created and open, we have to abilitity to send and receive data from the Transport channel that sits at the bottom of the Channel Stack. Remember how the IDuplexSessionChannel is nothing more than one IInputChannel, and one IOutputChannel. Well here all we have to do is call which one we’d like, either Receive() for receiving data, or Send() for sending data, again keeping in mind that we must follow the Asynchronous programming model. This is shown in the methods below:


private void OpenChannel(IAsyncResult ar, ActionType action)
{
duplexChannel = (IDuplexSessionChannel)ar.AsyncState;
duplexChannel.EndOpen(ar);

//Now we have an open Duplex channel to send to the Duplex service
//First register the client name.
switch (action)
{
case ActionType.Register:
RegisterClient();
break;
case ActionType.UnRegister:
UnRegisterClient();
break;
case ActionType.SendMessage:
SendMessage(null);
break;
default:
break;
}

}

private void RegisterClient()
{
String sw = "" + clientName + "";
MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(sw));

Message msgRegister = Message.CreateMessage(duplexChannel.GetProperty(), "RegisterClient", XmlDictionaryReader.CreateDictionaryReader(XmlReader.Create(ms)));
IAsyncResult registerAr = duplexChannel.BeginSend(msgRegister, new AsyncCallback(onSendComplete), duplexChannel);
if (registerAr.CompletedSynchronously)
{
SendComplete(registerAr);
}
PollForAnotherMessage(duplexChannel);
}

private void UnRegisterClient()
{

String sw = "" + clientName + "";
MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(sw));

Message msgRegister = Message.CreateMessage(duplexChannel.GetProperty(), "UnRegisterClient", XmlDictionaryReader.CreateDictionaryReader(XmlReader.Create(ms)));
IAsyncResult registerAr = duplexChannel.BeginSend(msgRegister, new AsyncCallback(onSendComplete), duplexChannel);
if (registerAr.CompletedSynchronously)
{
SendComplete(registerAr);
}
PollForAnotherMessage(duplexChannel);
}

private void SendMessage(object o)
{
if (o != null) duplexChannel = (IDuplexSessionChannel)o;

String sw = "" + txtSend.Text + "";
MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(sw));

Message msgRegister = Message.CreateMessage(duplexChannel.GetProperty(), "SendMessage", XmlDictionaryReader.CreateDictionaryReader(XmlReader.Create(ms)));
IAsyncResult registerAr = duplexChannel.BeginSend(msgRegister, new AsyncCallback(onSendComplete), duplexChannel);
if (registerAr.CompletedSynchronously)
{
SendComplete(registerAr);
}
PollForAnotherMessage(duplexChannel);
}


private void PollForAnotherMessage(IDuplexSessionChannel ch)
{
IAsyncResult ar = ch.BeginReceive(new AsyncCallback(onPollComplete), ch);
if (ar.CompletedSynchronously) PollComplete(ar);
}

private void PollComplete(IAsyncResult ar)
{
IDuplexSessionChannel ch = (IDuplexSessionChannel)ar.AsyncState;
Message msg = ch.EndReceive(ar);
if (msg != null)
_currentThreadContext.Post(NotifyFromServer, msg.GetBody());

}

private void NotifyFromServer(object msg)
{
string current = txtChatWindow.Text;
string newContent = msg.ToString().Replace(current, "");
txtChatWindow.Text += newContent;
}


private void SendComplete(IAsyncResult ar)
{
IDuplexSessionChannel ch = (IDuplexSessionChannel)ar.AsyncState;
ch.EndSend(ar);
_currentThreadContext.Post(RegisteredClient, "\n");
}

private void RegisteredClient(object msg)
{
txtChatWindow.Text += msg.ToString();
}

In the example code above, there are three methods that are sending data: RegisterClient, SendMessage, and UnRegisterClient all following the same pattern of the APM, and using the BeginSend(xxx) method to send data to the Service. There is only one method for receiving data: PollForAnotherMessage(xxx), which uses the BeginReceive(xxx) Asynchronous method to retrieve a message from the Duplex WCF Service.

Below is a quick view of what the client looks like, as well as a link pointing to an example application that shows exactly how to build a Push Style Notification using Silverlight and WCF Duplex bindings.




Happy Coding!!!
References:
http://ajaxian.com/archives/a-report-on-push-versus-pull
http://swerl.tudelft.nl/twiki/pub/Main/TechnicalReports/TUD-SERG-2007-016.pdf
(http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx)
http://petermcg.wordpress.com/2008/09/03/silverlight-polling-duplex-part-1-architecture/
http://alex.dojotoolkit.org/2006/03/comet-low-latency-data-for-the-browser/
http://blogs.msdn.com/silverlightws/archive/2008/06/10/detailed-overview-of-silverlight-2-beta-2-web-service-features.aspx
http://eugeneos.blogspot.com/2008/04/pushing-data-to-silverlight-application.html

2 comments:

Dwight Goins said...

Thanks Packey, I've updated the blog post a little to explain the WS-MakeConnection, as well as how it all fits together. I will upload code in a couple of days.

Oisin G. said...

Heh, Dwight - "Packey" is a spam bot.