Question

I finally managed to get compression working with NetTcpBinding in WCF in .net 4.0. But it seems quite nasty to me, so maybe someone else has a better idea.

Some general Information:

  • I know this is working with .net 4.5 out of the box - but we are stuck to 4.0
  • I found several examples with CustomBinding - but we want to stick with NetTcpBinding because of the binary enconding (it is simply faster than text enconding)
  • We are doing all the configuration in code (except server address), so for the customer it is just plug and play and no chance to change anything - but made it also sometimes difficult to get an example working (most are provided in config files)

My first approach was implementing a message dispatcher on client and server side which does the compression:

http://dotnetlombardia.org/b/tonyexpo/archive/2011/03/09/compressione-in-wcf.aspx

But whenever I changed (or replaced) the original message by any means, the AfterReceiveReply on client side was never executed. Though in WCF traces I could see that the client received the message and did even send an ACK to the server which the server received! But the client went in timeout?!

Then I found the MessageEncoderFactory for compression by Microsoft:

http://msdn.microsoft.com/en-us/library/ms751458%28v=vs.110%29.aspx

And applied the bug fix provided here:

http://blogs.msdn.com/b/dmetzgar/archive/2011/03/14/gzipmessageencoder-part-two.aspx

Finally I did inherit the NetTcpBinding and applied the new message encoder in the CreateBindingElements function:

    public class CompressedNetTcpBinding : NetTcpBinding
    {
        MyCompressionMessageEncodingBindingElement compressionEncoding;

        public CompressedNetTcpBinding()
            : base()
        {
            FieldInfo fi = typeof(NetTcpBinding).GetField("encoding", BindingFlags.Instance | BindingFlags.NonPublic);
            BinaryMessageEncodingBindingElement binaryEncoding = (BinaryMessageEncodingBindingElement)fi.GetValue(this);
            compressionEncoding = new MyCompressionMessageEncodingBindingElement(binaryEncoding, CompressionAlgorithm.Deflate);
        }

        /// <summary>
        /// Exchange <see cref="BinaryMessageEncodingBindingElement"/> and use compressed encoding binding element.
        /// </summary>
        /// <returns>binding elements with compressed message binding element</returns>
        public override BindingElementCollection CreateBindingElements()
        {
            BindingElementCollection bec = base.CreateBindingElements();
            BinaryMessageEncodingBindingElement enc = null;
            foreach (BindingElement be in bec)
            {
                if (be is BinaryMessageEncodingBindingElement)
                {
                    enc = (BinaryMessageEncodingBindingElement)be;
                    break;
                }
            }
            bec.Remove(enc);
            bec.Insert(2, compressionEncoding);

            return bec;
        }
    }

I did forward the ordinary BinaryMessageEncoder to the compression encoding, so when I change any settings of the NetTcpBinding, e.g. ReaderQuotas they are applied correctly.

I know the enconding member in NetTcpBinding is also used in the private function IsBindingElementsMatch but so far that did not cause any problems.

On my local machine, the (startup) performance penalty is insignificant (100ms - 250ms). But on LAN and WAN there is a significat performance increase (up to several seconds).

So what do you think:

  • Is that a (the) way to go?
  • Are there any better solutions?

No correct solution

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top