Dynamic discoverability using Udp Multicasting

Continuing with my playing with sockets, I've come up with some classes which allow instances of your application to discover eachother at runtime.

The basic idea is that we join a multicast group and listen for incoming discovery requests. When one is received we reply with the details of where to make a more reliable Tcp connection for any actual communication. Starting with the low level socket work we have two classes, UdpTransmitter and UdpMulticastListener. UdpTransmitter handles sending data to the multicast group which is accomplished fairly easily by creating a Udp socket and sending data to a multicast address:

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

socket.SendTo(data, endpoint);

UdpMulticastListener is more interesting and handles listening for Udp datagrams. Here we create a socket and set some options, this will join the multicast group, set the time-to-live or router hops and also disable loopback so we dont get our own transmissions getting sent back to us!

listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

listenerSocket.Bind(new IPEndPoint(IPAddress.Any, port));

listenerSocket.SetSocketOption(SocketOptionLevel.IP,

    SocketOptionName.AddMembership, new MulticastOption(multicastGroup));

listenerSocket.SetSocketOption(SocketOptionLevel.IP,

    SocketOptionName.MulticastTimeToLive, timeToLive);

listenerSocket.SetSocketOption(SocketOptionLevel.IP,

    SocketOptionName.MulticastLoopback, false);

Now we just start listening, remembering to leave the multicast group when we're done:

listenerSocket.SetSocketOption(SocketOptionLevel.IP,

        SocketOptionName.DropMembership, new MulticastOption(multicastGroup));

listenerSocket.Close();

listenerSocket = null;

UdpMulticastListener also also fires a DatagramReceived event when the socket receives data, the EventArgs for this event holds the endpoint the data came from and also a Byte[] containing the actual data.

Wrapping both these classes is one called RendezvousClient which handles both advertising a Tcp endpoint and also sending discovery requests to other instances on the network. Named due to the tendancy for the problem of dynamic discoverability to be refered to as the Rendezvous Problem. RendezvousClient has a very simple interface but there is some magic going on under the covers. There is a single public method SearchForServices, this sends out a discovery request to the multicast group. There are two events, DiscoveryRequest which is raised when RendezvousClient receives a discovery request from the network, there is a handy CancelEventArgs here so we can decide not to reply! There is also the ServicesDiscovered event which is raised when we recieve replies to our own discovery requests. Under the covers, discovery requests are made up of a Byte[] containing a Guid which must be known to both parties, and discovery replies are prefixed with the same Guid, this allows us to filter out any Udp traffic not meant for us. Replies are implemented as the aforementioned Guid and a ServiceEndPoint serialized with the BinaryFormatter, ServiceEndPoint is a little helper class which contains a string for the display name of the service and an IPEndPoint.

One last point, valid multicast addresses are in the range 224.0.0.0-239.255.255.255, however the first 255 are reserved. My home router has a slightly dodgy implementation of IGMP, the routing protocol that makes Udp multicasting possible, and as such only works properly using multicast group 224.0.0.1 which is a reserved address for all hosts in a subnet. Because of this the sample is actually set up to work more like a broadcast however I've also tested on my company network and everything works well in proper multicast mode. 

Anyway, I don't know if anyone managed to follow that whole description, but the code should speak for itself and is all available in the download. I have also included a WPF test project using the RendezvousClient. The well known Guid, multicast group, multicast port and time-to-live are all stored in a config file and can be changed without closing the application. See below for a short demonstration.

(Double click for full screen) 

RendezvousClient.zip (132.72 kb)

kick it on DotNetKicks.com  

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList
December 26, 2007 22:27 by Sean
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Related posts

Comments are closed