Sending a simple String using WCF - Understanding the channel stack.

The snippet below might not be for the faint hearted. Primarily since its quite gory wcf. I realized that there is so much abstraction in the Service model's channel layers thats its only justice that someone does a simple sample in how to send and read raw messages. I would suggest you to understand the ICommunicationObject by Nicholas before this.

I would not be diving into security and behaviors for this post but just wanted to touch upon using a simple IRequest and IReply channel. Now for those of you not familiar with MEP this would be a good time to figure out what are the different kinds of MEP (message exchange patterns) that WCF support. I would like to point you to https://blogs.msdn.com/drnick/archive/2007/03/08/channel-shapes.aspx where Nicholas has given a nice diagram for this.

In this post i use the HTTP model which is enabled through the Request reply pattern. The service in short is an Http listener and uses, as the name would suggest a, ChannelListener and the client which is capable of sending a message would use the channel factory. Both of these are created from the Binding.

If you program in WCF you come across a lot of references to the channel stack. One of the key take aways of this pattern is that the underlying channel has some duties delegated to it to be performed by the above layer and there are some fundament rules that have to be obeyed by every channel in the channel stack

You can take a look below on how raw strings can be sent across. The intent of this sample is to show how the binding is used to create the channel Listeners and the channel factory. 

1. The Channel Listener - The channel listener used here uses the Http listener and to start the listening service we use the IReplyChannel . We can invoke RecieveRequest which would give us the calling transport channel. This can be used to respond to the calling client. We can send a reply back to the caller using this RequestContext.

  1. Use the Binding to build the channel listener.
  2. Open the listener.
  3. Get the transport channel using AcceptChannel();
  4. Open the transport.

2. Channel Factory - To send a message across to the listener we use the binding to create the channel factory.

  1. Build the channel factory usign the Binding.

  2. Buid the channel to send using the channel factory

  3. send the message to the server using the Request on the IRequestChannel. As you would notice the request method also give your an output message which is what the server responds with.

    Message response = channel.Request(msg);

 

You can check out the full sample here.

using System;

using System.Collections.Generic;

using System.Text;

using System.ServiceModel;

using System.ServiceModel.Channels;

using System.Threading;

namespace WcfRawMessages

{

    class Program

    {

        static void Main(string[] args)

        {

            //Start the listener

            ServiceHelpers.StartListener();

            //Invoke with test messages.

            for (int i = 0; i < 10; i++)

            {

                TestClientHelper.SendTestMessage();

            }

            Console.ReadLine();

            ServiceHelpers.Stop();

        }

        public class BindingHelpers

        {

            public static Uri Address = new Uri("https://localhost:8089");

            public static Binding GetBinding()

            {

                CustomBinding binding = new CustomBinding();

                //add the encoder and the binding

                MessageEncodingBindingElement mbe = new TextMessageEncodingBindingElement();

                mbe.MessageVersion = MessageVersion.None;

                binding.Elements.Add(mbe);

                binding.Elements.Add(new HttpTransportBindingElement());

                return binding;

            }

        }

        public class ServiceHelpers

        {

            static IReplyChannel transportChannel;

            public static void StartListener()

            {

                Binding binding = BindingHelpers.GetBinding();

                IChannelListener<IReplyChannel> listener = binding.BuildChannelListener<IReplyChannel>(BindingHelpers.Address);

                listener.Open();

                transportChannel = listener.AcceptChannel();

                transportChannel.Open();

                Thread t = new Thread(delegate(object state)

              {

                  //Gory while loop to recieve messages from the transport

                  while (true)

                  {

                      try

                      {

                          //Use the request context to recieve a message

                        RequestContext request = transportChannel.ReceiveRequest();

                          if (request != null)

                          {

                              Message msg = request.RequestMessage;

                              Console.WriteLine(msg.GetBody<string>());

                              //Send the response…

                              Message replymessage = Message.CreateMessage(msg.Version, "https://responseAction/");

                              request.Reply(replymessage);

         request.Close();

                              msg.Close();

                              replymessage.Close();

                          }

                      }

                      catch (CommunicationException ex)

                      {

                          Console.WriteLine("Communication Exception " + ex.Message);

                      }

                      if (transportChannel.State != CommunicationState.Opened)

                          break;

                  }

              });

                t.Start();

            }

            internal static void Stop()

            {

                transportChannel.Close();

                //transportChannel.Abort();

            }

        }

        public class TestClientHelper

        {

            static IChannelFactory<IRequestChannel> factory;

            static int counter = 0;

            public static void SendTestMessage()

            {

                Binding binding = BindingHelpers.GetBinding();

                //Create the channel factor.

                if (factory == null)

                {

                    factory = binding.BuildChannelFactory<IRequestChannel>(BindingHelpers.Address);

                    factory.Open();

                }

                //Create the channel.

                IRequestChannel channel = factory.CreateChannel(new EndpointAddress(BindingHelpers.Address));

                channel.Open();

                Message msg = Message.CreateMessage(binding.MessageVersion, "", "Body Text" + counter++);

                Message response = channel.Request(msg);

                channel.Close();

                msg.Close();

                response.Close();

            }

        }

    }

}