Partager via


Creating Channels for Listening

Continuing from yesterday's article about IChannelFactory, today we're looking at the server side of the equation for creating channels.  Before we dive into the code for IChannelListener, let's go over a few of the differences between listeners and factories.

Factories, and the channels that they create, have an immediate mode of interaction.  As long as you're idle, factories and sending channels typically aren't doing any work.  When you push work onto a factory, it then starts processing that work.  In contrast, listeners and receiving channels have a queuing mode of interaction.  Some outside force triggers the creation of messages and connections.  You can't force an event to happen.  Some mechanism, ranging from a simple polling loop to a highly efficient asynchronous callback system, pools events into a queue.  I would not recommend going with the polling approach so don't say later that I didn't warn you about that.  From time-to-time, you need to go and take an event off of that queue.

Beyond the consequence of listeners being more complicated than factories, listeners also have a different concept of addressing.  On the factory, the address of the channel is specified at the time of channel creation.  On the listener, channel creation time is way too late to be specifying where to listen for connections.  Properties like the location are set at listener creation time and propagate to the channels created from that listener.

One last difference is in the relationship between managers and their channels.  If you close a channel listener, all of its listening channels continue to work.  If you close a channel factory, all of its channels get closed as well.  There's some motivation here from the difference in usage patterns between listeners and factories, but this behavior is something that's still in the process of being finalized.

If all this asymmetry feels confusing, take the time to picture how an event producer works in contrast with how an event consumer works.  When you're done picturing, let's dive into the code.

 public interface IChannelListener : IChannelManager, ICommunicationObject, IDisposable
{
   Identity Identity { get; }
   Uri Uri { get; }

   IAsyncResult BeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state);
   bool EndWaitForChannel(IAsyncResult result);
   bool WaitForChannel(TimeSpan timeout);
}

public interface IChannelListener<TChannel> : IChannelListener where TChannel : class, IChannel
{
   TChannel AcceptChannel();
   TChannel AcceptChannel(TimeSpan timeout);
   IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state);
   IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state);
   TChannel EndAcceptChannel(IAsyncResult result);
}

By this point, there should be nothing puzzling in these interfaces, except possibly what this creature called Identity does.  That story is for another day.  As a quick exercise, compare the methods on IChannelListener<IInputChannel> with the methods on IInputChannel.  The channel listener is a meta-input channel that receives input channels in the same way that input channels receive messages.  If conceptual purity was more important than shipping software, we could spend the time making the two classes have exactly the same set of methods.  It doesn't make as much sense to do the same thing with IChannelFactory<IOutputChannel> and IOutputChannel, but if you wanted to stretch things a little, you could.

Next time: What's the Difference Between Close and Abort?

Comments

  • Anonymous
    March 22, 2006
    When we last left the IChannel interface, there was a brief introduction of the concept of a channel...
  • Anonymous
    April 07, 2006
    After seeing the ChannelBase class yesterday for implementing a channel, today's post is about the base...