Building WCF Channels and Bindings

In a prior article, http://www.codeguru.com/csharp/csharp/cs_network/internetweb/article.php/c13513/, I introduced you to Windows Communications Foundation. In the article, I gave an overview of what WCF is and how WCF fundamentally works. I shared how WCF classes are divided into the Service Model layer and the Channel Layer and I mentioned that Channels and Bindings are a good place to begin your study of WCF. In this article, you'll learn how to build Channels and Bindings. First, you'll look at the anatomy of a channel and then look at how you code a Channel using the WCF classes.

What Is a Channel?

If your Service was the human body, channels would be the sensory nerves of your Service. Sensory nerves carry messages from receptors in your body to your brain. Sensory messages pass through multiple nerves on the way to the brain.

Like sensory nerves, Channels create and transmit messages and pass the messages from one Channel to the next. Instead of interpreting impulses received from your sense receptors, Channels create messages from bytes received on the wire. As discussed in a previous article, the group performing the communication of Channels is often referred to as the Channel Stack.

There are two types of Channels: Transport Channels and protocol Channels. Transport Channels handle communications over the wire. Protocol Channels handle things like decryption and data transformation.

Messages emitted from the topmost Channel in the stack are carried by what WCF calls the Dispatcher to functions you've created and mapped to the Contract in your service. The whole arrangement of Channel Stack and Dispatcher looks a lot like the figure below.

A Transport Channel is at the bottom of the Channel Stack. Data (Messages) flow upward from the Transport Channel and through the set of Channels in the Channel Stack. The Dispatcher is really a collection of classes. A complete discussion of the Dispatcher is beyond the scope of this article, but you can read more about the role of the Dispatcher in the WCF documentation.

As you may have guessed, there is a little more to Channels and building a Channel Stack than this description would have you believe. There are many more working pieces living inside a Channel Stack making the Channel Stack do its magic.

Channel Stack Anatomy 101

A Channel stack is composed of Channels, ChannelManagers, and other classes doing specialized work such as translating raw bytes into Messages. This article will focus on the role of Channels and ChannelManagers and leave a discussion of the other classes for a future article.

ChannelManagers are responsible for building and maintaining Channels. ChannelManagers come in two flavors described below:

  • ChannelFactories build and manage Channels used by a Client communicating with a Service.
  • ChannelListeners build and manage Channels used by a Service.

This article focuses on the Service side of WCF, so you're going to focus on the role of the ChannelListener and Channel in the Channel Stack.

Bindings are the "B" component of an Endpoint. Again, refer to the introductory article for more information. Bindings are a collection of Binding elements. Binding Elements build ChannelListeners and, as stated earlier, ChannelListeners build and manage Channels. The figure below graphically depicts the relationships between the classes.

According to the Microsoft documentation, Channels are decoupled from one another and instead are mediated by the Channel Listener.

Before you look at how ChannelListeners and Channels work together to provide communications in your Service, there is an important idea to understand. A State Machine governs all WCF communication. States in the State Machine are what you would expect: Open, Abort, Close, and so forth. Classes participating in the communication State Machine support the ICommunicationObject interface. For a complete explanation of the WCF communication state machine, see the WCF documentation.

Now that I've introduced you to the composition of a Channel Stack, you're ready to look at how you code a Channel Stack.

The Sample

The sample code is confined to the basics of Channel Stack construction. As stated earlier, the goal of the article is to introduce you to Channel construction. So, the sample code implements a small subset of WCF's true Channel capabilities.

All Services, WCF or not, typically follow some well-established communication patterns. The WCF documentation refers to the communication pattern as the Message Exchange Patterns, but the term "Shape" is also used to describe the type of interface you use to implement the pattern. The three Message Exchange patterns are summarized below:

  • In the Datagram, or one-way pattern, messages flow from the client to the service.
  • In the Request-Response pattern, data flows from the client to the service and then a separate response message flows back to the client.
  • In the Datagram and the Request-Response patterns, there is a single initiator. In the Duplex pattern, either the Client or the Service can be an initiator and messages flow back and forth.

