Monthly Archives: June 2005

Channels 110b: IListenerFactory

At the root of a service-side stack lives an IListenerFactory. When you add an Endpoint to a Service, its binding constructs an IListenerFactory stack which is responsible for receiving messages associated with that endpoint.

In our sample code, IListenerFactory-related files include UdpListenerFactory.cs, UdpInputListener.cs, and UdpInputChannel.cs.

UdpListenerFactory 

UdpListenerFactory derives from ListenerFactoryBase. For addressing purposes, we implement SetUri() and SetUniqueUri().

Similar to UdpChannelFactory, UdpListenerFactory overrides OnOpening() and OnClosed() to setup and tear-down resources (IBufferManager, FilterTable, etc) at the appropriate points of the state machine.

In OnOpen() we create and bind a single Udp socket to receive datagrams.

In OnOpened(), we begin receiving data on our Udp socket in an asynchronous loop. As we receive data, we convert the data into Messages using the Message Encoding Framework:

message = UdpConstants.MessageEncoder.ReadMessage(new ArraySegment<byte>(buffer, 0, count), bufferManager);

We use a FilterTable<UdpInputListener> in order to match incoming Messages to Listeners that we’ve created. If we get a match, we dispacth the message to the UdpInputListener, which will enqueue it for a pending receive on its singleton channel (see UdpInputListener and UdpInputChannel sections below for details). If we don’t have any matching listeners for the new message, we write a warning entry to the system Event Log, and drop the message.

UdpInputListener 

UdpInputListener is our implementation of IInputListener. Since the same datagram channel represents messages that come in from any number of sources, we call UdpInputListener a singleton listener. That is, we have at most one active IChannel associated with this listener at a time. We only generate another one if a channel returned from our AcceptChannel() is subsequently disposed.

UdpInputChannel 

UdpInputChannel is our implementation of IInputChannel. It consists of a queue of incoming Messages. UdpListenerFactory adds matching incoming messages to this queue, and these messages are dequeued by IInputChannel.Receive() calls.

You will also notice some complexity in UdpListenerFactory.cs around the async read loop to ensure we (a) always have an outstanding read on the receiving UDP socket and (b) maximize synchronicity while not blocking threads or deadlocking. If you want further details on a particular snippet of code, let me know.

Next up will be tying the IChannelFactory and IListenerFactory to the “ABCs” of ServiceModel by creating a custom Binding Element. I can feel the excitement building…

Channels 110a: IChannelFactory

Sorry for the radio silence, I was on holiday in Alaska. Now I’m back with extra vigor to continue our evolving tutorial. 🙂

NOTE: Source code for all of the files and functionality referenced in the next few posts is available here.

At the root of a custom transport implementation is the creation of your channels. The channel layer uses a factory pattern for constructing channels. We are implementing the Datagram message exchange pattern. So our IChannelFactory (client/proxy) will support creating IOutputChannels and our IListenerFactory (service) will support listening for IInputChannels.

Only the factory classes need to be public, not the IChannel and IListener implementations that they generate. Indigo provides some base class helpers for this process.

  • CommunicationObject is a base class that implements ICommunicationObject and enforces the common state machine.
  • ChannelManagerBase(:CommunicationObject) is a base class that implements IChannelManager and takes care of managing a collection of channels that are created (IChannelFactory) or accepted (IListenerFactory). ChannelManagerBase works in conjunction with ChannelBase, which is a base class that implements IChannel.
  • ChannelFactoryBase(:ChannelManagerBase) is a base class that implements IChannelFactory and consolidates the myriad CreateChannel() overloads into a single OnCreateChannel() abstract method. It also handles the concept of an InnerChannelFactory.
  • ListenerFactoryBase(:ChannelManagerBase) is a base class that implements IListenerFactory. It takes care of managing a collection of listeners that are created, and works in conjunction with ListenerBase, which is a base class that implements IListener. It also handles the concept of an InnerListenerFactory.

In our sample code, the Factory implementations are contained in UdpChannelFactory.cs and UdpListenerFactory.cs. The IChannel implementations are in UdpOutputChannel.cs and UdpInputChannel.cs. The IListener implementation is in UdpInputListener.cs. There are some implementation details of the factories (such as IBufferManager and MessageEncoderFactory) deserving of their own future topics. For the moment I will simply note that these utilities are very helpful in managing serialized message buffers and performing conversions between Message and serialized byte arrays.

UdpChannelFactory 

UdpChannelFactory derives from ChannelFactoryBase. It overrides the following properties and methods to accomplish the bulk of its work:

public override MessageVersion MessageVersion { get; }
For our implementation we hardcode the Message Version to SOAP 1.2+WS-Addressing 1.0.

public override string Scheme { get; }
For Udp we’ve chosen "soap.udp" as our addressing scheme.

public override bool CanCreateChannel<TChannel>()
This is where we declare that we only support IOutputChannel.

protected override TChannel OnCreateChannel<TChannel>(EndpointAddress remoteAddress)
This is where we create our channel implementation (UdpOutputChannel).

We also override OnOpen() and OnClosed() so that we can setup and tear-down our IBufferManager at the appropriate points in the state machine.

UdpOutputChannel 

UdpOutputChannel is our internal implementation of IOutputChannel. In the constructor we validate our arguments and, based on the EndpointAddress passed in, construct a System.Net.EndPoint for our destination .
In our OnOpen() override we create the Socket that we will use for sending data to this EndPoint:

this.socket = new Socket(this.remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

We have 2 places where we need to clean up the socket, as the Channel can close gracefully using Close(), or ungracefully using Abort(). In the Abort() case we want to ensure that our base method gets called, which is why it’s in a finally block:

try
{
this.socket.Close(0);

}
finally
{

base.OnAbort();

}

For Close(), we simply cleanup the socket and call our base class (if this throws, the infrastructure will call Abort() to ensure our channel is cleaned up):

this.socket.Close();
base.OnClose();

We then implement Send() and BeginSend()/EndSend(). This breaks down into two main sections. First we serialize the message into a byte array:

ArraySegment<byte> messageBuffer = EncodeMessage(message);

Then we send the resulting data on the wire:

this.socket.SendTo(messageBuffer.Array, messageBuffer.Offset, messageBuffer.Count, SocketFlags.None, this.remoteEndPoint);

At this point our Send() call has finished its transmission task and we have sent a UDP datagram containing a text encoding serialization of a SOAP message. Next I will cover the IListenerFactory implementation to receive this message.

Off to the Glaciers

Off for vacation for a week with the family. Leaving on an Alaskan cruise out of Vancouver. Will report back with pictures and stories of dog sledding and glacial kayaking! 🙂

Serenade of the Seas route