Share via


Building a Custom File Transport, Part 8: Channel Listener

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

Comments

  • Anonymous
    May 02, 2006
    I've been providing the contents for this article in bits and pieces but now the whole thing is assembled...
  • Anonymous
    May 09, 2006
    Today's article is just a summary of what we've put together with the file transport and a demonstration...
  • Anonymous
    July 09, 2006
    Hi Allen,

    I’m implementing the custom transport channel, which has to utilize IDuplexSessionChannel shape. I also read lot of your posts and probably, you should the right guy who can answer my questions.

    Imagine, there is a requirement to change your FileReplyChannelListener : ChannelListenerBase<IReplyChannel> to utilize the duplex-pattern as follows:

    public class MyTransportListener : ChannelListenerBase<IDuplexSessionChannel>

    I tried to do that and run into many, many questions.
    First of all I am looking for proposed pattern for implementing of any of methods

    BeginSomething(TimeSpan, AsyncCallback, object) and
    ISomething EndSomething(IAsyncResult)

    For example OnBeginAcceptChannel, OnEndAcceptChannel, BeginTryRaceive and EndTryReceive.

    How often the BeginAcceptChannel will be called?

    How often the BeginTryReceive will be called?

    I assume that dispatching of received messages on the higher level has to be done EndTryReceive in. If so, what is the way to implement continuous receiving? One ofter one message is dispatched by EndTryReceive the BeginTryRecive has to be called again?
  • Anonymous
    July 10, 2006
    Hi Allen,

    I tried your FileChannelTransport example and found out that instead of method OnAccptChannel the method OnBeginAcceptChannel is invoked.
    Who and when decides which of "accept"-methods will be invoked?
    Is that dependent on contract?
    If so, here is the contract I used in your example:

    [ServiceContract]
    public interface ITestService
    {
    [OperationContract]
     string Hello(string message);
    }
  • Anonymous
    July 10, 2006
    The comment has been removed
  • Anonymous
    July 10, 2006
    Allen, thanks for the quick answer. My question was more related to the “game” between service model runtime and BeginXXX/EndXXX methods. First thing, which confuses me is that all examples implement sync pattern only.
    However, by using of service model the default pattern is, as you wrote, async-pattern.

    I use following pattern:

    Create Binding
    CreateHost(A,B,C)
    OpenHost()

    What I would like to have is a recommendation how to deal with channels in service model context?
    How channels should be created and how many of them, should generally depend on the connection state etc?

    I think the fully implemented (with BeginXXX/EndXXX) working FileTransportChannel with service and client example would help.
  • Anonymous
    July 11, 2006
    Most samples only implement the sync case because it is much less code to do so.  Handling async calls makes the sample five times as large and people have a hard time reading through it.  We are trying to expand more of our samples to include async support as a demonstration.  Currently, the best one is the UDP transport in the Windows SDK.  The readme for that sample talks about many of these issues.

    In general, the transport author shouldn't worry too much about when channels are created.  The transport creates a channel when a higher layer (typically the service host) asks for one.  The service host will decide how many channels it should have at once and when to accept a new one.  The transport basically needs to provide two things.  One is to provide a place for deferring pending connection attempts by client.  The other is to accept one of the deferred connections or wait for a new connection to arrive when the higher layer asks for a channel.
  • Anonymous
    October 17, 2006
    I've been providing the contents for this article in bits and pieces but now the whole thing is assembled