Author Archives: kenny
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:
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…
Indigo+Avalon Beta 1 pre-release available
You can download the “Beta 1 RC” of Indigo+Avalon here. This release runs on top of Visual Studio 2005 Beta 2 and includes a much more comprehensive SDK than our previous Community Tech Preview. “RC” = “Release Candidate”, and these bits are very close to what will be in the official Beta 1 release. Enjoy!
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 withListenerBase
, 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
; } public override string
Scheme { get
; }"soap.udp"
as our addressing scheme. public override bool
CanCreateChannel<TChannel>()protected override
TChannel OnCreateChannel<TChannel>(EndpointAddress remoteAddress)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:
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:
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! 🙂
Cinco de Mayo de Cinco!
The first thing I’m always asked about my band (me, Aaron, Brian, and Mike) is “what’s your name?” This should be a simple enough question, but we’ve had an ongoing identity crisis, and nothing has stuck to date.
On May 5th we played out at a Cinco de Mayo party under the name “El Driftwood”. It was our first big event with the current lineup (Mike joined in October as our third bass player in two years), and it was a blast. Lauren took a bunch of pictures, much tequila was consumed, and courtesy of some tapers in attendance I’ve posted recordings here.
The latest name under consideration is “Mostly Harmless”, which I find amusing. I’d love to hear your opinions in this area.
Channels 103: Addressing the World
The next step in our custom transport walk-through is to create our channel managers. This includes an IChannelFactory
for our client side channels, and an IChannelListener
for our service-side channels.
Both IChannelFactory and IChannelListener derive from IChannelManager
. IChannelManager is responsible for tracking channels that are created or accepted. IChannelManager also has a property to control the MessageVersion supported for Messages sent or received on its channels. Lastly, it requires you to specify a Scheme to use for message addressing.
Which leads me to a crucial aspect in defining a channel, namely: what is your addressing model?
For layered channels, the addressing model is simply that of the channels they are layered on top of. Such implementations look like:
public override string
Scheme{
get
{ return
InnerListenerFactory.Scheme; }}
public override void
SetUri(Uri uri)
{
return
InnerListenerFactory.SetUri(uri); }
public override void
SetUniqueUri()
{
return
InnerListenerFactory.SetUniqueUri(); }
For transport channels, it’s an area that takes some thought. While HTTP has a pre-defined syntax and semantic, when writing a new transport you need to define both of these. Concretely, this includes a scheme and its associated URI syntax. This work is closely related to how you bind your protocol to SOAP.
The first item to consider is how your client Channels will resolve the URI. For example, HTTP takes the host name and TCP port of the URI (based on the defined URI grammar), uses DNS to resolve the host name into an IP Address, establishes a TCP connection to the resulting (IP Address, port), and sends an HTTP request using the path portion of the URI in our POST request.
The second factor in your URI design is how to match an incoming URI to an IListenerFactory (which will dispatch to a service). IListenerFactory URIs are broken down into 2 parts: a base address and a relative address. The base address is defined by your hosting environment (ServiceHost<T>, IIS vroot, etc). Individual endpoints then define a simple relative address. Note that an endpoint can be configured with a full URI, but this is only necessary in a few corner cases. This model allows for the host to be decoupled from its endpoints, and also allows the transport to optimize the network resources it uses (i.e. we only require at most one per base address, and can share that resource among relative endpoints).
Given these requirements, we define our URI syntax for UDP as:
soap.udp:// host [":" port] path-absolute
where host, port, and path-absolute are defined in the ABNF of RFC 3986.
For example: soap.udp://localhost:7000/service/endpoint
Our UdpOutputChannel will take the hostname (localhost) and convert it to an IP Address:
switch
(remoteAddress.Uri.HostNameType){
case
UriHostNameType.IPv4:case
UriHostNameType.IPv6:
break
; case
UriHostNameType.Basic:
case
UriHostNameType.Dns:
IPHostEntry hostEntry = Dns.GetHostEntry(remoteAddress.Uri.Host);
if (hostEntry.AddressList.Length > 0)
{
remoteIP = hostEntry.AddressList[0];
}
// …
}
break
;}
We will then use the resulting IP Address along with the UDP port from the URI (7000), construct a UDP socket to that remote endpoint, and set:
When our UdpListenerFactory (which is on the receiving end of the UDP socket) deserializes the message, it will compare the incoming message.To against its Uri and only accept the message if the two values match. More advanced implementations could factor out the listening socket into a shared app-domain wide resource that dispatches to the appropriate UdpListenerFactory based on the To of the incoming message.
With our transport addressing model crisply defined, we continue on to actually sending and receiving Messages over UDP!
It was a Darth and Stormy Knight
Star Wars Episode III opened today (actually last night at midnight from what I hear). A bunch of people from work checked it out and enjoyed it.
In preparation for tomorrow’s viewing, Lauren sent me a fun Star Wars refresher guide from Slate. I’ve also received a bunch of other silly links, including a haX0red trailer and a Star Wars photoshop contest. Yet I think I’m most partial to the Darth Vader blog.
Anyways, I’m keeping my expectations in check (Episodes I and II have set a pretty low bar), so it should be an entertaining respite from the very non-Seattle-like thunderstorms we’ve been having.
Saga of The Best Turkey Burger Ever
Last summer on a whim, Lauren and I bought a package of “Southwest Style Turkey Burgers” from Trader Joe’s. We forgot about them and they sat in the freezer for a month or so. Then we decided to bring them to a BBQ at Mike’s house. They were, in a word, awesome. Never again did a package of them last for more than a week or two.
Then on a rainy weekday in February, disaster struck. Walking down the freezer aisle, there were garden burgers, buffalo burgers, gorgonzola stuffed beef burgers, but no southwest turkey burgers. We asked the staff and were told: “We no longer stock that item. It was discontinued due to slow sales.” I was thunderstruck. I considered wearing all black for a week. Went through all seven stages of loss in fairly rapid succession.
Fast forward to yesterday afternoon and a routine trip to TJ’s to fill the empty fridge. I was in a wandering mood and decided to stroll all the aisles (at our local TJ’s this is all of 4 aisles). Just past the frozen Turbot I stopped, agape. Lauren wondered if I lost my mind, and all I could do was point. They had come back from the dead. The Southwest Turkey Burgers had returned. It was a miraculous event. The checkout lady told us that they hadn’t been discontinued, merely “out of stock”. For 3 months? I don’t know if I believe it, but who am I to question. And just to be safe, we purchased 4 boxes on the spot.
And now the story has been told. I encourage all of you who live near a Trader Joe’s to try this very affordable delicacy, and help ensure that the Southwest Turkey Burger stays off the “slow sales” endangered grocery list.
UPDATE (04/12/06): They are gone again. Talked to the staff on Capitol Hill and they had no information to help me. There will be a big hole in our grilling this summer 🙁
UPDATE (11/12/06): They are back! Hopefully this time they are here to stay…
Interlude: Working from home
A co-worker just reminded me that I promised to discuss the effects a home network could have on your Indigo Service, and how that could differ based on the transport you are using.
The first thing to realize about a home network is that your computer is likely behind a NAT. Most DSL or Cable routers will act in this role. The router is assigned an IP Address from your service provider via DHCP, and therefore your router is the only member of your network that is globally addressable. Which means that while you can connect to other machines on the internet, they cannot connect to you.
The practical upshot of this situation is that you cannot use the “dual” standard bindings (a.k.a. WsProfileDualHttpBinding and NetProfileDualTcpBinding) to access services located outside of your home network. Which won’t affect you unless you are using a duplex service contract. I can already hear you saying “but duplex contracts are incredibly useful!” Yes they are. And if you want to use them from behind a NAT you have 2 options:
- Use NetProfileTcpBinding. This will allow you to be the client for duplex communication with another Indigo endpoint.
- Host a router service on the Internet. This is a complicated solution that entails bridging the TCP connection from your home machine to a dual HTTP connection with the service. I call out HTTP specifically, because the only scenario where NetProfileTcpBinding will prove insufficient is if your service is a non-Indigo service and needs to be contacted through HTTP. I will try to comment further on this solution in a future post.
The second thing to note is that your network may not be configured with DNS. Which means that even though machines within your network are all addressable, you may need to identify them by their IP Address. To do this, simply use your machine’s IP Address where you would normally use a hostname (for a Service’s URL or a Proxy’s remote endpoint).
And now back to our (somewhat) regularly scheduled Channels/layering tutorial…