Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Multicast Message Broker

5.00/5 (3 votes)
4 Aug 2014CPOL4 min read 16.6K   910  
Single class encapsulating UDP IP multicasting functionality and exposing it via observer pattern.

Introduction

In one of my projects I needed to use udp multicast messaging to distribute notifications. I've found numerous articles about multicasting but these were mostly oriented towards an explanation of the technology with just simple code snippets demonstrating basic api calls.

My aim was to build a component which would provide reliable encapsulation of network interactions and that would expose multicast functionality to upper layers through publish/subscribe implementation of the observer pattern.

Requirements for message broker component:

  • distribute string messages to anyone interested (observer pattern) using udp multicasting
  •  single component encapsulating both send & receive logic
  • no machine specific configuration
  • basic error detection
  • thread-safe interaction
  • detection of duplicate messages
  • ability to handle fragmented data
  • ability to recover from data received in unexpected format (e.g. network errors, interrupted transmission, etc.)

Background

IP multicasting is extensive topic which I don't want to cover here as there are already a lot of resources on the web. To mention just a few:

In the next sections I will focus on usage and implementation details of this component.

Using the code

Message broker consists of one class - MulticastMessageBroker, just drop it where you need it.

In the following snippet is demonstrated basic interaction with message broker: 

C#
// create message broker
var multicastBroker = new MulticastMessageBroker(IPAddress.Parse("239.255.255.20"), 6000);

// connect to network
multicastBroker.Connect();

// subscribe to feed
var subscriptionId = Guid.NewGuid();
multicastBroker.Subscribe(subscriptionId,
	(m) =>
	{
		Console.WriteLine("Message received: {0}", m);
	});

// ...

// send notification
multicastBroker.SendMessage(subscriptionId, "hello!");

// ...

// clean-up when no more needed
multicastBroker.ClearAllSubscribers();
multicastBroker.Disconnect();

In the downloads section there is also included a very simple console chat application built on top of message broker. With this you can quickly test multicasting functionality inside your network. The application connects to 239.255.255.20:6000 and allows you to send/receive multicast messages. It should work on the same machine (when you run multiple instances you can send messages between them) as well across multiple machines inside multicast-enabled network.

Implementation

Message broker implementation is based on UdpClient from System.Net.Sockets.

Following message format is used internally:

Guid:myself(sender) + Guid:uniqueMessageId + Guid:subscriptionId + int:messageLenght + byte[2]:CRC16 + string:message

To support detection & recovery from data received in an unexpected format, CRC check of header is included in the message. If this check doesn't pass after message header bytes are read from the incoming data stream, receiving pipeline is reset and broker continues to search for start of the message. CRC algo is not my own implementation, I've included first one that I've found which had required interface.

One of my requirements was that the broker should work straight out of the box on any machine without the need of configuration. Because of this, by default, broker transmits/receives on all network interfaces it finds on the machine. If you want to change this behaviour you can adjust the interface filter in JoinMulticastGroup method. Currently the filter looks like this:

C#
Dns.GetHostAddresses(Dns.GetHostName()).Where(i => i.AddressFamily == AddressFamily.InterNetwork)

There is one constructor:

C#
public MulticastMessageBroker(
	IPAddress multicastGroup,
	int port,
	int multicastTTL = 255,
	int processedMessageHistoryCount = 30,
	int maxSendByteCount = -1)
...

Each message carries unique id. To detect duplicate messages, message broker stores unique id of each received message into the list. If the received message unique id is in this list, the message is discarded. The length of this list can be controlled by processedMessageHistoryCount. By setting value to -1, you can turn off duplicate message detection.

With maxSendByteCount you can specify how many bytes will be sent at once (= one call to UdpClient.Send method). This was used mainly for testing, but there was no reason to remove it, maybe it will be useful in some scenarios.

No time intensive work is done in the constructor. To connect to network and start receiving/sending messages you need to call Connect method. Current connection status is indicated by IsConnected property. Connect / Disconnect methods can be called independently from Subscribe Unsubscribe methods.

To subscribe for specific feed you can use:

public void Subscribe(Guid subscriptionId, Action<string> callback)

When calling this method you specify callback which will be called when a message with given subscription id is received. Each callback is invoked on new thread taken from the thread-pool, so there is no delay in case one of callbacks takes longer time to finish. If you would like to change or adjust this you can do it in FinalizeMessage method.

All of the public methods are thread-safe.

Points of Interest

There is one problem I came across when testing this through wifi connection on the battery powered laptop. I was able to send notifications, but reception seemed to be broken - sometimes I did not get messages sent from another computer in network. After some investigation I've found out that the problem was in the power profile for wifi adapter, after disabling power savings everything worked as expected. 

History

  • Version 1.0 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)