The sample implements a Datagram pattern because it's the most basic of the patterns and the simplest to implement. There are many ways to mix the role of ChannelListeners and Channels. My sample design is based on my interpretation of the documentation along with a review of the samples shipping in the SDK.

In the sample, there are three Channels in the stack. A transport channel creates the message and two other protocol channels attach header data to the message.

The State Machine governing WCF communication can invoke Channel functions asynchronously or synchronously. Every Message Exchange Patten contains each invocation style. For simplicity, only the Synchronous style has been implemented.

Also, the sample interacts directly with the Channel Stack. So, nowhere in the sample code do you declare a Contract. To run the samples, you'll need the latest SDK.

You're finally ready to look at some code. You'll start with the Channels and Channel Listeners, understand the binding code, and then invoke the Channel Stack without building other parts of a Service.

The Channels

As stated earlier, the Sample implements the Datagram Message Exchange Pattern. Therefore, the Channels implement the IInputChannel interface.

class TestTransportChannel : ChannelBase,IInputChannel
{
   protected EndpointAddress localAddress;

   public TestTransportChannel(TestTransportChannelListener parent,
                               EndpointAddress address)
      : base(parent)
      {
         ... ...

         this.localAddress = address;
      }

There are two key functions in the IInputChannel, the WaitForMessage and the Receive function. When a message is present, WaitForMessage returns true. Receive then retrieves the message. Below is the Receive message on one of the Protocal Channels.

public Message Receive(TimeSpan timeout)
{
   Message msg;
   MessageHeader mh;

   BigHelper.DisplayMessage("Receive " + this.ToString());

   msg = ((TestLevel2ChannelListener)this.Manager)
   .GetMessage(timeout);

   mh = MessageHeader.CreateHeader
   ("Level2", "http://Level2ns", "2");

   msg.Headers.Add(mh);

   return msg;
}

The Receive function returns a Message object. Message objects are passed throughout the layers of WCF. Refer to the prior article for more information on Message objects.

Now, take a look at the ChannelListener.

Building WCF Channels and Bindings

The Listeners

Earlier, you looked at the relationship between the Listener and the Channel. I invite you again to review that section of the article.

As you will see, ChannelListeners are interesting fellows. In your sample, there is a ChannelListener for each Channel in the program. I'll point out the difference between each ChannelLister where appropriate. For simplicity, you'll look at the protocol Channels ChannelListener.

First, the ChannelLister inherits from the ChannelListenerBase class. As you will see, Generics, a new feature in the .NET 2.0 framework, are used throughout the ChannelListener and other classes in WCF. If the syntax is new to you, here is a good whitepaper on the subject: http://msdn2.microsoft.com/en-us/library/ms379564(VS.80).aspx. Below is the ChannelListener class declaration.

class TestTransportChannelListener : ChannelListenerBase<IInputChannel>
   {
      private Uri _uri;
      private EndpointAddress _localAddress;

