Building a Custom Message Encoder to Record Throughput, Part 4

One of the advantages of using WCF is that you can change the network protocol without changing how your application works. Let's show that off a bit while also looking at what the counting message encoder stats look like for some common scenarios.

Last time, we looked at the file transport using a streamed transfer. Here's the same transport with the transfer mode changed to use buffered messages.

 CountingEncoderBindingElement encoder = new CountingEncoderBindingElement(new TextMessageEncodingBindingElement());
FileTransportBindingElement transport = new FileTransportBindingElement();
transport.Streamed = false;
Uri uri = new Uri("my.file://localhost/x");
 Creating factory... done.
Creating channel... done.
Enter some text: abcd
Sending request... done.
Processing reply: reflection
Reply: dcba
Read 355 bytes in 1 operations.
Wrote 352 bytes in 1 operations.

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

You'll notice that the number of bytes read and written didn't change, but everything completes in a single operation. That's because buffered messaging hands off the entire message at one time. When might you see buffered messaging take more than one operation to complete a transfer? If you're using chunking or reliable messaging, a single top-level call can result in multiple underlying operations.

Now, let's flip over to using TCP/IP and change the encoding from text to binary-encoded XML.

 CountingEncoderBindingElement encoder = new CountingEncoderBindingElement(new BinaryMessageEncodingBindingElement());
TcpTransportBindingElement transport = new TcpTransportBindingElement();
transport.TransferMode = TransferMode.Streamed;
Uri uri = new Uri("net.tcp://localhost:5555/");
 Creating factory... done.
Creating channel... done.
Enter some text: abcd
Sending request... done.
Processing reply: reflection
Reply: dcba
Read 108 bytes in 35 operations.
Wrote 137 bytes in 1 operations.

Creating listener... done.
Creating channel... done.
Waiting for request... done.
Processing request: reflect
Sending reply... done.
Read 137 bytes in 43 operations.
Wrote 108 bytes in 1 operations.
Waiting for request...

The byte count went way down while the operation count went way up. That's because the binary encoder does a lot of single byte reads before deciding how to interpret the upcoming data.

Let's jump from TCP/IP to named pipes while keeping the same encoding.

 CountingEncoderBindingElement encoder = new CountingEncoderBindingElement(new BinaryMessageEncodingBindingElement());
NamedPipeTransportBindingElement transport = new NamedPipeTransportBindingElement();
transport.TransferMode = TransferMode.Streamed;
Uri uri = new Uri("net.pipe://localhost/x");
 Creating factory... done.
Creating channel... done.
Enter some text: abcd
Sending request... done.
Processing reply: reflection
Reply: dcba
Read 108 bytes in 35 operations.
Wrote 134 bytes in 1 operations.

Creating listener... done.
Creating channel... done.
Waiting for request... done.
Processing request: reflect
Sending reply... done.
Read 134 bytes in 43 operations.
Wrote 108 bytes in 1 operations.
Waiting for request...

Virtually the same results, although the total byte count differs slightly. I'll let you figure out why that is. You can find out by switching to the text encoding and comparing the SOAP for the two transports.

Speaking of text, now we'll switch back to that encoding but over HTTP. I've got IIS running on port 80 already so I need to specify a different port for running my service.

 CountingEncoderBindingElement encoder = new CountingEncoderBindingElement(new TextMessageEncodingBindingElement());
HttpTransportBindingElement transport = new HttpTransportBindingElement();
transport.TransferMode = TransferMode.Streamed;
Uri uri = new Uri("localhost:5555/x");
 Creating factory... done.
Creating channel... done.
Enter some text: abcd
Sending request... done.
Processing reply: reflection
Reply: dcba
Read 300 bytes in 7 operations.
Wrote 354 bytes in 1 operations.

Creating listener... done.
Creating channel... done.
Waiting for request... done.
Processing request: reflect
Sending reply... done.
Read 354 bytes in 8 operations.
Wrote 300 bytes in 1 operations.
Waiting for request...

We haven't used the MTOM encoder yet, so let's keep on with HTTP but switch the encoder again.

 CountingEncoderBindingElement encoder = new CountingEncoderBindingElement(new MtomMessageEncodingBindingElement());
HttpTransportBindingElement transport = new HttpTransportBindingElement();
transport.TransferMode = TransferMode.Streamed;
Uri uri = new Uri("localhost:5555/x");
 Creating factory... done.
Creating channel... done.
Enter some text: abcd
Sending request... done.
Processing reply: reflection
Reply: dcba
Read 779 bytes in 2 operations.
Wrote 833 bytes in 3 operations.

Creating listener... done.
Creating channel... done.
Waiting for request... done.
Processing request: reflect
Sending reply... done.
Read 833 bytes in 1 operations.
Wrote 779 bytes in 3 operations.
Waiting for request...

Wow, the byte count really spiked when we changed the encoding. The operation count dipped to the lowest point we've seen for streamed transfers though. Which of the two is more important? BYTES. Even though network operations are typically expensive, we can create a readahead buffer for the stream so that all of the small reads are satisfied without hitting the network.

Next time: Resources for Channel Authors