Serialize WCF message in a binary way, and not as a SOAP Message

I have a client-server application, which communicates using WCF, and uses NetDataContractSerializer to serialize the objects graph.

Since a lot of data is transferred between the server and the client, I tried to decrease its size by fine tuning the size of the data members (e.g. changed int to short, long to int, etc.).

After finishing the tuning, I found out, that the amount of transferred data hasn't changed! The problem is, that the NetDataContractSerializer serializes the objects graph to XML, so no matter what's the size of the data-member, the only thing that matters is the size of its value. For example, the value 10023 of a Int16 data member will be serialized as the string "10023" (0x3130303233), instead of just 10023 (0x2727).

I remember that in Remoting I could use the BinaryFormatter which serialized the values according to the type of the data member, but I don't know if it's possible to use it with WCF.

Does someone have a solution?

Answers


WCF uses SOAP messages, but what kind of message encoding is used, is totally up to you.

Basically, out of the box, you have two: text encoding (text representation of XML message) or binary encoding. You can write your own message encoding, if you really must and need to.

Out of the box, the basicHttp and wsHttp bindings use text encoding - but you can change that if you want to. The netTcp binding (which is the clear preferred choice behind corporate firewalls) will use binary by default.

You can also define (just in config) your own "binary http" protocol, if you wish:

   <bindings>
      <customBinding>
        <binding name="BinaryHttpBinding">
          <binaryMessageEncoding />
          <httpTransport />
        </binding>
      </customBinding>
    </bindings>

and then use it in your service and client side config:

   <services>
      <service name="YourService">
        <endpoint
          address="http://localhost:8888/YourService/"
          binding="customBinding"
          bindingConfiguration="BinaryHttpBinding"
          contract="IYourService"
          name="YourService" />
      </service>
    </services>

Now you have a http-based transport protocol, which will encode your message in compact binary, for you to use and enjoy!

No additional coding or messy hacks or lots of manual XML serialization code needed - just plug it together and use it! Ah, the joy of WCF flexibility!


First thought; have you enabled transport compression?

How complex is the data? If it is something that would work with the regular DataContractSerializer (i.e. a simple object tree), then protobuf-net may be of use. It is a very efficient binary serialization library with support for WCF via additional attributes on the service contract - for example:

[ServiceContract]
public interface IFoo
{
    [OperationContract, ProtoBehavior]
    Test3 Bar(Test1 value);
}

(the [ProtoBehaviour] is what swaps in the different serializer for this method)

However:

  • it needs to be able to identify a numeric tag for each property - either via extra attributes, or it can use the Order on a [DataMember(Order = x)] attribute
  • inheritance (if you are using it) requires extra attributes
  • it works best if you are using assembly sharing ("mex" doesn't love it...)

Nicely, it also works with MTOM, reducing the base-64 cost for larger messages.


The binary encoder will NOT serialize your object in binary, because it has nothing to do with serialization at all! It is something working at a lower layer and decides how message is transported between server and client.

In other words, the object will first be serialized (by DataContractSerializer, for example) and then encoded (by BinaryEncoder). So your object will always be in XML format as long as DataContractSerializer is involved.

If you want a more compact data and better performance, read this blog:

https://blogs.msdn.microsoft.com/dmetzgar/2011/03/29/protocol-buffers-and-wcf/


Here is an example of how to make custom encoding here https://www.codeproject.com/Articles/434665/WCF-Serialization-A-Case-Study

It's worth noting that what actually gets sent is the same as if you had a service method sending byte[] with default encoding. The message going over the wire still uses a SOAP XML envelope regardless of how you configure serialization.

It looks like this:

POST http://127.0.0.1:12345/forSwerGup182948/Client HTTP/1.1
Content-Type: text/xml; charset=utf-8
VsDebuggerCausalityData: uIDPo+WkoDpet/JOtGlW+EHdpDQAAAAAvFs5XOJ0tEW0wTvNVRDUIiabR6u+p+JNnd5Z+SWl1NcACQAA
SOAPAction: "http://tempuri.org/ITransmissionService/SendData"
Host: 127.0.0.1:12345
Expect: 100-continue
Accept-Encoding: gzip, deflate
Content-Length: 2890

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><SendData xmlns="http://tempuri.org/"><message>eyI8Q2FsbGJhY2tJZD5rX19CYWNraW5nRmllbGQiOiJlYTQ3ZWIzMS1iYjIzLTRkODItODljNS1hNTZmNjdiYmQ4MTQiLCI8RnJvbT5rX19CYWNraW5nRmllbGQiOnsiPENoYW5uZWxOYW1lPmtfX0JhY2tpbmdGaWVsZCI6Ikdyb3VwMSIsIjxOYW1lPmtfX0==</message></SendData></s:Body></s:Envelope>

Need Your Help

Where and why do I have to put the "template" and "typename" keywords?

c++ templates typename c++-faq dependent-name

In templates, where and why do I have to put typename and template on dependent names? What exactly are dependent names anyway? I have the following code:

How can I create a blank/hardcoded column in a sql query?

sql coldfusion

I want have a query with a column that is a hardcoded value not from a table, can this be done? I need it basically as a placeholder that I am going to come back to later and fill in.