Building A Custom Message Encoder to Record Throughput, Part 3


After a short break we’re back to working on the custom message encoder. The complete source code for the encoder is available in Part 1 and Part 2 of this series. Today and tomorrow I’ll be performing some runs using the encoder to show how it works. We’ll need a sample client and server application to host the encoder. I’ve dusted off the code I put together for the FileTransport to fill this role.

using System;
using System.ServiceModel.Channels;
using CountingEncoder;
using FileTransport;

namespace Server
{
   class Server
   {
      static void Main(string[] args)
      {
         Console.Write("Creating listener...");
         CountingEncoderBindingElement encoder = new CountingEncoderBindingElement(new TextMessageEncodingBindingElement());
         FileTransportBindingElement transport = new FileTransportBindingElement();
         transport.Streamed = true;
         CustomBinding binding = new CustomBinding(encoder, transport);
         Uri uri = new Uri("my.file://localhost/x");
         IChannelListener<IReplyChannel> listener = binding.BuildChannelListener<IReplyChannel>(uri, new BindingParameterCollection());
         listener.Open(TimeSpan.FromSeconds(5));
         Console.WriteLine(" done.");
         Console.Write("Creating channel...");
         IReplyChannel channel = listener.AcceptChannel(TimeSpan.FromSeconds(5));
         channel.Open(TimeSpan.FromSeconds(5));
         Console.WriteLine(" done.");
         Console.Write("Waiting for request...");
         while (channel.WaitForRequest(TimeSpan.FromMinutes(1)))
         {
            using (IRequestContext context = channel.ReceiveRequest(TimeSpan.FromSeconds(5)))
            {
               Console.WriteLine(" done.");
               using (Message message = context.RequestMessage)
               {
                  Console.WriteLine("Processing request: {0}", message.Headers.Action);
                  if (message.Headers.Action == "http://reflect")
                  {
                     string response = ProcessReflectRequest(message.GetBody<string>());
                     Console.Write("Sending reply...");
                     Message replyMessage = Message.CreateMessage(MessageVersion.Default, "http://reflection", response);
                     context.Reply(replyMessage, TimeSpan.FromSeconds(5));
                     Console.WriteLine(" done.");
                  }
               }
            }
            Console.Out.WriteLine("Read {0} bytes in {1} operations.", encoder.ReadBytes, encoder.ReadCount);
            Console.Out.WriteLine("Wrote {0} bytes in {1} operations.", encoder.WriteBytes, encoder.WriteCount);
            Console.Write("Waiting for request...");
         }
         Console.WriteLine(" terminated.");
         channel.Close(TimeSpan.FromSeconds(5));
      }

      static string ProcessReflectRequest(string request)
      {
         char[] output = new char[request.Length];
         for (int index = 0; index < request.Length; index++)
         {
            output[index] = request[request.Length - index - 1];
         }
         return new string(output);
      }
   }
}
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using CountingEncoder;
using FileTransport;

namespace Client
{
   class Client
   {
      static void Main(string[] args)
      {
         Console.Write("Creating factory...");
         CountingEncoderBindingElement encoder = new CountingEncoderBindingElement(new TextMessageEncodingBindingElement());
         FileTransportBindingElement transport = new FileTransportBindingElement();
         transport.Streamed = true;
         CustomBinding binding = new CustomBinding(encoder, transport);
         IChannelFactory<IRequestChannel> factory = binding.BuildChannelFactory<IRequestChannel>();
         factory.Open(TimeSpan.FromSeconds(5));
         Console.WriteLine(" done.");
         Console.Write("Creating channel...");
         using (factory)
         {
            Uri uri = new Uri("my.file://localhost/x");
            IRequestChannel channel = factory.CreateChannel(new EndpointAddress(uri));
            Console.WriteLine(" done.");
            Console.Write("Enter some text: ");
            String text = Console.ReadLine();
            if (text == null)
            {
               return;
            }
            Console.Write("Sending request...");
            Message requestMessage = Message.CreateMessage(MessageVersion.Default, "http://reflect", text);
            channel.Open(TimeSpan.FromSeconds(5));
            Message replyMessage = channel.Request(requestMessage, TimeSpan.FromSeconds(5));
            Console.WriteLine(" done.");
            using (replyMessage)
            {
               Console.WriteLine("Processing reply: {0}", replyMessage.Headers.Action);
               Console.WriteLine("Reply: {0}", replyMessage.GetBody<string>());
            }
            channel.Close(TimeSpan.FromSeconds(5));
         }
         Console.Out.WriteLine("Read {0} bytes in {1} operations.", encoder.ReadBytes, encoder.ReadCount);
         Console.Out.WriteLine("Wrote {0} bytes in {1} operations.", encoder.WriteBytes, encoder.WriteCount);
      }
   }
}

In the code here, I’ve created a binding with the file transport and text message encoder. I’ve set the transfer mode to use streaming for this example. The counting encoder wraps around the text encoder to inspect the bytes as they go to the transport. After completing an operation, the client and server both print the number of bytes they sent and received as well as the number of individual calls required.

Here’s what I get when running this on the server:

Creating listener... done.
Creating channel... done.
Waiting for request... done.
Processing request: http://reflect
Sending reply... done.
Read 352 bytes in 8 operations.
Wrote 355 bytes in 1 operations.
Waiting for request...

And, here’s what I get when running this on the client:

Creating factory... done.
Creating channel... done.
Enter some text: abcd
Sending request... done.
Processing reply: http://reflection
Reply: dcba
Read 355 bytes in 8 operations.
Wrote 352 bytes in 1 operations.

Notice that a single send operation has eight read operations on the other side of the connection. The way that the data is framed and written by the send side of the connection does not put any requirements on how the receive side must consume the data. Some transports have this kind of requirement at the network level, but you generally cannot see any impact in your application.

Next time: Building A Custom Message Encoder to Record Throughput, Part 4

Comments (8)

  1. Authentication is the process of identifying whether a client is eligible to access a resource. The HTTP…

  2. I am presenting a custom encoder for FastInfoset at JavaOne this week. It implements the MessageEncoder API and can be used (currently client side only) to exchange XML messages (SOAP or REST/POX) in FI.

  3. Nicholas Allen says:

    Hi Gerald,

    That’s certainly a cool project.  Clemens Vasters pointed it out to a few people on the team last month but I haven’t had a chance to take a closer look at what you guys are doing.  I’d love to see how FI compares performance-wise to .NET XML Binary and MTOM.  Tomorrow’s entry actually looks at a number of encoding+transport combinations in terms of message size.  I may do a followup when I get the chance to talk about performance tests with real messages.

  4. One of the advantages of using WCF is that you can change the network protocol without changing how your…

  5. Hi Nicholas –

    Right now I am not quite ready for performance comparison, since I am having quite a few issues with the deserialization of complex types. As soon as I have that problem solved (BTW: do you have any good pointers on how this is being done within WCF?), I intend to look at the performance. Mind however, that at this point, I am mostly looking at client-side Indigo (and server-side Java), so server performance is not a high priority … Maybe you guys could think about that 🙂 🙂

    Best,

    Gerry

  6. Authentication is the process of identifying whether a client is eligible to access a resource. The HTTP

  7. Недавно мне пришлось решать обозначенную задачу. Честно говоря, вопрос о сжатии