The client is actually done at this point. I don't know how many people have actually tried running the code or if you're all just following along. If you did try running the client, you would see a file appear on your hard drive, probably at c:\x\request. Inside that file would be a SOAP message representing the client request. Fabulous. Now we just need to write the rest of the server so that something actually happens in response to that message.

The server has three classes left to write. We'll need the channel listener, which corresponds to the channel factory on the client side. We'll need the reply channel, which corresponds to the request channel on the client side. Finally, we'll need the request context. That piece is entirely unique to the server and is what correlates request messages with replies.

First up is the channel listener. This class is remarkably similar to the channel factory except for the methods that allow you to asynchronously receive channels back from the listener. Since the example doesn't support asynchronous operations, that means the listener and factory are pretty much identical in terms of code.

using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace FileTransport
{
   class FileReplyChannelListener : ChannelListenerBase<IReplyChannel>
   {
      readonly BufferManager bufferManager;
      readonly MessageEncoderFactory encoderFactory;
      public readonly long MaxReceivedMessageSize;
      readonly string scheme;
      public readonly bool Streamed;
      readonly Uri uri;

      public FileReplyChannelListener(FileTransportBindingElement transportElement, BindingContext context)
         : base(context.Binding)
      {
         MessageEncodingBindingElement messageEncodingElement = context.UnhandledBindingElements.Remove<MessageEncodingBindingElement>();
         this.bufferManager = BufferManager.CreateBufferManager(transportElement.MaxBufferPoolSize, int.MaxValue);
         this.encoderFactory = messageEncodingElement.CreateMessageEncoderFactory();
         MaxReceivedMessageSize = transportElement.MaxReceivedMessageSize;
         this.scheme = transportElement.Scheme;
         Streamed = transportElement.Streamed;
         this.uri = new Uri(context.ListenUriBaseAddress, context.ListenUriRelativeAddress);
      }

      protected override void OnOpen(TimeSpan timeout)
      {
         base.OnOpen(timeout);
         Directory.CreateDirectory(Uri.AbsolutePath);
      }

      protected override IReplyChannel OnAcceptChannel(TimeSpan timeout)
      {
         EndpointAddress address = new EndpointAddress(Uri);
         return new FileReplyChannel(this.bufferManager, this.encoderFactory, address, this);
      }

      protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
      {
         throw new Exception("The method or operation is not implemented.");
      }

      protected override IReplyChannel OnEndAcceptChannel(IAsyncResult result)
      {
         throw new Exception("The method or operation is not implemented.");
      }

      protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
      {
         throw new Exception("The method or operation is not implemented.");
      }

      protected override bool OnEndWaitForChannel(IAsyncResult result)
      {
         throw new Exception("The method or operation is not implemented.");
      }

      protected override bool OnWaitForChannel(TimeSpan timeout)
      {
         throw new Exception("The method or operation is not implemented.");
      }

      public override Uri Uri
      {
         get { return this.uri; }
      }

      public override MessageVersion MessageVersion
      {
         get { return MessageVersion.Default; }
      }

      public override string Scheme
      {
         get { return this.scheme; }
      }
   }
}

Next time: Building a Custom File Transport, Part 9: Reply Channel