Building a Custom File Transport, Part 11: Putting it Together

Today's article is just a summary of what we've put together with the file transport and a demonstration of how it works. There's no new code left to show, although I'll go over a number of improvements we could make to the transport in the future. Here are the previous articles in this series:

  1. Building a Custom File Transport, Part 1: Planning
  2. Building a Custom File Transport, Part 2: Server
  3. Building a Custom File Transport, Part 3: Client
  4. Building a Custom File Transport, Part 4: Binding and Binding Element
  5. Building a Custom File Transport, Part 5: Channel Basics
  6. Building a Custom File Transport, Part 6: Channel Factory
  7. Building a Custom File Transport, Part 7: Request Channel
  8. Building a Custom File Transport, Part 8: Channel Listener
  9. Building a Custom File Transport, Part 9: Reply Channel
  10. Building a Custom File Transport, Part 10: Request Context

Running the server creates a listener at my.file://localhost/x, which for the machine I'm running this on is going to resolve to c:\x. The listener creates a file system watcher to look for a request message at c:\x\request. We're using the TextMessageEncoder, so the format of the request message is expected to be a text-encoded message using SOAP.

Meanwhile, I've started up the client and given it an input string to send as the message body.

 Creating factory... done.
Creating channel... done.
Opening channel... done.
Enter some text (Ctrl-Z to quit): asdf
Sending request... done.
Processing reply: reflection
Reply: fdsa
Enter some text (Ctrl-Z to quit): ^Z

What this is going to look like on the "wire" is first a request message with the reflect action written to c:\x\request:

 <s:Envelope xmlns:s=www.w3.org/2003/05/soap-envelope xmlns:a="www.w3.org/2005/08/addressing">
 <s:Header>
  <a:Action s:mustUnderstand="1">reflect</a:Action> 
  <a:To s:mustUnderstand="1">my.file://localhost/x</a:To> 
 </s:Header>
 <s:Body>
  <string xmlns="schemas.microsoft.com/2003/10/Serialization/">asdf</string> 
 </s:Body>
</s:Envelope>

Then, the server writes back a reply message with the reflection action to c:\x\reply:

 <s:Envelope xmlns:s="www.w3.org/2003/05/soap-envelope" xmlns:a="www.w3.org/2005/08/addressing">
 <s:Header>
  <a:Action s:mustUnderstand="1">reflection</a:Action> 
  <a:To s:mustUnderstand="1">my.file://localhost/x</a:To> 
 </s:Header>
 <s:Body>
  <string xmlns="schemas.microsoft.com/2003/10/Serialization/">fdsa</string> 
 </s:Body>
</s:Envelope>

This transport is fully operational, but it's taken a number of shortcuts to get to that point in the smallest amount of code. Here's what has been left behind:

  • Exposing the configuration of the binding and binding element. This requires writing several classes, but the process is so mechanical that you'd probably want to use an automated tool to do so. I'll likely do this in the future as an example.
  • Monitoring of operation times to provide timeouts. There are several places in the code where timeouts are not effectively enforced. There is some threading subtlety to doing this efficiently, and thinking is required on your part to integrate with the timeout mechanisms of library calls.
  • Supporting concurrent readers and writers safely. A better implementation of this file transport would be more exclusive about when reads and writes could occur and would support multiple readers and writers at the same URI. There are also some problems peculiar to writing to a network file share, but you may just want to disallow that in favor of using a network transport directly.
  • Taking advantage of asynchronous and streaming support for file reads and writes. Most of these methods were left unimplemented to cut down the size of the example. Since we're building on top of libraries that support asynchronous operations, a lot of this will simply be writing a passthrough to those methods. However, we still have to write classes where we can stash the state of the asynchronous request and we have to take outstanding requests down cleanly when something goes wrong or we're shutting down.

I hope this series has helped explain the process of writing a transport. It's been tough having a long series like this. I hope in the future we can make the process of building a transport easier. There will be other transport examples in the SDK to look at when the product is released.

The next series is going to be a much shorter one and covers writing a custom message encoder.

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