Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

CompactMessageEncoder

4.78/5 (13 votes)
4 Sep 2007Ms-PL5 min read 1   1.2K  
The CompactMessageEncoder compresses messages transparently on the communication channel. Hence, lowers the network usage.

Introduction

There are many articles about how WCF works, so I'll skip that part in this article. The purpose of this piece of code is to enable the user to compress the data transferred between machines using the WCF. (For simplicity, I am using the term WCF instead of referring to specific components.)

When the WCF is used to communicate between two entities, it creates a channel of communication to transfer and then transfers messages. The messages contain the data of requests and responses. WCF uses special encoders to translate the request and responses data into an array of bytes.

The CompactMessageEncoder hooks into the channel on both sides (Client & Server). When a message is transferred, the encoder compresses it on the sending side and decompress it on the receiving side, hence it is transparent.

Background

My drive to implement such an encoder was the need to transfer a big file (XML format) from one machine to another, where the connection has a speed limit.

I decided to do this with WCF for simplicity (After the transfer, there were more things to do). I didn't want to write a special function that compresses on one side and uncompresses on the other, but rather configure the channel itself to do this. This way the compression can be reused on any contract.

After picking and poking in the Internet, I found out that writing a message encoder is the simplest way. The real problem was how to write a message encoder, since the MSDN doesn't have any example (at least I didn't find any). Any example I found was out of date since they were written before the WCF was finally released. However these examples gave me some firm ground of how to write a message encoder.

My implementation is based on an example that I got installed on my machine, but I don't know its origin. I found it at C:\Program Files\Microsoft SDKs\WinFX\samples\Allsamples\Indigo\TechnologySamples\Extensibility\
Channels\MessageEncoder\Compression\CS.
The example is exactly what I wanted to have in the first place, unfortunately it doesn't work. It is written for previous versions of WCF and includes interfaces and classes that no longer exist. From this example, I got the knowledge of how to compress and uncompress the message. I also noticed that the implementation had two minor bugs, so I fixed them.

I had some help from Nicholas Allen's blog at http://blogs.msdn.com/drnick/archive/2006/05/09/592933.aspx. However, it doesn't contain code on how to add the message encoder to the configuration file.

I also used the Reflector to see how other message encoders work such as:

C#
System.ServiceModel.Channels.BinaryMessageEncoderFactory.BinaryMessageEncoder.

How It Works

The idea of the compact message encoder is to hook the sending and receiving of messages on the channel and compress them on the sender and then decompress them on the receiver. To implant the message encoder, a binding element is added into a customBinding.

Since the compression doesn't have to do the encoding itself, it uses another message encoder to do this for it, for example, the BinaryMessageEncoder.

Normal Service Method Execution

Sender side:

  1. A method is called in code.
  2. The method and its parameters are serialized into a SOAP message.
  3. The message encoder serializes the message into a bytes array.
  4. The bytes array are sent via the transport layer.

Receiver side:

  1. The transport layer receives a bytes array.
  2. The message encoder deserialized the bytes array into a message.
  3. The method and its parameters are deserialized into a SOAP message.
  4. The real method is called.

When the compact message encoder is added, the method call is changed a bit:

Sender side:

  1. A method is called in code.
  2. The method and its parameters are serialized into a SOAP message.
  3. The compact message encoder lets its inner message encoder to serialize the message into a bytes array.
  4. The compact message encoder compresses the bytes array into a second bytes array.
  5. The bytes array are sent via the transport layer.

Receiver side:

  1. The transport layer receives bytes array.
  2. The compact message encoder decompresses the bytes array into a second bytes array.
  3. The compact message encoder let its inner message encoder to deserialize the second bytes array into a message.
  4. The method and its parameters are deserialized into a SOAP message.
  5. The real method is called.

The compact message encoder is divided to several classes:

CompactMessageEncoder - This class provides the message encoder implementation.

CompactMessageEncoderFactory - This class is responsible to provide the Compact message encoder instance.

CompactMessageEncodingBindingElement - This is the class that participates in the binding stack of the channel.

CompactMessageEncodingElement - This is the class that enables the message encoder to be added via the application configuration file.

Compression

The CompactMessageEncoder uses the GZip compression which is implemented within the .NET Framework. This is implemented with System.IO.Compression.GZipStream.

How To Use