      public TestTransportChannelListener(TestTransportBindingElement
                                          transportElement,
                                          BindingContext context)
         : base(context.Binding)
      {
         BigHelper.DisplayMessage("Construct " + this.ToString());

         _uri = new Uri(context.ListenUriBaseAddress,
                        context.ListenUriRelativeAddress);

         _localAddress = transportElement.LocalAddress;
      }

As you can see, the first parameter in the constructor is the BindingElement responsible for building the ChannelListener. You'll see more about BindingElements later is this article. You may have also noticed that the ChannelListener stores the ChannelListener from the layer just below in the Channel Stack. You'll learn more about why this is important later in the article.

The ChannelListener is also controlled by the Communication State Machine.

As stated earlier, the ChannelListener is responsible for building Channels. Channel construction happens on the OnAcceptChannel function. Here an example of an OnAcceptChannel function implemented by one of the Protocal Channels.

protected override IInputChannel OnAcceptChannel(TimeSpan timeout)
{
   EndpointAddress address = new EndpointAddress(BigHelper.Uri);

   _innerChannel = _innerChannelListener.AcceptChannel(timeout);

   TestLevel2Channel channel = new TestLevel2Channel(this, address);

   return channel;
}

Below is a graphical depiction of the interactions when calling AcceptChannel.

[WCFbcFig03.JPG]

As I pointed out in the constructor, _innerChannelListener is the ChannelListener a level lower in the ChannelStack. The ChannelListener stores the Channel from the level below the current Listener and returns a Channel to the level above. The process of storing the Channel below and returning the Channel created by the Listener continues until, at the topmost level, the application gets a reference to the topmost Channel in the stack. A graphical depiction of this appears below. Because there is no level lower than the Transport Channel, the code in the Transport Channel simply creates a Message object.

[WCFbcFig04.JPG]

The GetProperty function allows other layers of WCF to peruse the Channel Stack. How WCF uses the GetProperty function is beyond the scope of this article.

This brings us to the Binding and the BindingElement.

Binding it all Together

Bindings are composed of BindingElements. As stated earlier, BindingElements build ChannelListeners. In the sample, each BindingElement is responsible for one particular ChannelListener. The most interesting function in the BindingElement is the BuildChannelListener.

public override IChannelListener<TChannel>
   BuildChannelListener<TChannel>(BindingContext context)
{
   if (context == null)
   {
      throw new ArgumentNullException("context");
   }
   if (!CanBuildChannelListener<TChannel>(context))
   {
      throw new ArgumentException(String.Format("Unsupported
         channel type: {0}.", typeof(TChannel).Name));
   }

   BigHelper.DisplayMessage("Build Listener " + this.ToString() +
      " of type " + typeof(TChannel).ToString() );

   return (IChannelListener<TChannel>)(object)new
      TestTransportChannelListener(this, context);
}

As you will see in the next section, WCF reads the Binding and builds the Channel Stack from the Collection of BindingElements.

Invoking the Channel Stack

The code below performs the following actions:

  1. Instantiates a new Binding.
  2. Gets the ChannelListener from the topmost part of the Channel Stack.
  3. Opens the topmost ChannelListener.
  4. Calls AcceptChannel, which in turn, calls OnAcceptChannel on the topmost ChannelListener and returns the topmost Channel.
  5. Opens the topmost Channel.
  6. Calls Receive on the topmost Channel and prints the message returned.
static void Main(string[] args)
{
   TestBinding bind = new TestBinding();
   IChannelListener<IInputChannel> listener;
   IInputChannel channel;
   Message msg;
   BindingParameterCollection parms = new BindingParameterCollection();

   BigHelper.DisplayComment("Tracing...");
   BigHelper.DisplayComment("");

   listener = bind.BuildChannelListener<IInputChannel>(parms);

   listener.Open();

   channel = listener.AcceptChannel();

   channel.Open();

   channel.WaitForMessage(TimeSpan.FromSeconds(3.0));

   msg = channel.Receive();

As you saw earlier, calling AcceptChannel and Receive on the topmost Listener and Channel cascades through the Channel Layers until the Transport layer is reached.

Exploring WCF Channels and Bindings

This article was meant to answer some of your WCF questions regarding Channels and Bindings and to raise more questions. WCF is an immense undertaking within Microsoft, consolidating much of the communication capabilities of the .NET Framework. Many future products will be built upon it. So, as you study WCF, pay attention to the landmarks explored in this article.



About the Author

Jeffrey Juday

Jeff is a software developer specializing in enterprise application integration solutions utilizing BizTalk, SharePoint, WCF, WF, and SQL Server. Jeff has been developing software with Microsoft tools for more than 15 years in a variety of industries including: military, manufacturing, financial services, management consulting, and computer security. Jeff is a Microsoft BizTalk MVP. Jeff spends his spare time with his wife Sherrill and daughter Alexandra.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • On-demand Event Event Date: October 23, 2014 Despite the current "virtualize everything" mentality, there are advantages to utilizing physical hardware for certain tasks. This is especially true for backups. In many cases, it is clearly in an organization's best interest to make use of physical, purpose-built backup appliances rather than relying on virtual backup software (VBA - Virtual Backup Appliances). Join us for this webcast to learn why physical appliances are preferable to virtual backup appliances, …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds