In a recent project I had to have 3 kiosks communicating to each other… enter RTMFP.

I was scratching my head as to how I was going to do this but a visit to Adobe Refresh and watching a demo of AIR applications on desktop and mobile devices by my good ol’ friend Paul Burnett I was beginning to think this may be a solution.

I had a chat to Paul after the show about how he was managing the communication between applications, he was using Cirrus to achieve this. He advised me to look up Tom Krcha as he had helped him out with this.

Tom’s blog is awesome and after reading a heap of it found a way to get peer-to-peer communication without having to use the Cirrus server (basically because I the kiosks were going to be right next to each other – no need to go out to the internet).

This is where I learned about RTMFP (Real Time Media Flow Protocol). After reading up the docs and building a proof-of-concept app a solution was born!

I am using the Robotlegs framework for my jobs these days so I thought I’d share the Service I created and explain how it works.

package com.kafkaris
{
	import org.robotlegs.mvcs.Actor;

	import flash.events.Event;
	import flash.events.NetStatusEvent;
	import flash.net.GroupSpecifier;
	import flash.net.NetConnection;
	import flash.net.NetGroup;

	/**
	 * @author jono kafkaris
	 */
	public class PeerToPeerService extends Actor
	{
		private var _nc:NetConnection;
		private var _group:NetGroup;
		private var _counter:Number = 0;

		public function init(event:Event = null):void
		{
			_nc = new NetConnection();
			_nc.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
			_nc.connect("rtmfp:");
		}

		private function netStatus(event:NetStatusEvent):void
		{
			switch(event.info.code)
			{
				case "NetConnection.Connect.Success":
					setupGroup();
					break;
				case "NetGroup.Posting.Notify":
					receiveMessage(event.info.message);
					break;
				case "NetStream.Pause.Notify":
					break;
			}
		}

		private function setupGroup():void
		{
			var groupspec:GroupSpecifier = new GroupSpecifier("kafkaris/group1");
			groupspec.postingEnabled = true;
			groupspec.ipMulticastMemberUpdatesEnabled = true;
			groupspec.addIPMulticastAddress("225.225.0.1:30303");

			_group = new NetGroup(_nc, groupspec.groupspecWithAuthorizations());
			_group.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
		}
		
		// send message to others with this function
		public function sendMessage(txt:String):void
		{
			var message:Object = new Object();
			message.text = txt;
			message.sender = _group.convertPeerIDToGroupAddress(_nc.nearID);
			message.id = _counter;

			_group.post(message);
			_counter++;
		}
		
		// recieved messages from others handles here
		public function receiveMessage(message:Object):void
		{
			// Now do something with the message - like dispatch an event.
			trace(message.text);
		}
	}
}

Now once you have injected the Service in your Context file:

injector.mapSingleton(PeerToPeerService);

And then Injected it into another class:

[Inject]
public var peerToPeerService:PeerToPeerService;

and call it’s init() function to set it up (I do this in my startup command)

peerToPeerService.init();

you are ready to receive messages and all you have to do to send out a message to other applications that are set up the same way, is to:

peerToPeerService.sendMessage("some string others will hear!");

Then you can handle the received message however you like in the receiveMessage function.

A couple of “gotchas” that stumped me for a bit were:
– The application that sends out a message won’t hear that message.
– Each message has to be unique (hence the counter in the above code – could use a date stamp also perhaps).

There is a little latency I found, roughly half a second but that was fine for me for a solution that did not require an additional app running as a server for the communication.

So once again thanks to Adobe Refresh, Paul and Tom for their knowledge. If you have any questions let me know!

JK