Add Reference to the CompactMessageEncoder.dll

Before changing the app.config, you must add a reference to the CompactMessageEncoder.dll. This must be done on both Client and Server applications.

Server config Change

This is an example of app.config before adding the compact message encoder:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <system.serviceModel> 
        <services> 
            <service name="Server.MyService">
                <endpoint 
                    address="net.tcp://localhost:1234/MyService" 
                    binding="netTcpBinding"
                    contract="Server.IMyService" /> 
            </service> 
        </services> 
    </system.serviceModel>
</configuration>

This is an example of the app.config after adding the compact message encoder:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <system.serviceModel> 
        <services> 
            <service name="Server.MyService">

            <!-- Set the binding of the endpoint to customBinding -->
            <endpoint 
                    address="net.tcp://localhost:1234/MyService" 
                    binding="customBinding"
                    contract="Server.IMyService" /> 
            </service> 
        </services> 

        <!-- Defines a new customBinding that contains the compactMessageEncoding -->
        <bindings> 
            <customBinding> 
                <binding name="compactBinding"> 
                    <compactMessageEncoding>

                <!-- Defines the inner message encoder as binary encoder -->
                <binaryMessageEncoding /> 
                    </compactMessageEncoding> 
                    <tcpTransport /> 
                </binding> 
            </customBinding> 
        </bindings> 

    <!-- Adds the extension DLL so the WCF can find the compactMessageEncoding -->
        <extensions> 
            <bindingElementExtensions> 
                <add name="compactMessageEncoding" 
		type="Amib.WCF.CompactMessageEncodingElement, 
		CompactMessageEncoder, Version=1.0.0.0, 
		Culture=neutral, PublicKeyToken=null" />
            </bindingElementExtensions> 
        </extensions> 

     </system.serviceModel>
</configuration>

Client config Change

This is an example of app.config before adding the compact message encoder:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <system.serviceModel> 
        <client> 
            <endpoint
                address="net.tcp://localhost:1234/MyService"
                binding="customBinding"
                bindingConfiguration="compactBinding"
                contract="Client.IMyService" />
        </client> 
    </system.serviceModel>
</configuration>

This is an example of the app.config after adding the compact message encoder:
<?xml version="1.0" encoding="utf-8" ?>
<configuration> 
    <system.serviceModel> 
        <client> 
            <endpoint
                address="net.tcp://localhost:1234/MyService"
                binding="customBinding"
                bindingConfiguration="compactBinding"
                contract="Client.IMyService" />
        </client> 

        <!-- Defines a new customBinding that contains the compactMessageEncoding --> 
        <bindings> 
            <customBinding> 
                <binding name="compactBinding">
                    <compactMessageEncoding> 
                        <binaryMessageEncoding/> 
                    </compactMessageEncoding>
                    <tcpTransport /> 
                </binding> 
            </customBinding> 
        </bindings>
        
    <!-- Adds the extension DLL so the WCF can find the compactMessageEncoding -->
        <extensions> 
            <bindingElementExtensions> 
                <add name="compactMessageEncoding" 
		type="Amib.WCF.CompactMessageEncodingElement, 
		CompactMessageEncoder, Version=1.0.0.0, 
		Culture=neutral, PublicKeyToken=null" /> 
            </bindingElementExtensions> 
        </extensions>

    </system.serviceModel>
</configuration>

Limitations & Workarounds

  1. All messages are compressed on the channel even if it will cause the message to inflate. This happens when the message is small.
  2. The CompactMessageEncoder supports only Buffered transport and not Strearmed transport.
  3. Using the CompactMessageEncoder on client and server that run on the same machine may downgrade performance.
  4. The WCF configuration editor works partially with the CompactMessageEncoder, therefore part of the configuration must be done manually with the XML text editor.
  5. Each time the WCF configuration editor opens the app.config file, it asks about the safety of the CompactMessageEncoder.dll. I don't know how to get rid of this behavior.
  6. The configuration of the binaryMessageEncoding and the textMessageEncoding cannot be edited within the WCF configuration editor. In order to overcome this, remove the compactMessageEncoding element from the app.config (leaving its children), and then open it with the WCF configuration editor. Afterwards, add the compactMessageEncoding element back.

Disclaimer

THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

History

  • 5th September, 2007: Initial version

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)