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 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:
byte>(buffer, 0, count), bufferManager);
We use a
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 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 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…